# HG changeset patch
# User Julien Cristau
# Date 1389370340 -3600
# Node ID e83cbc116352cf4b24e59ef1986041ccb647bcba
# Parent 96dba2efd16df59c30fa6b6d15b758d3f89fcfe7# Parent 2790fb8f7e037457f5b689f543fba6c6fa33cdf0
merge stable
diff -r 2790fb8f7e03 -r e83cbc116352 README
--- a/README Fri Jun 21 14:22:39 2013 +0200
+++ b/README Fri Jan 10 17:12:20 2014 +0100
@@ -34,4 +34,4 @@
Look in the doc/ subdirectory or read http://docs.cubicweb.org/
-
+It includes the Entypo pictograms by Daniel Bruce — www.entypo.com
diff -r 2790fb8f7e03 -r e83cbc116352 __init__.py
--- a/__init__.py Fri Jun 21 14:22:39 2013 +0200
+++ b/__init__.py Fri Jan 10 17:12:20 2014 +0100
@@ -22,6 +22,8 @@
# ignore the pygments UserWarnings
import warnings
+import cPickle
+import zlib
warnings.filterwarnings('ignore', category=UserWarning,
message='.*was already imported',
module='.*pygments')
@@ -120,6 +122,26 @@
binary.seek(0)
return binary
+ def __eq__(self, other):
+ if not isinstance(other, Binary):
+ return False
+ return self.getvalue(), other.getvalue()
+
+
+ # Binary helpers to store/fetch python objects
+
+ @classmethod
+ def zpickle(cls, obj):
+ """ return a Binary containing a gzipped pickle of obj """
+ retval = cls()
+ retval.write(zlib.compress(cPickle.dumps(obj, protocol=2)))
+ return retval
+
+ def unzpickle(self):
+ """ decompress and loads the stream before returning it """
+ return cPickle.loads(zlib.decompress(self.getvalue()))
+
+
def str_or_binary(value):
if isinstance(value, Binary):
return value
@@ -127,7 +149,6 @@
BASE_CONVERTERS['Password'] = str_or_binary
-
# use this dictionary to rename entity types while keeping bw compat
ETYPE_NAME_MAP = {}
diff -r 2790fb8f7e03 -r e83cbc116352 __pkginfo__.py
--- a/__pkginfo__.py Fri Jun 21 14:22:39 2013 +0200
+++ b/__pkginfo__.py Fri Jan 10 17:12:20 2014 +0100
@@ -40,10 +40,10 @@
]
__depends__ = {
- 'logilab-common': '>= 0.59.0',
+ 'logilab-common': '>= 0.60.0',
'logilab-mtconverter': '>= 0.8.0',
'rql': '>= 0.31.2',
- 'yams': '>= 0.37.0',
+ 'yams': '>= 0.39.0',
#gettext # for xgettext, msgcat, etc...
# web dependancies
'simplejson': '>= 2.0.9',
@@ -51,7 +51,7 @@
'Twisted': '',
# XXX graphviz
# server dependencies
- 'logilab-database': '>= 1.10',
+ 'logilab-database': '>= 1.11',
'passlib': '',
}
diff -r 2790fb8f7e03 -r e83cbc116352 _exceptions.py
--- a/_exceptions.py Fri Jun 21 14:22:39 2013 +0200
+++ b/_exceptions.py Fri Jan 10 17:12:20 2014 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -19,6 +19,10 @@
__docformat__ = "restructuredtext en"
+from warnings import warn
+
+from logilab.common.decorators import cachedproperty
+
from yams import ValidationError as ValidationError
# abstract exceptions #########################################################
@@ -81,6 +85,26 @@
class UniqueTogetherError(RepositoryError):
"""raised when a unique_together constraint caused an IntegrityError"""
+ def __init__(self, session, **kwargs):
+ self.session = session
+ assert 'rtypes' in kwargs or 'cstrname' in kwargs
+ self.kwargs = kwargs
+
+ @cachedproperty
+ def rtypes(self):
+ if 'rtypes' in self.kwargs:
+ return self.kwargs['rtypes']
+ cstrname = unicode(self.kwargs['cstrname'])
+ cstr = self.session.find('CWUniqueTogetherConstraint', name=cstrname).one()
+ return sorted(rtype.name for rtype in cstr.relations)
+
+ @cachedproperty
+ def args(self):
+ warn('[3.18] UniqueTogetherError.args is deprecated, just use '
+ 'the .rtypes accessor.',
+ DeprecationWarning)
+ # the first argument, etype, is never used and was never garanteed anyway
+ return None, self.rtypes
# security exceptions #########################################################
@@ -134,6 +158,15 @@
a non final entity
"""
+class MultipleResultsError(CubicWebRuntimeError):
+ """raised when ResultSet.one() is called on a resultset with multiple rows
+ of multiple columns.
+ """
+
+class NoResultError(CubicWebRuntimeError):
+ """raised when no result is found but at least one is expected.
+ """
+
class UndoTransactionException(QueryError):
"""Raised when undoing a transaction could not be performed completely.
diff -r 2790fb8f7e03 -r e83cbc116352 cubicweb.spec
--- a/cubicweb.spec Fri Jun 21 14:22:39 2013 +0200
+++ b/cubicweb.spec Fri Jan 10 17:12:20 2014 +0100
@@ -20,11 +20,11 @@
BuildArch: noarch
Requires: %{python}
-Requires: %{python}-logilab-common >= 0.59.0
+Requires: %{python}-logilab-common >= 0.60.0
Requires: %{python}-logilab-mtconverter >= 0.8.0
Requires: %{python}-rql >= 0.31.2
-Requires: %{python}-yams >= 0.37.0
-Requires: %{python}-logilab-database >= 1.10.0
+Requires: %{python}-yams >= 0.39.0
+Requires: %{python}-logilab-database >= 1.11.0
Requires: %{python}-passlib
Requires: %{python}-lxml
Requires: %{python}-twisted-web
diff -r 2790fb8f7e03 -r e83cbc116352 cwconfig.py
--- a/cwconfig.py Fri Jun 21 14:22:39 2013 +0200
+++ b/cwconfig.py Fri Jan 10 17:12:20 2014 +0100
@@ -53,8 +53,7 @@
If you are not administrator of you machine or if you need to play with some
specific version of |cubicweb| you can use `virtualenv`_ a tool to create
-isolated Python environments. Since version 3.9 |cubicweb| is **`virtualenv`
-friendly** and won't write any file outside the virtualenv directory.
+isolated Python environments.
- instances are stored in :file:`/etc/cubicweb.d`
- temporary files (such as pid file) in :file:`/var/run/cubicweb`
@@ -206,7 +205,7 @@
"""return a list of installed configurations in a directory
according to \*-ctl files
"""
- return [name for name in ('repository', 'twisted', 'all-in-one')
+ return [name for name in ('repository', 'all-in-one')
if exists(join(directory, '%s.conf' % name))]
def guess_configuration(directory):
@@ -328,7 +327,7 @@
# the format below can be useful to debug multi thread issues:
# log_format = '%(asctime)s - [%(threadName)s] (%(name)s) %(levelname)s: %(message)s'
# nor remove appobjects based on unused interface [???]
- cleanup_interface_sobjects = True
+ cleanup_unused_appobjects = True
if (CWDEV and _forced_mode != 'system'):
mode = 'user'
@@ -499,21 +498,11 @@
try:
gendeps = getattr(pkginfo, key.replace('_cubes', ''))
except AttributeError:
- # bw compat
- if hasattr(pkginfo, oldkey):
- warn('[3.8] cube %s: %s is deprecated, use %s dict'
- % (cube, oldkey, key), DeprecationWarning)
- deps = getattr(pkginfo, oldkey)
- else:
- deps = {}
+ deps = {}
else:
deps = dict( (x[len('cubicweb-'):], v)
for x, v in gendeps.iteritems()
if x.startswith('cubicweb-'))
- if not isinstance(deps, dict):
- deps = dict((key, None) for key in deps)
- warn('[3.8] cube %s should define %s as a dict' % (cube, key),
- DeprecationWarning)
for depcube in deps:
try:
newname = CW_MIGRATION_MAP[depcube]
@@ -940,10 +929,9 @@
' "cubicweb-ctl list")' % appid)
return home
- MODES = ('common', 'repository', 'Any', 'web')
+ MODES = ('common', 'repository', 'Any')
MCOMPAT = {'all-in-one': MODES,
- 'repository': ('common', 'repository', 'Any'),
- 'twisted' : ('common', 'web'),}
+ 'repository': ('common', 'repository', 'Any')}
@classmethod
def accept_mode(cls, mode):
#assert mode in cls.MODES, mode
@@ -1189,11 +1177,13 @@
sourcedirs.append(self.i18n_lib_dir())
return i18n.compile_i18n_catalogs(sourcedirs, i18ndir, langs)
- def sendmails(self, msgs):
+ def sendmails(self, msgs, fromaddr=None):
"""msgs: list of 2-uple (message object, recipients). Return False
if connection to the smtp server failed, else True.
"""
server, port = self['smtp-host'], self['smtp-port']
+ if fromaddr is None:
+ fromaddr = '%s <%s>' % (self['sender-name'], self['sender-addr'])
SMTP_LOCK.acquire()
try:
try:
@@ -1202,10 +1192,9 @@
self.exception("can't connect to smtp server %s:%s (%s)",
server, port, ex)
return False
- heloaddr = '%s <%s>' % (self['sender-name'], self['sender-addr'])
for msg, recipients in msgs:
try:
- smtp.sendmail(heloaddr, recipients, msg.as_string())
+ smtp.sendmail(fromaddr, recipients, msg.as_string())
except Exception as ex:
self.exception("error sending mail to %s (%s)",
recipients, ex)
diff -r 2790fb8f7e03 -r e83cbc116352 cwctl.py
--- a/cwctl.py Fri Jun 21 14:22:39 2013 +0200
+++ b/cwctl.py Fri Jan 10 17:12:20 2014 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -48,13 +48,9 @@
from cubicweb.toolsutils import Command, rm, create_dir, underline_title
from cubicweb.__pkginfo__ import version
-if support_args(CommandLine, 'check_duplicated_command'):
- # don't check duplicated commands, it occurs when reloading site_cubicweb
- CWCTL = CommandLine('cubicweb-ctl', 'The CubicWeb swiss-knife.',
- version=version, check_duplicated_command=False)
-else:
- CWCTL = CommandLine('cubicweb-ctl', 'The CubicWeb swiss-knife.',
- version=version)
+# don't check duplicated commands, it occurs when reloading site_cubicweb
+CWCTL = CommandLine('cubicweb-ctl', 'The CubicWeb swiss-knife.',
+ version=version, check_duplicated_command=False)
def wait_process_end(pid, maxtry=10, waittime=1):
"""wait for a process to actually die"""
@@ -266,12 +262,7 @@
if tinfo:
descr = getattr(tinfo, 'description', '')
if not descr:
- descr = getattr(tinfo, 'short_desc', '')
- if descr:
- warn('[3.8] short_desc is deprecated, update %s'
- ' pkginfo' % cube, DeprecationWarning)
- else:
- descr = tinfo.__doc__
+ descr = tinfo.__doc__
if descr:
print ' '+ ' \n'.join(descr.splitlines())
modes = detect_available_modes(cwcfg.cube_dir(cube))
@@ -357,7 +348,7 @@
}),
('config',
{'short': 'c', 'type' : 'choice', 'metavar': '',
- 'choices': ('all-in-one', 'repository', 'twisted'),
+ 'choices': ('all-in-one', 'repository'),
'default': 'all-in-one',
'help': 'installation type, telling which part of an instance '
'should be installed. You can list available configurations using the'
@@ -761,6 +752,7 @@
mih = config.migration_handler()
repo = mih.repo_connect()
vcconf = repo.get_versions()
+ helper = self.config_helper(config, required=False)
if self.config.force_cube_version:
for cube, version in self.config.force_cube_version.iteritems():
vcconf[cube] = Version(version)
@@ -799,6 +791,8 @@
if not self.i18nupgrade(config):
return
print
+ if helper:
+ helper.postupgrade(repo)
print '-> instance migrated.'
if instance_running and not (CWDEV or self.config.nostartstop):
# restart instance through fork to get a proper environment, avoid
@@ -1039,9 +1033,56 @@
raise ConfigurationError('unknown configuration key "%s" for mode %s' % (key, appcfg.name))
appcfg.save()
+
+# WSGI #########
+
+def wsgichoices():
+ try:
+ from werkzeug import serving
+ except ImportError:
+ return ('stdlib',)
+ return ('stdlib', 'werkzeug')
+
+class WSGIDebugStartHandler(InstanceCommand):
+ """Start an interactive wsgi server """
+ name = 'wsgi'
+ actionverb = 'started'
+ arguments = ''
+ options = (
+ ('method',
+ {'short': 'm',
+ 'type': 'choice',
+ 'metavar': '',
+ 'default': 'stdlib',
+ 'choices': wsgichoices(),
+ 'help': 'wsgi utility/method'}),
+ ('loglevel',
+ {'short': 'l',
+ 'type' : 'choice',
+ 'metavar': '',
+ 'default': 'debug',
+ 'choices': ('debug', 'info', 'warning', 'error'),
+ 'help': 'debug if -D is set, error otherwise',
+ }),
+ )
+
+ def wsgi_instance(self, appid):
+ config = cwcfg.config_for(appid, debugmode=1)
+ init_cmdline_log_threshold(config, self['loglevel'])
+ assert config.name == 'all-in-one'
+ meth = self['method']
+ if meth == 'stdlib':
+ from cubicweb.wsgi import server
+ else:
+ from cubicweb.wsgi import wz as server
+ return server.run(config)
+
+
+
for cmdcls in (ListCommand,
CreateInstanceCommand, DeleteInstanceCommand,
StartInstanceCommand, StopInstanceCommand, RestartInstanceCommand,
+ WSGIDebugStartHandler,
ReloadConfigurationCommand, StatusCommand,
UpgradeInstanceCommand,
ListVersionsInstanceCommand,
@@ -1052,6 +1093,8 @@
):
CWCTL.register(cmdcls)
+
+
def run(args):
"""command line tool"""
import os
diff -r 2790fb8f7e03 -r e83cbc116352 cwvreg.py
--- a/cwvreg.py Fri Jun 21 14:22:39 2013 +0200
+++ b/cwvreg.py Fri Jan 10 17:12:20 2014 +0100
@@ -211,8 +211,7 @@
from cubicweb import (CW_SOFTWARE_ROOT, ETYPE_NAME_MAP, CW_EVENT_MANAGER,
onevent, Binary, UnknownProperty, UnknownEid)
-from cubicweb.predicates import (implements, appobject_selectable,
- _reset_is_instance_cache)
+from cubicweb.predicates import appobject_selectable, _reset_is_instance_cache
@onevent('before-registry-reload')
@@ -230,15 +229,6 @@
sys.modules.pop('cubicweb.web.uicfg', None)
sys.modules.pop('cubicweb.web.uihelper', None)
-def use_interfaces(obj):
- """return interfaces required by the given object by searching for
- `implements` predicate
- """
- impl = obj.__select__.search_selector(implements)
- if impl:
- return sorted(impl.expected_ifaces)
- return ()
-
def require_appobject(obj):
"""return appobjects required by the given object by searching for
`appobject_selectable` predicate
@@ -568,7 +558,6 @@
def reset(self):
CW_EVENT_MANAGER.emit('before-registry-reset', self)
super(CWRegistryStore, self).reset()
- self._needs_iface = {}
self._needs_appobject = {}
# two special registries, propertydefs which care all the property
# definitions, and propertyvals which contains values for those
@@ -626,6 +615,15 @@
self.register_objects(path)
CW_EVENT_MANAGER.emit('after-registry-reload')
+ def load_file(self, filepath, modname):
+ # override to allow some instrumentation (eg localperms)
+ modpath = modname.split('.')
+ try:
+ self.currently_loading_cube = modpath[modpath.index('cubes') + 1]
+ except ValueError:
+ self.currently_loading_cube = 'cubicweb'
+ return super(CWRegistryStore, self).load_file(filepath, modname)
+
def _set_schema(self, schema):
"""set instance'schema"""
self.schema = schema
@@ -641,20 +639,6 @@
for obj in objects:
obj.schema = schema
- @deprecated('[3.9] use .register instead')
- def register_if_interface_found(self, obj, ifaces, **kwargs):
- """register `obj` but remove it if no entity class implements one of
- the given `ifaces` interfaces at the end of the registration process.
-
- Extra keyword arguments are given to the
- :meth:`~cubicweb.cwvreg.CWRegistryStore.register` function.
- """
- self.register(obj, **kwargs)
- if not isinstance(ifaces, (tuple, list)):
- self._needs_iface[obj] = (ifaces,)
- else:
- self._needs_iface[obj] = ifaces
-
def register(self, obj, *args, **kwargs):
"""register `obj` application object into `registryname` or
`obj.__registry__` if not specified, with identifier `oid` or
@@ -665,15 +649,6 @@
"""
obj = related_appobject(obj)
super(CWRegistryStore, self).register(obj, *args, **kwargs)
- # XXX bw compat
- ifaces = use_interfaces(obj)
- if ifaces:
- if not obj.__name__.endswith('Adapter') and \
- any(iface for iface in ifaces if not isinstance(iface, basestring)):
- warn('[3.9] %s: interfaces in implements selector are '
- 'deprecated in favor of adapters / adaptable '
- 'selector' % obj.__name__, DeprecationWarning)
- self._needs_iface[obj] = ifaces
depends_on = require_appobject(obj)
if depends_on is not None:
self._needs_appobject[obj] = depends_on
@@ -687,41 +662,14 @@
def initialization_completed(self):
"""cw specific code once vreg initialization is completed:
- * remove objects requiring a missing interface, unless
- config.cleanup_interface_sobjects is false
+ * remove objects requiring a missing appobject, unless
+ config.cleanup_unused_appobjects is false
* init rtags
"""
# we may want to keep interface dependent objects (e.g.for i18n
# catalog generation)
- if self.config.cleanup_interface_sobjects:
- # XXX deprecated with cw 3.9: remove appobjects that don't support
- # any available interface
- implemented_interfaces = set()
- if 'Any' in self.get('etypes', ()):
- for etype in self.schema.entities():
- if etype.final:
- continue
- cls = self['etypes'].etype_class(etype)
- if cls.__implements__:
- warn('[3.9] %s: using __implements__/interfaces are '
- 'deprecated in favor of adapters' % cls.__name__,
- DeprecationWarning)
- for iface in cls.__implements__:
- implemented_interfaces.update(iface.__mro__)
- implemented_interfaces.update(cls.__mro__)
- for obj, ifaces in self._needs_iface.items():
- ifaces = frozenset(isinstance(iface, basestring)
- and iface in self.schema
- and self['etypes'].etype_class(iface)
- or iface
- for iface in ifaces)
- if not ('Any' in ifaces or ifaces & implemented_interfaces):
- reg = self[obj_registries(obj)[0]]
- self.debug('unregister %s (no implemented '
- 'interface among %s)', reg.objid(obj), ifaces)
- self.unregister(obj)
- # since 3.9: remove appobjects which depending on other, unexistant
- # appobjects
+ if self.config.cleanup_unused_appobjects:
+ # remove appobjects which depend on other, unexistant appobjects
for obj, (regname, regids) in self._needs_appobject.items():
try:
registry = self[regname]
@@ -740,8 +688,8 @@
if 'uicfg' in self: # 'uicfg' is not loaded in a pure repository mode
for rtags in self['uicfg'].itervalues():
for rtag in rtags:
- # don't check rtags if we don't want to cleanup_interface_sobjects
- rtag.init(self.schema, check=self.config.cleanup_interface_sobjects)
+ # don't check rtags if we don't want to cleanup_unused_appobjects
+ rtag.init(self.schema, check=self.config.cleanup_unused_appobjects)
# rql parsing utilities ####################################################
diff -r 2790fb8f7e03 -r e83cbc116352 dataimport.py
--- a/dataimport.py Fri Jun 21 14:22:39 2013 +0200
+++ b/dataimport.py Fri Jan 10 17:12:20 2014 +0100
@@ -802,6 +802,9 @@
assert not rtype.startswith('reverse_')
self.add_relation(self.session, eid_from, rtype, eid_to,
self.rschema(rtype).inlined)
+ if self.rschema[rtype].symmetric:
+ self.add_relation(self.session, eid_to, rtype, eid_from,
+ self.rschema(rtype).inlined)
self._nb_inserted_relations += 1
@property
@@ -928,6 +931,9 @@
# XXX Could subjtype be inferred ?
self.source.add_relation(self.session, subj_eid, rtype, obj_eid,
self.rschema(rtype).inlined, **kwargs)
+ if self.rschema[rtype].symmetric:
+ self.source.add_relation(self.session, obj_eid, rtype, subj_eid,
+ self.rschema(rtype).inlined, **kwargs)
def drop_indexes(self, etype):
"""Drop indexes for a given entity type"""
diff -r 2790fb8f7e03 -r e83cbc116352 dbapi.py
--- a/dbapi.py Fri Jun 21 14:22:39 2013 +0200
+++ b/dbapi.py Fri Jan 10 17:12:20 2014 +0100
@@ -422,25 +422,6 @@
req.set_session(self.session, user)
return req
- @deprecated('[3.8] use direct access to req.session.data dictionary')
- def session_data(self):
- """return a dictionary containing session data"""
- return self.session.data
-
- @deprecated('[3.8] use direct access to req.session.data dictionary')
- def get_session_data(self, key, default=None, pop=False):
- if pop:
- return self.session.data.pop(key, default)
- return self.session.data.get(key, default)
-
- @deprecated('[3.8] use direct access to req.session.data dictionary')
- def set_session_data(self, key, value):
- self.session.data[key] = value
-
- @deprecated('[3.8] use direct access to req.session.data dictionary')
- def del_session_data(self, key):
- self.session.data.pop(key, None)
-
# 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
@@ -486,7 +467,7 @@
def _txid(self):
return self.connection._txid(self)
- def execute(self, rql, args=None, eid_key=None, build_descr=True):
+ def execute(self, rql, args=None, build_descr=True):
"""execute a rql query, return resulting rows and their description in
a :class:`~cubicweb.rset.ResultSet` object
@@ -517,10 +498,6 @@
execute('Any X WHERE X eid %(x)s', {'x': 123})
"""
- if eid_key is not None:
- warn('[3.8] eid_key is deprecated, you can safely remove this argument',
- DeprecationWarning, stacklevel=2)
- # XXX use named argument for build_descr in case repo is < 3.8
rset = self._repo.execute(self._sessid, rql, args,
build_descr=build_descr, **self._txid())
rset.req = self.req
diff -r 2790fb8f7e03 -r e83cbc116352 debian/control
--- a/debian/control Fri Jun 21 14:22:39 2013 +0200
+++ b/debian/control Fri Jan 10 17:12:20 2014 +0100
@@ -15,7 +15,7 @@
python-unittest2 | python (>= 2.7),
python-logilab-mtconverter,
python-rql,
- python-yams (>= 0.37),
+ python-yams (>= 0.39),
python-lxml,
Standards-Version: 3.9.1
Homepage: http://www.cubicweb.org
@@ -51,7 +51,7 @@
${python:Depends},
cubicweb-common (= ${source:Version}),
cubicweb-ctl (= ${source:Version}),
- python-logilab-database (>= 1.10.0),
+ python-logilab-database (>= 1.11.0),
cubicweb-postgresql-support
| cubicweb-mysql-support
| python-pysqlite2,
@@ -131,6 +131,8 @@
python-fyzz,
python-imaging,
python-rdflib
+Breaks:
+ cubicweb-inlinedit (<< 1.1.1),
Description: web interface library for the CubicWeb framework
CubicWeb is a semantic web application framework.
.
@@ -149,8 +151,8 @@
graphviz,
gettext,
python-logilab-mtconverter (>= 0.8.0),
- python-logilab-common (>= 0.59.0),
- python-yams (>= 0.37.0),
+ python-logilab-common (>= 0.60.0),
+ python-yams (>= 0.39.0),
python-rql (>= 0.31.2),
python-lxml
Recommends:
@@ -158,6 +160,10 @@
python-crypto
Conflicts: cubicweb-core
Replaces: cubicweb-core
+Breaks:
+ cubicweb-comment (<< 1.9.1),
+ cubicweb-person (<< 1.8.0),
+ cubicweb-geocoding (<< 0.2.0),
Description: common library for the CubicWeb framework
CubicWeb is a semantic web application framework.
.
diff -r 2790fb8f7e03 -r e83cbc116352 debian/copyright
--- a/debian/copyright Fri Jun 21 14:22:39 2013 +0200
+++ b/debian/copyright Fri Jan 10 17:12:20 2014 +0100
@@ -8,7 +8,7 @@
Copyright:
- Copyright (c) 2003-2011 LOGILAB S.A. (Paris, FRANCE).
+ Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
http://www.logilab.fr/ -- mailto:contact@logilab.fr
License:
@@ -29,3 +29,18 @@
On Debian systems, the complete text of the GNU Lesser General Public License
may be found in '/usr/share/common-licenses/LGPL-2.1'.
+
+Entypo pictograms:
+
+Author:
+
+ Daniel Bruce (www.entypo.com)
+
+Licence:
+
+ Entypo pictograms are licensed under CC BY-SA 3.0 and the font
+ under SIL Open Font License.
+
+ The rights to each pictogram in the social extension are either
+ trademarked or copyrighted by the respective company.
+
diff -r 2790fb8f7e03 -r e83cbc116352 devtools/__init__.py
--- a/devtools/__init__.py Fri Jun 21 14:22:39 2013 +0200
+++ b/devtools/__init__.py Fri Jan 10 17:12:20 2014 +0100
@@ -38,7 +38,7 @@
from cubicweb import ExecutionError, BadConnectionId
from cubicweb import schema, cwconfig
from cubicweb.server.serverconfig import ServerConfiguration
-from cubicweb.etwist.twconfig import TwistedConfiguration
+from cubicweb.etwist.twconfig import WebConfigurationBase
cwconfig.CubicWebConfiguration.cls_adjust_sys_path()
@@ -214,12 +214,12 @@
return BASE_URL
-class BaseApptestConfiguration(TestServerConfiguration, TwistedConfiguration):
+class BaseApptestConfiguration(TestServerConfiguration, WebConfigurationBase):
name = 'all-in-one' # so it search for all-in-one.conf, not repository.conf
options = cwconfig.merge_options(TestServerConfiguration.options
- + TwistedConfiguration.options)
- cubicweb_appobject_path = TestServerConfiguration.cubicweb_appobject_path | TwistedConfiguration.cubicweb_appobject_path
- cube_appobject_path = TestServerConfiguration.cube_appobject_path | TwistedConfiguration.cube_appobject_path
+ + WebConfigurationBase.options)
+ cubicweb_appobject_path = TestServerConfiguration.cubicweb_appobject_path | WebConfigurationBase.cubicweb_appobject_path
+ cube_appobject_path = TestServerConfiguration.cube_appobject_path | WebConfigurationBase.cube_appobject_path
def available_languages(self, *args):
return self.cw_languages()
diff -r 2790fb8f7e03 -r e83cbc116352 devtools/devctl.py
--- a/devtools/devctl.py Fri Jun 21 14:22:39 2013 +0200
+++ b/devtools/devctl.py Fri Jan 10 17:12:20 2014 +0100
@@ -46,7 +46,7 @@
a cube or for cubicweb (without a home)
"""
creating = True
- cleanup_interface_sobjects = False
+ cleanup_unused_appobjects = False
cubicweb_appobject_path = (ServerConfiguration.cubicweb_appobject_path
| WebConfiguration.cubicweb_appobject_path)
diff -r 2790fb8f7e03 -r e83cbc116352 devtools/instrument.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/devtools/instrument.py Fri Jan 10 17:12:20 2014 +0100
@@ -0,0 +1,224 @@
+# copyright 2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr -- mailto:contact@logilab.fr
+#
+# This program 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.
+#
+# This program 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 this program. If not, see .
+"""Instrumentation utilities"""
+
+import os
+
+try:
+ import pygraphviz
+except ImportError:
+ pygraphviz = None
+
+from cubicweb.cwvreg import CWRegistryStore
+from cubicweb.devtools.devctl import DevConfiguration
+
+
+ALL_COLORS = [
+ "00FF00", "0000FF", "FFFF00", "FF00FF", "00FFFF", "000000",
+ "800000", "008000", "000080", "808000", "800080", "008080", "808080",
+ "C00000", "00C000", "0000C0", "C0C000", "C000C0", "00C0C0", "C0C0C0",
+ "400000", "004000", "000040", "404000", "400040", "004040", "404040",
+ "200000", "002000", "000020", "202000", "200020", "002020", "202020",
+ "600000", "006000", "000060", "606000", "600060", "006060", "606060",
+ "A00000", "00A000", "0000A0", "A0A000", "A000A0", "00A0A0", "A0A0A0",
+ "E00000", "00E000", "0000E0", "E0E000", "E000E0", "00E0E0", "E0E0E0",
+ ]
+_COLORS = {}
+def get_color(key):
+ try:
+ return _COLORS[key]
+ except KeyError:
+ _COLORS[key] = '#'+ALL_COLORS[len(_COLORS) % len(ALL_COLORS)]
+ return _COLORS[key]
+
+def warn(msg, *args):
+ print 'WARNING: %s' % (msg % args)
+
+def info(msg):
+ print 'INFO: ' + msg
+
+
+class PropagationAnalyzer(object):
+ """Abstract propagation analyzer, providing utility function to extract
+ entities involved in propagation from a schema, as well as propagation
+ rules from hooks (provided they use intrumentalized sets, see
+ :class:`CubeTracerSet`).
+
+ Concrete classes should at least define `prop_rel` class attribute and
+ implements the `is_root` method.
+
+ See `localperms` or `nosylist` cubes for example usage (`ccplugin` module).
+ """
+ prop_rel = None # name of the propagation relation
+
+ def init(self, cube):
+ """Initialize analyze for the given cube, returning the (already loaded)
+ vregistry and a set of entities which we're interested in.
+ """
+ config = DevConfiguration(cube)
+ schema = config.load_schema()
+ vreg = CWRegistryStore(config)
+ vreg.set_schema(schema) # set_schema triggers objects registrations
+ eschemas = set(eschema for eschema in schema.entities()
+ if self.should_include(eschema))
+ return vreg, eschemas
+
+ def is_root(self, eschema):
+ """Return `True` if given entity schema is a root of the graph"""
+ raise NotImplementedError()
+
+ def should_include(self, eschema):
+ """Return `True` if given entity schema should be included by the graph.
+ """
+
+ if self.prop_rel in eschema.subjrels or self.is_root(eschema):
+ return True
+ return False
+
+ def prop_edges(self, s_rels, o_rels, eschemas):
+ """Return a set of edges where propagation has been detected.
+
+ Each edge is defined by a 4-uple (from node, to node, rtype, package)
+ where `rtype` is the relation type bringing from to and `package` is the cube adding the rule to the propagation
+ control set (see see :class:`CubeTracerSet`).
+ """
+ schema = iter(eschemas).next().schema
+ prop_edges = set()
+ for rtype in s_rels:
+ found = False
+ for subj, obj in schema.rschema(rtype).rdefs:
+ if subj in eschemas and obj in eschemas:
+ found = True
+ prop_edges.add( (subj, obj, rtype, s_rels.value_cube[rtype]) )
+ if not found:
+ warn('no rdef match for %s', rtype)
+ for rtype in o_rels:
+ found = False
+ for subj, obj in schema.rschema(rtype).rdefs:
+ if subj in eschemas and obj in eschemas:
+ found = True
+ prop_edges.add( (obj, subj, rtype, o_rels.value_cube[rtype]) )
+ if not found:
+ warn('no rdef match for %s', rtype)
+ return prop_edges
+
+ def detect_problems(self, eschemas, edges):
+ """Given the set of analyzed entity schemas and edges between them,
+ return a set of entity schemas where a problem has been detected.
+ """
+ problematic = set()
+ for eschema in eschemas:
+ if self.has_problem(eschema, edges):
+ problematic.add(eschema)
+ not_problematic = set(eschemas).difference(problematic)
+ if not_problematic:
+ info('nothing problematic in: %s' %
+ ', '.join(e.type for e in not_problematic))
+ return problematic
+
+ def has_problem(self, eschema, edges):
+ """Return `True` if the given schema is considered problematic,
+ considering base propagation rules.
+ """
+ root = self.is_root(eschema)
+ has_prop_rel = self.prop_rel in eschema.subjrels
+ # root but no propagation relation
+ if root and not has_prop_rel:
+ warn('%s is root but miss %s', eschema, self.prop_rel)
+ return True
+ # propagated but without propagation relation / not propagated but
+ # with propagation relation
+ if not has_prop_rel and \
+ any(edge for edge in edges if edge[1] == eschema):
+ warn("%s miss %s but is reached by propagation",
+ eschema, self.prop_rel)
+ return True
+ elif has_prop_rel and not root:
+ rdef = eschema.rdef(self.prop_rel, takefirst=True)
+ edges = [edge for edge in edges if edge[1] == eschema]
+ if not edges:
+ warn("%s has %s but isn't reached by "
+ "propagation", eschema, self.prop_rel)
+ return True
+ # require_permission relation / propagation rule not added by
+ # the same cube
+ elif not any(edge for edge in edges if edge[-1] == rdef.package):
+ warn('%s has %s relation / propagation rule'
+ ' not added by the same cube (%s / %s)', eschema,
+ self.prop_rel, rdef.package, edges[0][-1])
+ return True
+ return False
+
+ def init_graph(self, eschemas, edges, problematic):
+ """Initialize and return graph, adding given nodes (entity schemas) and
+ edges between them.
+
+ Require pygraphviz installed.
+ """
+ if pygraphviz is None:
+ raise RuntimeError('pygraphviz is not installed')
+ graph = pygraphviz.AGraph(strict=False, directed=True)
+ for eschema in eschemas:
+ if eschema in problematic:
+ params = {'color': '#ff0000', 'fontcolor': '#ff0000'}
+ else:
+ params = {}#'color': get_color(eschema.package)}
+ graph.add_node(eschema.type, **params)
+ for subj, obj, rtype, package in edges:
+ graph.add_edge(str(subj), str(obj), label=rtype,
+ color=get_color(package))
+ return graph
+
+ def add_colors_legend(self, graph):
+ """Add a legend of used colors to the graph."""
+ for package, color in sorted(_COLORS.iteritems()):
+ graph.add_node(package, color=color, fontcolor=color, shape='record')
+
+
+class CubeTracerSet(object):
+ """Dumb set implementation whose purpose is to keep track of which cube is
+ being loaded when something is added to the set.
+
+ Results will be found in the `value_cube` attribute dictionary.
+
+ See `localperms` or `nosylist` cubes for example usage (`hooks` module).
+ """
+ def __init__(self, vreg, wrapped):
+ self.vreg = vreg
+ self.wrapped = wrapped
+ self.value_cube = {}
+
+ def add(self, value):
+ self.wrapped.add(value)
+ cube = self.vreg.currently_loading_cube
+ if value in self.value_cube:
+ warn('%s is propagated by cube %s and cube %s',
+ value, self.value_cube[value], cube)
+ else:
+ self.value_cube[value] = cube
+
+ def __iter__(self):
+ return iter(self.wrapped)
+
+ def __ior__(self, other):
+ for value in other:
+ self.add(value)
+ return self
+
+ def __ror__(self, other):
+ other |= self.wrapped
+ return other
diff -r 2790fb8f7e03 -r e83cbc116352 devtools/testlib.py
--- a/devtools/testlib.py Fri Jun 21 14:22:39 2013 +0200
+++ b/devtools/testlib.py Fri Jan 10 17:12:20 2014 +0100
@@ -89,7 +89,7 @@
MAILBOX = []
-class Email:
+class Email(object):
"""you'll get instances of Email into MAILBOX during tests that trigger
some notification.
@@ -98,7 +98,8 @@
* `recipients` is a list of email address which are the recipients of this
message
"""
- def __init__(self, recipients, msg):
+ def __init__(self, fromaddr, recipients, msg):
+ self.fromaddr = fromaddr
self.recipients = recipients
self.msg = msg
@@ -125,8 +126,8 @@
pass
def close(self):
pass
- def sendmail(self, helo_addr, recipients, msg):
- MAILBOX.append(Email(recipients, msg))
+ def sendmail(self, fromaddr, recipients, msg):
+ MAILBOX.append(Email(fromaddr, recipients, msg))
cwconfig.SMTP = MockSMTP
@@ -419,13 +420,10 @@
return self.cnx.cursor(req or self.request())
@nocoverage
- def execute(self, rql, args=None, eidkey=None, req=None):
+ def execute(self, rql, args=None, req=None):
"""executes , builds a resultset, and returns a couple (rset, req)
where req is a FakeRequest
"""
- if eidkey is not None:
- warn('[3.8] eidkey is deprecated, you can safely remove this argument',
- DeprecationWarning, stacklevel=2)
req = req or self.request(rql=rql)
return req.execute(unicode(rql), args)
@@ -447,10 +445,7 @@
# server side db api #######################################################
- def sexecute(self, rql, args=None, eid_key=None):
- if eid_key is not None:
- warn('[3.8] eid_key is deprecated, you can safely remove this argument',
- DeprecationWarning, stacklevel=2)
+ def sexecute(self, rql, args=None):
self.session.set_cnxset()
return self.session.execute(rql, args)
@@ -986,15 +981,6 @@
self.assertEqual(len(MAILBOX), nb_msgs)
return messages
- # deprecated ###############################################################
-
- @deprecated('[3.8] use self.execute(...).get_entity(0, 0)')
- def entity(self, rql, args=None, eidkey=None, req=None):
- if eidkey is not None:
- warn('[3.8] eidkey is deprecated, you can safely remove this argument',
- DeprecationWarning, stacklevel=2)
- return self.execute(rql, args, req=req).get_entity(0, 0)
-
# auto-populating test classes and utilities ###################################
diff -r 2790fb8f7e03 -r e83cbc116352 doc/3.15.rst
--- a/doc/3.15.rst Fri Jun 21 14:22:39 2013 +0200
+++ b/doc/3.15.rst Fri Jan 10 17:12:20 2014 +0100
@@ -13,7 +13,7 @@
used for entity cache invalidation.
* Improved WSGI support. While there is still some caveats, most of the code
- which as twisted only is now generic and allows related functionalities to work
+ which was twisted only is now generic and allows related functionalities to work
with a WSGI front-end.
* Full undo/transaction support : undo of modification has eventually been
diff -r 2790fb8f7e03 -r e83cbc116352 doc/3.18.rst
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/3.18.rst Fri Jan 10 17:12:20 2014 +0100
@@ -0,0 +1,101 @@
+What's new in CubicWeb 3.18?
+============================
+
+The migration script does not handle sqlite nor mysql instances.
+
+
+New functionalities
+--------------------
+
+* add a security debugging tool
+ (see `#2920304 `_)
+
+* introduce an `add` permission on attributes, to be interpreted at
+ entity creation time only and allow the implementation of complex
+ `update` rules that don't block entity creation (before that the
+ `update` attribute permission was interpreted at entity creation and
+ update time)
+
+* the primary view display controller (uicfg) now has a
+ `set_fields_order` method similar to the one available for forms
+
+* new method `ResultSet.one(col=0)` to retrive a single entity and enforce the
+ result has only one row (see `#3352314 https://www.cubicweb.org/ticket/3352314`_)
+
+* new method `RequestSessionBase.find` to look for entities
+ (see `#3361290 https://www.cubicweb.org/ticket/3361290`_)
+
+* the embedded jQuery copy has been updated to version 1.10.2, and jQuery UI to
+ version 1.10.3.
+
+* initial support for wsgi for the debug mode, available through the new
+ ``wsgi`` cubicweb-ctl command, which can use either python's builtin
+ wsgi server or the werkzeug module if present.
+
+* a ``rql-table`` directive is now available in ReST fields
+
+* cubicweb-ctl upgrade can now generate the static data resource directory
+ directly, without a manual call to gen-static-datadir.
+
+API changes
+-----------
+
+* not really an API change, but the entity permission checks are now
+ systematically deferred to an operation, instead of a) trying in a
+ hook and b) if it failed, retrying later in an operation
+
+* The default value storage for attributes is no longer String, but
+ Bytes. This opens the road to storing arbitrary python objects, e.g.
+ numpy arrays, and fixes a bug where default values whose truth value
+ was False were not properly migrated.
+
+* `symmetric` relations are no more handled by an rql rewrite but are
+ now handled with hooks (from the `activeintegrity` category); this
+ may have some consequences for applications that do low-level database
+ manipulations or at times disable (some) hooks.
+
+* `unique together` constraints (multi-columns unicity constraints)
+ get a `name` attribute that maps the CubicWeb contraint entities to
+ corresponding backend index.
+
+* BreadCrumbEntityVComponent's open_breadcrumbs method now includes
+ the first breadcrumbs separator
+
+* entities can be compared for equality and hashed
+
+* the ``on_fire_transition`` predicate accepts a sequence of possible
+ transition names
+
+* the GROUP_CONCAT rql aggregate function no longer repeats duplicate
+ values, on the sqlite and postgresql backends
+
+Deprecation
+---------------------
+
+* ``pyrorql`` sources have been deprecated. Multisource will be fully dropped
+ in the next version. If you are still using pyrorql, switch to ``datafeed``
+ **NOW**!
+
+* the old multi-source system
+
+* `find_one_entity` and `find_entities` in favor of `find`
+ (see `#3361290 https://www.cubicweb.org/ticket/3361290`_)
+
+* the `TmpFileViewMixin` and `TmpPngView` classes (see `#3400448
+ https://www.cubicweb.org/ticket/3400448`_)
+
+Deprecated Code Drops
+----------------------
+
+* ``ldapuser`` have been dropped; use ``ldapfeed`` now
+ (see `#2936496 `_)
+
+* action ``GotRhythm`` was removed, make sure you do not
+ import it in your cubes (even to unregister it)
+ (see `#3093362 `_)
+
+* all 3.8 backward compat is gone
+
+* all 3.9 backward compat (including the javascript side) is gone
+
+* the ``twisted`` (web-only) instance type has been removed
diff -r 2790fb8f7e03 -r e83cbc116352 doc/book/en/admin/config.rst
--- a/doc/book/en/admin/config.rst Fri Jun 21 14:22:39 2013 +0200
+++ b/doc/book/en/admin/config.rst Fri Jan 10 17:12:20 2014 +0100
@@ -57,19 +57,28 @@
PostgreSQL
~~~~~~~~~~
-For installation, please refer to the `PostgreSQL project online documentation`_.
-
-.. _`PostgreSQL project online documentation`: http://www.postgresql.org/
+Many Linux distributions ship with the appropriate PostgreSQL packages.
+Basically, you need to install the following packages:
-You need to install the three following packages: `postgresql-8.X`,
-`postgresql-client-8.X`, and `postgresql-plpython-8.X`. If you run postgres
-version prior to 8.3, you'll also need the `postgresql-contrib-8.X` package for
-full-text search extension.
+* `postgresql` and `postgresql-client`, which will pull the respective
+ versioned packages (e.g. `postgresql-9.1` and `postgresql-client-9.1`) and,
+ optionally,
+* a `postgresql-plpython-X.Y` package with a version corresponding to that of
+ the aforementioned packages (e.g. `postgresql-plpython-9.1`).
+
+If you run postgres version prior to 8.3, you'll also need the
+`postgresql-contrib-8.X` package for full-text search extension.
If you run postgres on another host than the |cubicweb| repository, you should
install the `postgresql-client` package on the |cubicweb| host, and others on the
database host.
+For extra details concerning installation, please refer to the `PostgreSQL
+project online documentation`_.
+
+.. _`PostgreSQL project online documentation`: http://www.postgresql.org/docs
+
+
Database cluster
++++++++++++++++
diff -r 2790fb8f7e03 -r e83cbc116352 doc/book/en/admin/setup.rst
--- a/doc/book/en/admin/setup.rst Fri Jun 21 14:22:39 2013 +0200
+++ b/doc/book/en/admin/setup.rst Fri Jan 10 17:12:20 2014 +0100
@@ -121,10 +121,10 @@
`Virtualenv` install
--------------------
-Since version 3.9, |cubicweb| can be safely installed, used and contained inside
-a `virtualenv`_. You can use either :ref:`pip ` or
-:ref:`easy_install ` to install |cubicweb| inside an
-activated virtual environment.
+|cubicweb| can be safely installed, used and contained inside a
+`virtualenv`_. You can use either :ref:`pip ` or
+:ref:`easy_install ` to install |cubicweb|
+inside an activated virtual environment.
.. _PipInstallation:
diff -r 2790fb8f7e03 -r e83cbc116352 doc/book/en/devrepo/datamodel/definition.rst
--- a/doc/book/en/devrepo/datamodel/definition.rst Fri Jun 21 14:22:39 2013 +0200
+++ b/doc/book/en/devrepo/datamodel/definition.rst Fri Jan 10 17:12:20 2014 +0100
@@ -226,13 +226,13 @@
* `SizeConstraint`: allows to specify a minimum and/or maximum size on
string (generic case of `maxsize`)
-* `BoundConstraint`: allows to specify a minimum and/or maximum value
+* `BoundaryConstraint`: allows to specify a minimum and/or maximum value
on numeric types and date
.. sourcecode:: python
- from yams.constraints import BoundConstraint, TODAY
- BoundConstraint('<=', TODAY())
+ from yams.constraints import BoundaryConstraint, TODAY
+ BoundaryConstraint('<=', TODAY())
* `IntervalBoundConstraint`: allows to specify an interval with
included values
@@ -330,7 +330,8 @@
For a relation, the possible actions are `read`, `add`, and `delete`.
-For an attribute, the possible actions are `read`, and `update`.
+For an attribute, the possible actions are `read`, `add` and `update`,
+and they are a refinement of an entity type permission.
For each access type, a tuple indicates the name of the authorized groups and/or
one or multiple RQL expressions to satisfy to grant access. The access is
@@ -364,7 +365,8 @@
.. sourcecode:: python
__permissions__ = {'read': ('managers', 'users', 'guests',),
- 'update': ('managers', ERQLExpression('U has_update_permission X')),}
+ 'add': ('managers', ERQLExpression('U has_add_permission X'),
+ 'update': ('managers', ERQLExpression('U has_update_permission X')),}
The standard user groups
````````````````````````
@@ -476,13 +478,8 @@
Here are the current rules:
-1. permission to add/update entity and its attributes are checked:
-
- - on commit if the entity has been added
-
- - in an 'after_update_entity' hook if the entity has been updated. If it fails
- at this time, it will be retried on commit (hence you get the permission if
- you have it just after the modification or *at* commit time)
+1. permission to add/update entity and its attributes are checked on
+ commit
2. permission to delete an entity is checked in 'before_delete_entity' hook
diff -r 2790fb8f7e03 -r e83cbc116352 doc/book/en/devrepo/entityclasses/adapters.rst
--- a/doc/book/en/devrepo/entityclasses/adapters.rst Fri Jun 21 14:22:39 2013 +0200
+++ b/doc/book/en/devrepo/entityclasses/adapters.rst Fri Jan 10 17:12:20 2014 +0100
@@ -10,13 +10,7 @@
.. _`interfaces`: http://java.sun.com/docs/books/tutorial/java/concepts/interface.html
.. _`adapter`: http://en.wikipedia.org/wiki/Adapter_pattern
-In |cubicweb| adapters provide logical functionalities to entity types. They
-are introduced in version `3.9`. Before that one had to implement Interfaces in
-entity classes to achieve a similar goal. However, the problem with this
-approach is that is clutters the entity class's namespace, exposing name
-collision risks with schema attributes/relations or even methods names
-(different interfaces may define the same method with not necessarily the same
-behaviour expected).
+In |cubicweb| adapters provide logical functionalities to entity types.
Definition of an adapter is quite trivial. An excerpt from cubicweb
itself (found in :mod:`cubicweb.entities.adapters`):
diff -r 2790fb8f7e03 -r e83cbc116352 doc/book/en/devrepo/migration.rst
--- a/doc/book/en/devrepo/migration.rst Fri Jun 21 14:22:39 2013 +0200
+++ b/doc/book/en/devrepo/migration.rst Fri Jan 10 17:12:20 2014 +0100
@@ -46,7 +46,7 @@
Again in the directory `migration`, the file `depends.map` allows to indicate
that for the migration to a particular model version, you always have to first
migrate to a particular *CubicWeb* version. This file can contain comments (lines
-starting by `#`) and a dependancy is listed as follows: ::
+starting with `#`) and a dependency is listed as follows: ::
:
@@ -170,9 +170,9 @@
* `rql(rql, kwargs=None, cachekey=None, ask_confirm=True)`, executes an arbitrary RQL
query, either to interrogate or update. A result set object is returned.
-* `add_entity(etype, *args, **kwargs)`, adds a nes entity type of the given
- type. The attribute and relation values are specified using the named and
- positionned parameters.
+* `add_entity(etype, *args, **kwargs)`, adds a new entity of the given type.
+ The attribute and relation values are specified as named positional
+ arguments.
Workflow creation
-----------------
diff -r 2790fb8f7e03 -r e83cbc116352 doc/book/en/devrepo/testing.rst
--- a/doc/book/en/devrepo/testing.rst Fri Jun 21 14:22:39 2013 +0200
+++ b/doc/book/en/devrepo/testing.rst Fri Jan 10 17:12:20 2014 +0100
@@ -18,12 +18,7 @@
convenience methods to help test all of this.
In the realm of views, automatic tests check that views are valid
-XHTML. See :ref:`automatic_views_tests` for details. Since 3.9, bases
-for web functional testing using `windmill
- `_ are set. See test cases in
-cubicweb/web/test/windmill and python wrapper in
-cubicweb/web/test_windmill/ if you want to use this in your own cube.
-
+XHTML. See :ref:`automatic_views_tests` for details.
Most unit tests need a live database to work against. This is achieved
by CubicWeb using automatically sqlite (bundled with Python, see
diff -r 2790fb8f7e03 -r e83cbc116352 doc/book/en/tutorials/advanced/part02_security.rst
--- a/doc/book/en/tutorials/advanced/part02_security.rst Fri Jun 21 14:22:39 2013 +0200
+++ b/doc/book/en/tutorials/advanced/part02_security.rst Fri Jan 10 17:12:20 2014 +0100
@@ -259,26 +259,26 @@
# relations where the "parent" entity is the object
O_RELS = set(('filed_under', 'comments',))
- class AddEntitySecurityPropagationHook(hook.PropagateSubjectRelationHook):
+ class AddEntitySecurityPropagationHook(hook.PropagateRelationHook):
"""propagate permissions when new entity are added"""
__regid__ = 'sytweb.addentity_security_propagation'
- __select__ = (hook.PropagateSubjectRelationHook.__select__
+ __select__ = (hook.PropagateRelationHook.__select__
& hook.match_rtype_sets(S_RELS, O_RELS))
main_rtype = 'may_be_read_by'
subject_relations = S_RELS
object_relations = O_RELS
- class AddPermissionSecurityPropagationHook(hook.PropagateSubjectRelationAddHook):
+ class AddPermissionSecurityPropagationHook(hook.PropagateRelationAddHook):
"""propagate permissions when new entity are added"""
__regid__ = 'sytweb.addperm_security_propagation'
- __select__ = (hook.PropagateSubjectRelationAddHook.__select__
+ __select__ = (hook.PropagateRelationAddHook.__select__
& hook.match_rtype('may_be_read_by',))
subject_relations = S_RELS
object_relations = O_RELS
- class DelPermissionSecurityPropagationHook(hook.PropagateSubjectRelationDelHook):
+ class DelPermissionSecurityPropagationHook(hook.PropagateRelationDelHook):
__regid__ = 'sytweb.delperm_security_propagation'
- __select__ = (hook.PropagateSubjectRelationDelHook.__select__
+ __select__ = (hook.PropagateRelationDelHook.__select__
& hook.match_rtype('may_be_read_by',))
subject_relations = S_RELS
object_relations = O_RELS
diff -r 2790fb8f7e03 -r e83cbc116352 doc/book/en/tutorials/advanced/part04_ui-base.rst
--- a/doc/book/en/tutorials/advanced/part04_ui-base.rst Fri Jun 21 14:22:39 2013 +0200
+++ b/doc/book/en/tutorials/advanced/part04_ui-base.rst Fri Jan 10 17:12:20 2014 +0100
@@ -194,8 +194,6 @@
.. Note::
- * Adapters have been introduced in CubicWeb 3.9 / cubicweb-folder 1.8.
-
* As seen earlier, we want to **replace** the folder's `ITree` adapter by our
implementation, hence the custom `registration_callback` method.
@@ -241,12 +239,6 @@
ascendant/descendant ordering and a strict comparison with current file's name
(the "X" variable representing the current file).
-.. Note::
-
- * Former `implements` selector should be replaced by one of `is_instance` /
- `adaptable` selector with CubicWeb >= 3.9. In our case, `is_instance` to
- tell our adapter is able to adapt `File` entities.
-
Notice that this query supposes we wont have two files of the same name in the
same folder, else things may go wrong. Fixing this is out of the scope of this
blog. And as I would like to have at some point a smarter, context sensitive
@@ -358,7 +350,7 @@
You'll have to answer some questions, as we've seen in `an earlier post`_.
Now that everything is tested, I can transfer the new code to the production
-server, `apt-get upgrade` cubicweb 3.9 and its dependencies, and eventually
+server, `apt-get upgrade` cubicweb and its dependencies, and eventually
upgrade the production instance.
diff -r 2790fb8f7e03 -r e83cbc116352 doc/book/en/tutorials/advanced/part05_ui-advanced.rst
--- a/doc/book/en/tutorials/advanced/part05_ui-advanced.rst Fri Jun 21 14:22:39 2013 +0200
+++ b/doc/book/en/tutorials/advanced/part05_ui-advanced.rst Fri Jan 10 17:12:20 2014 +0100
@@ -1,8 +1,6 @@
Building my photos web site with |cubicweb| part V: let's make it even more user friendly
=========================================================================================
-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?
@@ -29,9 +27,9 @@
LOGO = data('logo.jpg')
-The uiprops machinery has been introduced in `CubicWeb 3.9`_. It is used to define
-some static file resources, such as the logo, default Javascript / CSS files, as
-well as CSS properties (we'll see that later).
+The uiprops machinery is used to define some static file resources,
+such as the logo, default Javascript / CSS files, as well as CSS
+properties (we'll see that later).
.. Note::
This file is imported specifically by |cubicweb|, with a predefined name space,
@@ -373,5 +371,4 @@
.. _`CubicWeb 3.10`: http://www.cubicweb.org/blogentry/1330518
-.. _`CubicWeb 3.9`: http://www.cubicweb.org/blogentry/1179899
.. _`here`: http://webdesign.about.com/od/css3/f/blfaqbgsize.htm
diff -r 2790fb8f7e03 -r e83cbc116352 doc/tools/pyjsrest.py
--- a/doc/tools/pyjsrest.py Fri Jun 21 14:22:39 2013 +0200
+++ b/doc/tools/pyjsrest.py Fri Jan 10 17:12:20 2014 +0100
@@ -136,7 +136,6 @@
'cubicweb.preferences',
'cubicweb.edition',
'cubicweb.reledit',
- 'cubicweb.rhythm',
'cubicweb.timeline-ext',
]
diff -r 2790fb8f7e03 -r e83cbc116352 entities/adapters.py
--- a/entities/adapters.py Fri Jun 21 14:22:39 2013 +0200
+++ b/entities/adapters.py Fri Jan 10 17:12:20 2014 +0100
@@ -1,4 +1,4 @@
-# copyright 2010-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2010-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -28,9 +28,7 @@
from logilab.common.decorators import cached
from cubicweb import ValidationError, view
-from cubicweb.predicates import (implements, is_instance, relation_possible,
- match_exception)
-from cubicweb.interfaces import IDownloadable, ITree
+from cubicweb.predicates import is_instance, relation_possible, match_exception
class IEmailableAdapter(view.EntityAdapter):
@@ -67,11 +65,9 @@
class INotifiableAdapter(view.EntityAdapter):
- __needs_bw_compat__ = True
__regid__ = 'INotifiable'
__select__ = is_instance('Any')
- @view.implements_adapter_compat('INotifiableAdapter')
def notification_references(self, view):
"""used to control References field of email send on notification
for this entity. `view` is the notification view.
@@ -167,27 +163,25 @@
class IDownloadableAdapter(view.EntityAdapter):
"""interface for downloadable entities"""
- __needs_bw_compat__ = True
__regid__ = 'IDownloadable'
- __select__ = implements(IDownloadable, warn=False) # XXX for bw compat, else should be abstract
+ __abstract__ = True
- @view.implements_adapter_compat('IDownloadable')
def download_url(self, **kwargs): # XXX not really part of this interface
"""return an url to download entity's content"""
raise NotImplementedError
- @view.implements_adapter_compat('IDownloadable')
+
def download_content_type(self):
"""return MIME type of the downloadable content"""
raise NotImplementedError
- @view.implements_adapter_compat('IDownloadable')
+
def download_encoding(self):
"""return encoding of the downloadable content"""
raise NotImplementedError
- @view.implements_adapter_compat('IDownloadable')
+
def download_file_name(self):
"""return file name of the downloadable content"""
raise NotImplementedError
- @view.implements_adapter_compat('IDownloadable')
+
def download_data(self):
"""return actual data of the downloadable content"""
raise NotImplementedError
@@ -219,27 +213,16 @@
.. automethod: children_rql
.. automethod: path
"""
- __needs_bw_compat__ = True
__regid__ = 'ITree'
- __select__ = implements(ITree, warn=False) # XXX for bw compat, else should be abstract
+ __abstract__ = True
child_role = 'subject'
parent_role = 'object'
- @property
- def tree_relation(self):
- warn('[3.9] tree_attribute is deprecated, define tree_relation on a custom '
- 'ITree for %s instead' % (self.entity.__class__),
- DeprecationWarning)
- return self.entity.tree_attribute
-
- # XXX should be removed from the public interface
- @view.implements_adapter_compat('ITree')
def children_rql(self):
"""Returns RQL to get the children of the entity."""
return self.entity.cw_related_rql(self.tree_relation, self.parent_role)
- @view.implements_adapter_compat('ITree')
def different_type_children(self, entities=True):
"""Return children entities of different type as this entity.
@@ -253,7 +236,6 @@
return [e for e in res if e.e_schema != eschema]
return res.filtered_rset(lambda x: x.e_schema != eschema, self.entity.cw_col)
- @view.implements_adapter_compat('ITree')
def same_type_children(self, entities=True):
"""Return children entities of the same type as this entity.
@@ -267,23 +249,19 @@
return [e for e in res if e.e_schema == eschema]
return res.filtered_rset(lambda x: x.e_schema is eschema, self.entity.cw_col)
- @view.implements_adapter_compat('ITree')
def is_leaf(self):
"""Returns True if the entity does not have any children."""
return len(self.children()) == 0
- @view.implements_adapter_compat('ITree')
def is_root(self):
"""Returns true if the entity is root of the tree (e.g. has no parent).
"""
return self.parent() is None
- @view.implements_adapter_compat('ITree')
def root(self):
"""Return the root entity of the tree."""
return self._cw.entity_from_eid(self.path()[0])
- @view.implements_adapter_compat('ITree')
def parent(self):
"""Returns the parent entity if any, else None (e.g. if we are on the
root).
@@ -294,7 +272,6 @@
except (KeyError, IndexError):
return None
- @view.implements_adapter_compat('ITree')
def children(self, entities=True, sametype=False):
"""Return children entities.
@@ -307,7 +284,6 @@
return self.entity.related(self.tree_relation, self.parent_role,
entities=entities)
- @view.implements_adapter_compat('ITree')
def iterparents(self, strict=True):
"""Return an iterator on the parents of the entity."""
def _uptoroot(self):
@@ -322,7 +298,6 @@
return chain([self.entity], _uptoroot(self))
return _uptoroot(self)
- @view.implements_adapter_compat('ITree')
def iterchildren(self, _done=None):
"""Return an iterator over the item's children."""
if _done is None:
@@ -334,7 +309,6 @@
yield child
_done.add(child.eid)
- @view.implements_adapter_compat('ITree')
def prefixiter(self, _done=None):
"""Return an iterator over the item's descendants in a prefixed order."""
if _done is None:
@@ -347,7 +321,6 @@
for entity in child.cw_adapt_to('ITree').prefixiter(_done):
yield entity
- @view.implements_adapter_compat('ITree')
@cached
def path(self):
"""Returns the list of eids from the root object to this object."""
@@ -389,23 +362,12 @@
__select__ = match_exception(UniqueTogetherError)
def raise_user_exception(self):
- etype, rtypes = self.exc.args
- # Because of index name size limits (e.g: postgres around 64,
- # sqlserver around 128), we cannot be sure of what we got,
- # especially for the rtypes part.
- # Hence we will try to validate them, and handle invalid ones
- # in the most user-friendly manner ...
_ = self._cw._
- schema = self.entity._cw.vreg.schema
+ rtypes = self.exc.rtypes
rtypes_msg = {}
for rtype in rtypes:
- if rtype in schema:
- rtypes_msg[rtype] = _('%s is part of violated unicity constraint') % rtype
- globalmsg = _('some relations %sviolate a unicity constraint')
- if len(rtypes) != len(rtypes_msg): # we got mangled/missing rtypes
- globalmsg = globalmsg % _('(not all shown here) ')
- else:
- globalmsg = globalmsg % ''
+ rtypes_msg[rtype] = _('%s is part of violated unicity constraint') % rtype
+ globalmsg = _('some relations violate a unicity constraint')
rtypes_msg['unicity constraint'] = globalmsg
raise ValidationError(self.entity.eid, rtypes_msg)
diff -r 2790fb8f7e03 -r e83cbc116352 entities/test/unittest_base.py
--- a/entities/test/unittest_base.py Fri Jun 21 14:22:39 2013 +0200
+++ b/entities/test/unittest_base.py Fri Jan 10 17:12:20 2014 +0100
@@ -25,7 +25,6 @@
from cubicweb.devtools.testlib import CubicWebTC
-from cubicweb.interfaces import IMileStone, ICalendarable
from cubicweb.entities import AnyEntity
@@ -134,27 +133,6 @@
self.request().create_entity('CWGroup', name=u'logilab', reverse_in_group=e)
-class InterfaceTC(CubicWebTC):
-
- def test_nonregr_subclasses_and_mixins_interfaces(self):
- from cubicweb.entities.wfobjs import WorkflowableMixIn
- WorkflowableMixIn.__implements__ = (ICalendarable,)
- CWUser = self.vreg['etypes'].etype_class('CWUser')
- class MyUser(CWUser):
- __implements__ = (IMileStone,)
- self.vreg._loadedmods[__name__] = {}
- self.vreg.register(MyUser)
- self.vreg['etypes'].initialization_completed()
- MyUser_ = self.vreg['etypes'].etype_class('CWUser')
- # a copy is done systematically
- self.assertTrue(issubclass(MyUser_, MyUser))
- self.assertTrue(implements(MyUser_, IMileStone))
- self.assertTrue(implements(MyUser_, ICalendarable))
- # original class should not have beed modified, only the copy
- self.assertTrue(implements(MyUser, IMileStone))
- self.assertFalse(implements(MyUser, ICalendarable))
-
-
class SpecializedEntityClassesTC(CubicWebTC):
def select_eclass(self, etype):
diff -r 2790fb8f7e03 -r e83cbc116352 entities/wfobjs.py
--- a/entities/wfobjs.py Fri Jun 21 14:22:39 2013 +0200
+++ b/entities/wfobjs.py Fri Jan 10 17:12:20 2014 +0100
@@ -32,7 +32,6 @@
from cubicweb.entities import AnyEntity, fetch_config
from cubicweb.view import EntityAdapter
from cubicweb.predicates import relation_possible
-from cubicweb.mixins import MI_REL_TRIGGERS
class WorkflowException(Exception): pass
@@ -379,65 +378,8 @@
return self.by_transition and self.by_transition[0] or None
-class WorkflowableMixIn(object):
- """base mixin providing workflow helper methods for workflowable entities.
- This mixin will be automatically set on class supporting the 'in_state'
- relation (which implies supporting 'wf_info_for' as well)
- """
- @property
- @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').main_workflow")
- def main_workflow(self):
- return self.cw_adapt_to('IWorkflowable').main_workflow
- @property
- @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').current_workflow")
- def current_workflow(self):
- return self.cw_adapt_to('IWorkflowable').current_workflow
- @property
- @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').current_state")
- def current_state(self):
- return self.cw_adapt_to('IWorkflowable').current_state
- @property
- @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').state")
- def state(self):
- return self.cw_adapt_to('IWorkflowable').state
- @property
- @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').printable_state")
- def printable_state(self):
- return self.cw_adapt_to('IWorkflowable').printable_state
- @property
- @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').workflow_history")
- def workflow_history(self):
- return self.cw_adapt_to('IWorkflowable').workflow_history
-
- @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').cwetype_workflow()")
- def cwetype_workflow(self):
- return self.cw_adapt_to('IWorkflowable').main_workflow()
- @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').latest_trinfo()")
- def latest_trinfo(self):
- return self.cw_adapt_to('IWorkflowable').latest_trinfo()
- @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').possible_transitions()")
- def possible_transitions(self, type='normal'):
- return self.cw_adapt_to('IWorkflowable').possible_transitions(type)
- @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').fire_transition()")
- def fire_transition(self, tr, comment=None, commentformat=None):
- return self.cw_adapt_to('IWorkflowable').fire_transition(tr, comment, commentformat)
- @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').change_state()")
- def change_state(self, statename, comment=None, commentformat=None, tr=None):
- return self.cw_adapt_to('IWorkflowable').change_state(statename, comment, commentformat, tr)
- @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').subworkflow_input_trinfo()")
- def subworkflow_input_trinfo(self):
- return self.cw_adapt_to('IWorkflowable').subworkflow_input_trinfo()
- @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').subworkflow_input_transition()")
- def subworkflow_input_transition(self):
- return self.cw_adapt_to('IWorkflowable').subworkflow_input_transition()
-
-
-MI_REL_TRIGGERS[('in_state', 'subject')] = WorkflowableMixIn
-
-
-
-class IWorkflowableAdapter(WorkflowableMixIn, EntityAdapter):
+class IWorkflowableAdapter(EntityAdapter):
"""base adapter providing workflow helper methods for workflowable entities.
"""
__regid__ = 'IWorkflowable'
diff -r 2790fb8f7e03 -r e83cbc116352 entity.py
--- a/entity.py Fri Jun 21 14:22:39 2013 +0200
+++ b/entity.py Fri Jan 10 17:12:20 2014 +0100
@@ -42,7 +42,6 @@
from cubicweb.rqlrewrite import RQLRewriter
from cubicweb.uilib import soup2xhtml
-from cubicweb.mixins import MI_REL_TRIGGERS
from cubicweb.mttransforms import ENGINE
_marker = object()
@@ -194,31 +193,11 @@
setattr(cls, rschema.type, Attribute(rschema.type))
mixins = []
for rschema, _, role in eschema.relation_definitions():
- if (rschema, role) in MI_REL_TRIGGERS:
- mixin = MI_REL_TRIGGERS[(rschema, role)]
- if not (issubclass(cls, mixin) or mixin in mixins): # already mixed ?
- mixins.append(mixin)
- for iface in getattr(mixin, '__implements__', ()):
- if not interface.implements(cls, iface):
- interface.extend(cls, iface)
if role == 'subject':
attr = rschema.type
else:
attr = 'reverse_%s' % rschema.type
setattr(cls, attr, Relation(rschema, role))
- if mixins:
- # see etype class instantation in cwvreg.ETypeRegistry.etype_class method:
- # due to class dumping, cls is the generated top level class with actual
- # user class as (only) parent. Since we want to be able to override mixins
- # method from this user class, we have to take care to insert mixins after that
- # class
- #
- # note that we don't plug mixins as user class parent since it causes pb
- # with some cases of entity classes inheritance.
- mixins.insert(0, cls.__bases__[0])
- mixins += cls.__bases__[1:]
- cls.__bases__ = tuple(mixins)
- cls.info('plugged %s mixins on %s', mixins, cls)
fetch_attrs = ('modification_date',)
@@ -308,7 +287,10 @@
select._varmaker = rqlvar_maker(defined=select.defined_vars,
aliases=select.aliases, index=26)
if settype:
- select.add_type_restriction(mainvar, cls.__regid__)
+ rel = select.add_type_restriction(mainvar, cls.__regid__)
+ # should use 'is_instance_of' instead of 'is' so we retrieve
+ # subclasses instances as well
+ rel.r_type = 'is_instance_of'
if fetchattrs is None:
fetchattrs = cls.fetch_attrs
cls._fetch_restrictions(mainvar, select, fetchattrs, user, ordermethod)
@@ -558,7 +540,14 @@
raise NotImplementedError('comparison not implemented for %s' % self.__class__)
def __eq__(self, other):
- raise NotImplementedError('comparison not implemented for %s' % self.__class__)
+ if isinstance(self.eid, (int, long)):
+ return self.eid == other.eid
+ return self is other
+
+ def __hash__(self):
+ if isinstance(self.eid, (int, long)):
+ return self.eid
+ return super(Entity, self).__hash__()
def _cw_update_attr_cache(self, attrcache):
# if context is a repository session, don't consider dont-cache-attrs as
@@ -983,7 +972,7 @@
return value
def related(self, rtype, role='subject', limit=None, entities=False, # XXX .cw_related
- safe=False):
+ safe=False, targettypes=None):
"""returns a resultset of related entities
:param rtype:
@@ -997,10 +986,13 @@
:param safe:
if True, an empty rset/list of entities will be returned in case of
:exc:`Unauthorized`, else (the default), the exception is propagated
+ :param targettypes:
+ a tuple of target entity types to restrict the query
"""
rtype = str(rtype)
- if limit is None:
- # we cannot do much wrt cache on limited queries
+ # Caching restricted/limited results is best avoided.
+ cacheable = limit is None and targettypes is None
+ if cacheable:
cache_key = '%s_%s' % (rtype, role)
if cache_key in self._cw_related_cache:
return self._cw_related_cache[cache_key][entities]
@@ -1008,7 +1000,7 @@
if entities:
return []
return self._cw.empty_rset()
- rql = self.cw_related_rql(rtype, role, limit=limit)
+ rql = self.cw_related_rql(rtype, role, limit=limit, targettypes=targettypes)
try:
rset = self._cw.execute(rql, {'x': self.eid})
except Unauthorized:
@@ -1016,9 +1008,9 @@
raise
rset = self._cw.empty_rset()
if entities:
- if limit is None:
+ if cacheable:
self.cw_set_relation_cache(rtype, role, rset)
- return self.related(rtype, role, limit, entities)
+ return self.related(rtype, role, entities=entities)
return list(rset.entities())
else:
return rset
@@ -1340,34 +1332,6 @@
def clear_all_caches(self):
return self.cw_clear_all_caches()
- @deprecated('[3.9] use entity.cw_attr_value(attr)')
- def get_value(self, name):
- return self.cw_attr_value(name)
-
- @deprecated('[3.9] use entity.cw_delete()')
- def delete(self, **kwargs):
- return self.cw_delete(**kwargs)
-
- @deprecated('[3.9] use entity.cw_attr_metadata(attr, metadata)')
- def attr_metadata(self, attr, metadata):
- return self.cw_attr_metadata(attr, metadata)
-
- @deprecated('[3.9] use entity.cw_has_perm(action)')
- def has_perm(self, action):
- return self.cw_has_perm(action)
-
- @deprecated('[3.9] use entity.cw_set_relation_cache(rtype, role, rset)')
- def set_related_cache(self, rtype, role, rset):
- self.cw_set_relation_cache(rtype, role, rset)
-
- @deprecated('[3.9] use entity.cw_clear_relation_cache(rtype, role)')
- def clear_related_cache(self, rtype=None, role=None):
- self.cw_clear_relation_cache(rtype, role)
-
- @deprecated('[3.9] use entity.cw_related_rql(rtype, [role, [targettypes]])')
- def related_rql(self, rtype, role='subject', targettypes=None):
- return self.cw_related_rql(rtype, role, targettypes)
-
@property
@deprecated('[3.10] use entity.cw_edited')
def edited_attributes(self):
diff -r 2790fb8f7e03 -r e83cbc116352 etwist/request.py
--- a/etwist/request.py Fri Jun 21 14:22:39 2013 +0200
+++ b/etwist/request.py Fri Jan 10 17:12:20 2014 +0100
@@ -24,15 +24,21 @@
class CubicWebTwistedRequestAdapter(CubicWebRequestBase):
+ """ from twisted .req to cubicweb .form
+ req.files are put into .form[]
+ """
def __init__(self, req, vreg, https):
self._twreq = req
super(CubicWebTwistedRequestAdapter, self).__init__(
vreg, https, req.args, headers=req.received_headers)
- for key, (name, stream) in req.files.iteritems():
- if name is None:
- self.form[key] = (name, stream)
- else:
- self.form[key] = (unicode(name, self.encoding), stream)
+ for key, name_stream_list in req.files.iteritems():
+ for name, stream in name_stream_list:
+ if name is not None:
+ name = unicode(name, self.encoding)
+ self.form.setdefault(key, []).append((name, stream))
+ # 3.16.4 backward compat
+ if len(self.form[key]) == 1:
+ self.form[key] = self.form[key][0]
self.content = self._twreq.content # stream
def http_method(self):
diff -r 2790fb8f7e03 -r e83cbc116352 etwist/server.py
--- a/etwist/server.py Fri Jun 21 14:22:39 2013 +0200
+++ b/etwist/server.py Fri Jan 10 17:12:20 2014 +0100
@@ -244,7 +244,6 @@
self._do_process_multipart = True
self.process()
-
@monkeypatch(http.Request)
def process_multipart(self):
if not self._do_process_multipart:
@@ -254,16 +253,17 @@
keep_blank_values=1,
strict_parsing=1)
for key in form:
- value = form[key]
- if isinstance(value, list):
- self.args[key] = [v.value for v in value]
- elif value.filename:
- if value.done != -1: # -1 is transfer has been interrupted
- self.files[key] = (value.filename, value.file)
+ values = form[key]
+ if not isinstance(values, list):
+ values = [values]
+ for value in values:
+ if value.filename:
+ if value.done != -1: # -1 is transfer has been interrupted
+ self.files.setdefault(key, []).append((value.filename, value.file))
+ else:
+ self.files.setdefault(key, []).append((None, None))
else:
- self.files[key] = (None, None)
- else:
- self.args[key] = value.value
+ self.args.setdefault(key, []).append(value.value)
from logging import getLogger
from cubicweb import set_log_methods
diff -r 2790fb8f7e03 -r e83cbc116352 etwist/twconfig.py
--- a/etwist/twconfig.py Fri Jun 21 14:22:39 2013 +0200
+++ b/etwist/twconfig.py Fri Jan 10 17:12:20 2014 +0100
@@ -34,9 +34,8 @@
from cubicweb.web.webconfig import WebConfiguration
-class TwistedConfiguration(WebConfiguration):
+class WebConfigurationBase(WebConfiguration):
"""web instance (in a twisted web server) client of a RQL server"""
- name = 'twisted'
options = merge_options((
# ctl configuration
@@ -107,19 +106,17 @@
return 'http://%s:%s/' % (self['host'] or getfqdn(), self['port'] or 8080)
-CONFIGURATIONS.append(TwistedConfiguration)
-
try:
from cubicweb.server.serverconfig import ServerConfiguration
- class AllInOneConfiguration(TwistedConfiguration, ServerConfiguration):
+ class AllInOneConfiguration(WebConfigurationBase, ServerConfiguration):
"""repository and web instance in the same twisted process"""
name = 'all-in-one'
- options = merge_options(TwistedConfiguration.options
+ options = merge_options(WebConfigurationBase.options
+ ServerConfiguration.options)
- cubicweb_appobject_path = TwistedConfiguration.cubicweb_appobject_path | ServerConfiguration.cubicweb_appobject_path
- cube_appobject_path = TwistedConfiguration.cube_appobject_path | ServerConfiguration.cube_appobject_path
+ cubicweb_appobject_path = WebConfigurationBase.cubicweb_appobject_path | ServerConfiguration.cubicweb_appobject_path
+ cube_appobject_path = WebConfigurationBase.cube_appobject_path | ServerConfiguration.cube_appobject_path
def pyro_enabled(self):
"""tell if pyro is activated for the in memory repository"""
return self['pyro-server']
diff -r 2790fb8f7e03 -r e83cbc116352 etwist/twctl.py
--- a/etwist/twctl.py Fri Jun 21 14:22:39 2013 +0200
+++ b/etwist/twctl.py Fri Jan 10 17:12:20 2014 +0100
@@ -22,7 +22,7 @@
from logilab.common.shellutils import rm
from cubicweb.toolsutils import CommandHandler
-from cubicweb.web.webctl import WebCreateHandler
+from cubicweb.web.webctl import WebCreateHandler, WebUpgradeHandler
# trigger configuration registration
import cubicweb.etwist.twconfig # pylint: disable=W0611
@@ -48,6 +48,9 @@
def poststop(self):
pass
+class TWUpgradeHandler(WebUpgradeHandler):
+ cfgname = 'twisted'
+
try:
from cubicweb.server import serverctl
@@ -73,5 +76,8 @@
cfgname = 'all-in-one'
subcommand = 'cubicweb-twisted'
+ class AllInOneUpgradeHandler(TWUpgradeHandler):
+ cfgname = 'all-in-one'
+
except ImportError:
pass
diff -r 2790fb8f7e03 -r e83cbc116352 ext/rest.py
--- a/ext/rest.py Fri Jun 21 14:22:39 2013 +0200
+++ b/ext/rest.py Fri Jan 10 17:12:20 2014 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -29,6 +29,8 @@
* `sourcecode` (if pygments is installed), source code colorization
+* `rql-table`, create a table from a RQL query
+
"""
__docformat__ = "restructuredtext en"
@@ -40,7 +42,7 @@
from docutils import statemachine, nodes, utils, io
from docutils.core import Publisher
-from docutils.parsers.rst import Parser, states, directives
+from docutils.parsers.rst import Parser, states, directives, Directive
from docutils.parsers.rst.roles import register_canonical_role, set_classes
from logilab.mtconverter import ESC_UCAR_TABLE, ESC_CAR_TABLE, xml_escape
@@ -251,6 +253,76 @@
winclude_directive.options = {'literal': directives.flag,
'encoding': directives.encoding}
+class RQLTableDirective(Directive):
+ """rql-table directive
+
+ Example:
+
+ .. rql-table::
+ :vid: mytable
+ :headers: , , progress
+ :colvids: 2=progress
+
+ Any X,U,X WHERE X is Project, X url U
+
+ All fields but the RQL string are optionnal. The ``:headers:`` option can
+ contain empty column names.
+ """
+
+ required_arguments = 0
+ optional_arguments = 0
+ has_content= True
+ final_argument_whitespace = True
+ option_spec = {'vid': directives.unchanged,
+ 'headers': directives.unchanged,
+ 'colvids': directives.unchanged}
+
+ def run(self):
+ errid = "rql-table directive"
+ self.assert_has_content()
+ if self.arguments:
+ raise self.warning('%s does not accept arguments' % errid)
+ rql = ' '.join([l.strip() for l in self.content])
+ _cw = self.state.document.settings.context._cw
+ _cw.ensure_ro_rql(rql)
+ try:
+ rset = _cw.execute(rql)
+ except Exception as exc:
+ raise self.error("fail to execute RQL query in %s: %r" %
+ (errid, exc))
+ if not rset:
+ raise self.warning("empty result set")
+ vid = self.options.get('vid', 'table')
+ try:
+ view = _cw.vreg['views'].select(vid, _cw, rset=rset)
+ except Exception as exc:
+ raise self.error("fail to select '%s' view in %s: %r" %
+ (vid, errid, exc))
+ headers = None
+ if 'headers' in self.options:
+ headers = [h.strip() for h in self.options['headers'].split(',')]
+ while headers.count(''):
+ headers[headers.index('')] = None
+ if len(headers) != len(rset[0]):
+ raise self.error("the number of 'headers' does not match the "
+ "number of columns in %s" % errid)
+ cellvids = None
+ if 'colvids' in self.options:
+ cellvids = {}
+ for f in self.options['colvids'].split(','):
+ try:
+ idx, vid = f.strip().split('=')
+ except ValueError:
+ raise self.error("malformatted 'colvids' option in %s" %
+ errid)
+ cellvids[int(idx.strip())] = vid.strip()
+ try:
+ content = view.render(headers=headers, cellvids=cellvids)
+ except Exception as exc:
+ raise self.error("Error rendering %s (%s)" % (errid, exc))
+ return [nodes.raw('', content, format='html')]
+
+
try:
from pygments import highlight
from pygments.lexers import get_lexer_by_name
@@ -385,3 +457,4 @@
directives.register_directive('winclude', winclude_directive)
if pygments_directive is not None:
directives.register_directive('sourcecode', pygments_directive)
+ directives.register_directive('rql-table', RQLTableDirective)
diff -r 2790fb8f7e03 -r e83cbc116352 ext/test/data/views.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ext/test/data/views.py Fri Jan 10 17:12:20 2014 +0100
@@ -0,0 +1,24 @@
+# copyright 2013 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 .
+
+
+from cubicweb.web.views import tableview
+
+class CustomRsetTableView(tableview.RsetTableView):
+ __regid__ = 'mytable'
+
diff -r 2790fb8f7e03 -r e83cbc116352 ext/test/unittest_rest.py
--- a/ext/test/unittest_rest.py Fri Jun 21 14:22:39 2013 +0200
+++ b/ext/test/unittest_rest.py Fri Jan 10 17:12:20 2014 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -82,5 +82,133 @@
out = rest_publish(context, ':bookmark:`%s`' % eid)
self.assertEqual(out, u'
CWUser_plural
\n')
+ def test_rqltable_nocontent(self):
+ context = self.context()
+ out = rest_publish(context, """.. rql-table::""")
+ self.assertIn("System Message: ERROR", out)
+ self.assertIn("Content block expected for the "rql-table" "
+ "directive; none found" , out)
+
+ def test_rqltable_norset(self):
+ context = self.context()
+ rql = "Any X WHERE X is CWUser, X firstname 'franky'"
+ out = rest_publish(
+ context, """\
+.. rql-table::
+
+ %(rql)s""" % {'rql': rql})
+ self.assertIn("System Message: WARNING", out)
+ self.assertIn("empty result set", out)
+
+ def test_rqltable_nooptions(self):
+ rql = """Any S,F,L WHERE X is CWUser, X surname S,
+ X firstname F, X login L"""
+ out = rest_publish(
+ self.context(), """\
+.. rql-table::
+
+ %(rql)s
+ """ % {'rql': rql})
+ req = self.request()
+ view = self.vreg['views'].select('table', req, rset=req.execute(rql))
+ self.assertEqual(view.render(w=None)[49:], out[49:])
+
+ def test_rqltable_vid(self):
+ rql = """Any S,F,L WHERE X is CWUser, X surname S,
+ X firstname F, X login L"""
+ vid = 'mytable'
+ out = rest_publish(
+ self.context(), """\
+.. rql-table::
+ :vid: %(vid)s
+
+ %(rql)s
+ """ % {'rql': rql, 'vid': vid})
+ req = self.request()
+ view = self.vreg['views'].select(vid, req, rset=req.execute(rql))
+ self.assertEqual(view.render(w=None)[49:], out[49:])
+ self.assertIn(vid, out[:49])
+
+ def test_rqltable_badvid(self):
+ rql = """Any S,F,L WHERE X is CWUser, X surname S,
+ X firstname F, X login L"""
+ vid = 'mytabel'
+ out = rest_publish(
+ self.context(), """\
+.. rql-table::
+ :vid: %(vid)s
+
+ %(rql)s
+ """ % {'rql': rql, 'vid': vid})
+ self.assertIn("fail to select '%s' view" % vid, out)
+
+ def test_rqltable_headers(self):
+ rql = """Any S,F,L WHERE X is CWUser, X surname S,
+ X firstname F, X login L"""
+ headers = ["nom", "prenom", "identifiant"]
+ out = rest_publish(
+ self.context(), """\
+.. rql-table::
+ :headers: %(headers)s
+
+ %(rql)s
+ """ % {'rql': rql, 'headers': ', '.join(headers)})
+ req = self.request()
+ view = self.vreg['views'].select('table', req, rset=req.execute(rql))
+ view.headers = headers
+ self.assertEqual(view.render(w=None)[49:], out[49:])
+
+ def test_rqltable_headers_missing(self):
+ rql = """Any S,F,L WHERE X is CWUser, X surname S,
+ X firstname F, X login L"""
+ headers = ["nom", "", "identifiant"]
+ out = rest_publish(
+ self.context(), """\
+.. rql-table::
+ :headers: %(headers)s
+
+ %(rql)s
+ """ % {'rql': rql, 'headers': ', '.join(headers)})
+ req = self.request()
+ view = self.vreg['views'].select('table', req, rset=req.execute(rql))
+ view.headers = [headers[0], None, headers[2]]
+ self.assertEqual(view.render(w=None)[49:], out[49:])
+
+ def test_rqltable_headers_missing_edges(self):
+ rql = """Any S,F,L WHERE X is CWUser, X surname S,
+ X firstname F, X login L"""
+ headers = [" ", "prenom", ""]
+ out = rest_publish(
+ self.context(), """\
+.. rql-table::
+ :headers: %(headers)s
+
+ %(rql)s
+ """ % {'rql': rql, 'headers': ', '.join(headers)})
+ req = self.request()
+ view = self.vreg['views'].select('table', req, rset=req.execute(rql))
+ view.headers = [None, headers[1], None]
+ self.assertEqual(view.render(w=None)[49:], out[49:])
+
+ def test_rqltable_colvids(self):
+ rql = """Any X,S,F,L WHERE X is CWUser, X surname S,
+ X firstname F, X login L"""
+ colvids = {0: "oneline"}
+ out = rest_publish(
+ self.context(), """\
+.. rql-table::
+ :colvids: %(colvids)s
+
+ %(rql)s
+ """ % {'rql': rql,
+ 'colvids': ', '.join(["%d=%s" % (k, v)
+ for k, v in colvids.iteritems()])
+ })
+ req = self.request()
+ view = self.vreg['views'].select('table', req, rset=req.execute(rql))
+ view.cellvids = colvids
+ self.assertEqual(view.render(w=None)[49:], out[49:])
+
+
if __name__ == '__main__':
unittest_main()
diff -r 2790fb8f7e03 -r e83cbc116352 hooks/integrity.py
--- a/hooks/integrity.py Fri Jun 21 14:22:39 2013 +0200
+++ b/hooks/integrity.py Fri Jan 10 17:12:20 2014 +0100
@@ -109,6 +109,30 @@
category = 'integrity'
+class EnsureSymmetricRelationsAdd(hook.Hook):
+ """ ensure X r Y => Y r X iff r is symmetric """
+ __regid__ = 'cw.add_ensure_symmetry'
+ category = 'activeintegrity'
+ events = ('after_add_relation',)
+ # __select__ is set in the registration callback
+
+ def __call__(self):
+ self._cw.repo.system_source.add_relation(self._cw, self.eidto,
+ self.rtype, self.eidfrom)
+
+
+class EnsureSymmetricRelationsDelete(hook.Hook):
+ """ ensure X r Y => Y r X iff r is symmetric """
+ __regid__ = 'cw.delete_ensure_symmetry'
+ category = 'activeintegrity'
+ events = ('after_delete_relation',)
+ # __select__ is set in the registration callback
+
+ def __call__(self):
+ self._cw.repo.system_source.delete_relation(self._cw, self.eidto,
+ self.rtype, self.eidfrom)
+
+
class CheckCardinalityHookBeforeDeleteRelation(IntegrityHook):
"""check cardinalities are satisfied"""
__regid__ = 'checkcard_before_delete_relation'
@@ -348,3 +372,11 @@
elif composite == 'object':
_DelayedDeleteSEntityOp.get_instance(self._cw).add_data(
(self.eidfrom, rtype))
+
+def registration_callback(vreg):
+ vreg.register_all(globals().values(), __name__)
+ symmetric_rtypes = [rschema.type for rschema in vreg.schema.relations()
+ if rschema.symmetric]
+ EnsureSymmetricRelationsAdd.__select__ = hook.Hook.__select__ & hook.match_rtype(*symmetric_rtypes)
+ EnsureSymmetricRelationsDelete.__select__ = hook.Hook.__select__ & hook.match_rtype(*symmetric_rtypes)
+
diff -r 2790fb8f7e03 -r e83cbc116352 hooks/metadata.py
--- a/hooks/metadata.py Fri Jun 21 14:22:39 2013 +0200
+++ b/hooks/metadata.py Fri Jan 10 17:12:20 2014 +0100
@@ -149,7 +149,7 @@
# entity source handling #######################################################
-class ChangeEntityUpdateCaches(hook.Operation):
+class ChangeEntitySourceUpdateCaches(hook.Operation):
oldsource = newsource = entity = None # make pylint happy
def postcommit_event(self):
@@ -221,6 +221,6 @@
'mtime': datetime.now()}
self._cw.system_sql(syssource.sqlgen.insert('entities', attrs), attrs)
# register an operation to update repository/sources caches
- ChangeEntityUpdateCaches(self._cw, entity=entity,
- oldsource=oldsource.repo_source,
- newsource=syssource)
+ ChangeEntitySourceUpdateCaches(self._cw, entity=entity,
+ oldsource=oldsource.repo_source,
+ newsource=syssource)
diff -r 2790fb8f7e03 -r e83cbc116352 hooks/security.py
--- a/hooks/security.py Fri Jun 21 14:22:39 2013 +0200
+++ b/hooks/security.py Fri Jan 10 17:12:20 2014 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -20,6 +20,7 @@
"""
__docformat__ = "restructuredtext en"
+from warnings import warn
from logilab.common.registry import objectify_predicate
@@ -29,8 +30,8 @@
from cubicweb.server import BEFORE_ADD_RELATIONS, ON_COMMIT_ADD_RELATIONS, hook
-_DEFAULT_UPDATE_ATTRPERM = buildobjs.DEFAULT_ATTRPERMS['update']
-def check_entity_attributes(session, entity, editedattrs=None, creation=False):
+
+def check_entity_attributes(session, entity, action, editedattrs=None):
eid = entity.eid
eschema = entity.e_schema
# ._cw_skip_security_attributes is there to bypass security for attributes
@@ -43,27 +44,25 @@
continue
rdef = eschema.rdef(attr)
if rdef.final: # non final relation are checked by standard hooks
- # attributes only have a specific 'update' permission
- updateperm = rdef.permissions.get('update')
+ perms = rdef.permissions.get(action)
# comparison below works because the default update perm is:
#
- # ('managers', ERQLExpression(Any X WHERE U has_update_permission X, X eid %(x)s, U eid %(u)s))
+ # ('managers', ERQLExpression(Any X WHERE U has_update_permission X,
+ # X eid %(x)s, U eid %(u)s))
#
# is deserialized in this order (groups first), and ERQLExpression
- # implements comparison by expression.
- if updateperm == _DEFAULT_UPDATE_ATTRPERM:
- # The default update permission is to delegate to the entity
- # update permission. This is an historical artefact but it is
- # costly (in general). Hence we take this permission object as a
- # marker saying "no specific" update permissions for this
- # attribute. Thus we just do nothing.
+ # implements comparison by rql expression.
+ if perms == buildobjs.DEFAULT_ATTRPERMS[action]:
+ # The default rule is to delegate to the entity
+ # rule. This is an historical artefact. Hence we take
+ # this object as a marker saying "no specific"
+ # permission rule for this attribute. Thus we just do
+ # nothing.
continue
- if creation and updateperm == ():
- # That actually means an immutable attribute. We make an
- # _exception_ to the `check attr update perms at entity create &
- # update time` rule for this case.
- continue
- rdef.check_perm(session, 'update', eid=eid)
+ if perms == ():
+ # That means an immutable attribute.
+ raise Unauthorized(action, str(rdef))
+ rdef.check_perm(session, action, eid=eid)
class CheckEntityPermissionOp(hook.DataOperationMixIn, hook.LateOperation):
@@ -72,8 +71,7 @@
for eid, action, edited in self.get_data():
entity = session.entity_from_eid(eid)
entity.cw_check_perm(action)
- check_entity_attributes(session, entity, edited,
- creation=(action == 'add'))
+ check_entity_attributes(session, entity, action, edited)
class CheckRelationPermissionOp(hook.DataOperationMixIn, hook.LateOperation):
@@ -111,17 +109,11 @@
events = ('after_update_entity',)
def __call__(self):
- try:
- # check user has permission right now, if not retry at commit time
- self.entity.cw_check_perm('update')
- check_entity_attributes(self._cw, self.entity)
- except Unauthorized:
- self.entity._cw_clear_local_perm_cache('update')
- # save back editedattrs in case the entity is reedited later in the
- # same transaction, which will lead to cw_edited being
- # overwritten
- CheckEntityPermissionOp.get_instance(self._cw).add_data(
- (self.entity.eid, 'update', self.entity.cw_edited) )
+ # save back editedattrs in case the entity is reedited later in the
+ # same transaction, which will lead to cw_edited being
+ # overwritten
+ CheckEntityPermissionOp.get_instance(self._cw).add_data(
+ (self.entity.eid, 'update', self.entity.cw_edited) )
class BeforeDelEntitySecurityHook(SecurityHook):
diff -r 2790fb8f7e03 -r e83cbc116352 hooks/syncschema.py
--- a/hooks/syncschema.py Fri Jun 21 14:22:39 2013 +0200
+++ b/hooks/syncschema.py Fri Jan 10 17:12:20 2014 +0100
@@ -28,7 +28,7 @@
from copy import copy
from yams.schema import BASE_TYPES, RelationSchema, RelationDefinitionSchema
-from yams import buildobjs as ybo, schema2sql as y2sql
+from yams import buildobjs as ybo, schema2sql as y2sql, convert_default_value
from logilab.common.decorators import clear_cache
@@ -39,21 +39,6 @@
from cubicweb.server import hook, schemaserial as ss
from cubicweb.server.sqlutils import SQL_PREFIX
-
-TYPE_CONVERTER = { # XXX
- 'Boolean': bool,
- 'Int': int,
- 'BigInt': int,
- 'Float': float,
- 'Password': str,
- 'String': unicode,
- 'Date' : unicode,
- 'Datetime' : unicode,
- 'Time' : unicode,
- 'TZDatetime' : unicode,
- 'TZTime' : unicode,
- }
-
# core entity and relation types which can't be removed
CORE_TYPES = BASE_TYPES | SCHEMA_TYPES | META_RTYPES | set(
('CWUser', 'CWGroup','login', 'upassword', 'name', 'in_group'))
@@ -116,7 +101,7 @@
if (specialization, rdefdef.object) in rschema.rdefs:
continue
sperdef = RelationDefinitionSchema(specialization, rschema,
- object, props)
+ object, None, values=props)
ss.execschemarql(session.execute, sperdef,
ss.rdef2rql(sperdef, cstrtypemap, groupmap))
@@ -437,11 +422,11 @@
def precommit_event(self):
session = self.session
entity = self.entity
- # entity.defaultval is a string or None, but we need a correctly typed
+ # entity.defaultval is a Binary or None, but we need a correctly typed
# value
default = entity.defaultval
if default is not None:
- default = TYPE_CONVERTER[entity.otype.name](default)
+ default = default.unzpickle()
props = {'default': default,
'indexed': entity.indexed,
'fulltextindexed': entity.fulltextindexed,
@@ -493,20 +478,11 @@
# attribute is still set to False, so we've to ensure it's False
rschema.final = True
insert_rdef_on_subclasses(session, eschema, rschema, rdefdef, props)
- # set default value, using sql for performance and to avoid
- # modification_date update
- if default:
- if rdefdef.object in ('Date', 'Datetime', 'TZDatetime'):
- # XXX may may want to use creation_date
- if default == 'TODAY':
- default = syssource.dbhelper.sql_current_date()
- elif default == 'NOW':
- default = syssource.dbhelper.sql_current_timestamp()
- session.system_sql('UPDATE %s SET %s=%s'
- % (table, column, default))
- else:
- session.system_sql('UPDATE %s SET %s=%%(default)s' % (table, column),
- {'default': default})
+ # update existing entities with the default value of newly added attribute
+ if default is not None:
+ default = convert_default_value(self.rdefdef, default)
+ session.system_sql('UPDATE %s SET %s=%%(default)s' % (table, column),
+ {'default': default})
def revertprecommit_event(self):
# revert changes on in memory schema
@@ -738,44 +714,38 @@
class CWUniqueTogetherConstraintAddOp(MemSchemaOperation):
entity = None # make pylint happy
+
def precommit_event(self):
session = self.session
prefix = SQL_PREFIX
- table = '%s%s' % (prefix, self.entity.constraint_of[0].name)
- cols = ['%s%s' % (prefix, r.name) for r in self.entity.relations]
- dbhelper= session.cnxset.source('system').dbhelper
- sqls = dbhelper.sqls_create_multicol_unique_index(table, cols)
+ entity = self.entity
+ table = '%s%s' % (prefix, entity.constraint_of[0].name)
+ cols = ['%s%s' % (prefix, r.name) for r in entity.relations]
+ dbhelper = session.cnxset.source('system').dbhelper
+ sqls = dbhelper.sqls_create_multicol_unique_index(table, cols, entity.name)
for sql in sqls:
session.system_sql(sql)
- # XXX revertprecommit_event
-
def postcommit_event(self):
- eschema = self.session.vreg.schema.schema_by_eid(self.entity.constraint_of[0].eid)
- attrs = [r.name for r in self.entity.relations]
+ entity = self.entity
+ eschema = self.session.vreg.schema.schema_by_eid(entity.constraint_of[0].eid)
+ attrs = [r.name for r in entity.relations]
eschema._unique_together.append(attrs)
class CWUniqueTogetherConstraintDelOp(MemSchemaOperation):
- entity = oldcstr = None # for pylint
- cols = [] # for pylint
+ entity = cstrname = None # for pylint
+ cols = () # for pylint
+
def precommit_event(self):
session = self.session
prefix = SQL_PREFIX
table = '%s%s' % (prefix, self.entity.type)
- dbhelper= session.cnxset.source('system').dbhelper
+ dbhelper = session.cnxset.source('system').dbhelper
cols = ['%s%s' % (prefix, c) for c in self.cols]
- sqls = dbhelper.sqls_drop_multicol_unique_index(table, cols)
+ sqls = dbhelper.sqls_drop_multicol_unique_index(table, cols, self.cstrname)
for sql in sqls:
- try:
- session.system_sql(sql)
- except Exception as exc: # should be ProgrammingError
- if sql.startswith('DROP'):
- self.error('execute of `%s` failed (cause: %s)', sql, exc)
- continue
- raise
-
- # XXX revertprecommit_event
+ session.system_sql(sql)
def postcommit_event(self):
eschema = self.session.vreg.schema.schema_by_eid(self.entity.eid)
@@ -1195,9 +1165,9 @@
schema = self._cw.vreg.schema
cstr = self._cw.entity_from_eid(self.eidfrom)
entity = schema.schema_by_eid(self.eidto)
- cols = [r.name for r in cstr.relations]
+ cols = tuple(r.name for r in cstr.relations)
CWUniqueTogetherConstraintDelOp(self._cw, entity=entity,
- oldcstr=cstr, cols=cols)
+ cstrname=cstr.name, cols=cols)
# permissions synchronization hooks ############################################
diff -r 2790fb8f7e03 -r e83cbc116352 hooks/test/data/schema.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hooks/test/data/schema.py Fri Jan 10 17:12:20 2014 +0100
@@ -0,0 +1,25 @@
+# copyright 2003-2013 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 .
+
+from yams.buildobjs import RelationDefinition
+
+class friend(RelationDefinition):
+ subject = ('CWUser', 'CWGroup')
+ object = ('CWUser', 'CWGroup')
+ symmetric = True
+
diff -r 2790fb8f7e03 -r e83cbc116352 hooks/test/unittest_hooks.py
--- a/hooks/test/unittest_hooks.py Fri Jun 21 14:22:39 2013 +0200
+++ b/hooks/test/unittest_hooks.py Fri Jan 10 17:12:20 2014 +0100
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -39,6 +39,37 @@
rset = self.execute('Any S WHERE X sender S, X eid %s' % eeid)
self.assertEqual(len(rset), 1)
+ def test_symmetric(self):
+ req = self.request()
+ u1 = self.create_user(req, u'1')
+ u2 = self.create_user(req, u'2')
+ u3 = self.create_user(req, u'3')
+ ga = req.create_entity('CWGroup', name=u'A')
+ gb = req.create_entity('CWGroup', name=u'B')
+ u1.cw_set(friend=u2)
+ u2.cw_set(friend=u3)
+ ga.cw_set(friend=gb)
+ ga.cw_set(friend=u1)
+ self.commit()
+ req = self.request()
+ for l1, l2 in ((u'1', u'2'),
+ (u'2', u'3')):
+ self.assertTrue(req.execute('Any U1,U2 WHERE U1 friend U2, U1 login %(l1)s, U2 login %(l2)s',
+ {'l1': l1, 'l2': l2}))
+ self.assertTrue(req.execute('Any U1,U2 WHERE U2 friend U1, U1 login %(l1)s, U2 login %(l2)s',
+ {'l1': l1, 'l2': l2}))
+ self.assertTrue(req.execute('Any GA,GB WHERE GA friend GB, GA name "A", GB name "B"'))
+ self.assertTrue(req.execute('Any GA,GB WHERE GB friend GA, GA name "A", GB name "B"'))
+ self.assertTrue(req.execute('Any GA,U1 WHERE GA friend U1, GA name "A", U1 login "1"'))
+ self.assertTrue(req.execute('Any GA,U1 WHERE U1 friend GA, GA name "A", U1 login "1"'))
+ self.assertFalse(req.execute('Any GA,U WHERE GA friend U, GA name "A", U login "2"'))
+ for l1, l2 in ((u'1', u'3'),
+ (u'3', u'1')):
+ self.assertFalse(req.execute('Any U1,U2 WHERE U1 friend U2, U1 login %(l1)s, U2 login %(l2)s',
+ {'l1': l1, 'l2': l2}))
+ self.assertFalse(req.execute('Any U1,U2 WHERE U2 friend U1, U1 login %(l1)s, U2 login %(l2)s',
+ {'l1': l1, 'l2': l2}))
+
def test_html_tidy_hook(self):
req = self.request()
entity = req.create_entity('Workflow', name=u'wf1',
diff -r 2790fb8f7e03 -r e83cbc116352 hooks/test/unittest_syncschema.py
--- a/hooks/test/unittest_syncschema.py Fri Jun 21 14:22:39 2013 +0200
+++ b/hooks/test/unittest_syncschema.py Fri Jan 10 17:12:20 2014 +0100
@@ -19,7 +19,7 @@
from logilab.common.testlib import TestCase, unittest_main
-from cubicweb import ValidationError
+from cubicweb import ValidationError, Binary
from cubicweb.schema import META_RTYPES
from cubicweb.devtools.testlib import CubicWebTC
from cubicweb.server.sqlutils import SQL_PREFIX
@@ -74,9 +74,10 @@
self.commit()
self.assertTrue(schema.has_entity('Societe2'))
self.assertTrue(schema.has_relation('concerne2'))
- attreid = self.execute('INSERT CWAttribute X: X cardinality "11", X defaultval "noname", '
+ attreid = self.execute('INSERT CWAttribute X: X cardinality "11", X defaultval %(default)s, '
' X indexed TRUE, X relation_type RT, X from_entity E, X to_entity F '
- 'WHERE RT name "name", E name "Societe2", F name "String"')[0][0]
+ 'WHERE RT name "name", E name "Societe2", F name "String"',
+ {'default': Binary.zpickle('noname')})[0][0]
self._set_attr_perms(attreid)
concerne2_rdef_eid = self.execute(
'INSERT CWRelation X: X cardinality "**", X relation_type RT, X from_entity E, X to_entity E '
@@ -290,8 +291,10 @@
def test_add_attribute_to_base_class(self):
- attreid = self.execute('INSERT CWAttribute X: X cardinality "11", X defaultval "noname", X indexed TRUE, X relation_type RT, X from_entity E, X to_entity F '
- 'WHERE RT name "messageid", E name "BaseTransition", F name "String"')[0][0]
+ attreid = self.execute('INSERT CWAttribute X: X cardinality "11", X defaultval %(default)s, '
+ 'X indexed TRUE, X relation_type RT, X from_entity E, X to_entity F '
+ 'WHERE RT name "messageid", E name "BaseTransition", F name "String"',
+ {'default': Binary.zpickle('noname')})[0][0]
assert self.execute('SET X read_permission Y WHERE X eid %(x)s, Y name "managers"',
{'x': attreid})
self.commit()
diff -r 2790fb8f7e03 -r e83cbc116352 interfaces.py
--- a/interfaces.py Fri Jun 21 14:22:39 2013 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,214 +0,0 @@
-# copyright 2003-2010 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 .
-"""Standard interfaces. Deprecated in favor of adapters.
-
-.. note::
-
- The `implements` selector used to match not only entity classes but also their
- interfaces. This will disappear in a future version. You should define an
- adapter for that interface and use `adaptable('MyIFace')` selector on appobjects
- that require that interface.
-
-"""
-__docformat__ = "restructuredtext en"
-
-from logilab.common.interface import Interface
-
-
-# XXX deprecates in favor of IProgressAdapter
-class IProgress(Interface):
- """something that has a cost, a state and a progression"""
-
- @property
- def cost(self):
- """the total cost"""
-
- @property
- def done(self):
- """what is already done"""
-
- @property
- def todo(self):
- """what remains to be done"""
-
- def progress_info(self):
- """returns a dictionary describing progress/estimated cost of the
- version.
-
- - mandatory keys are (''estimated', 'done', 'todo')
-
- - optional keys are ('notestimated', 'notestimatedcorrected',
- 'estimatedcorrected')
-
- 'noestimated' and 'notestimatedcorrected' should default to 0
- 'estimatedcorrected' should default to 'estimated'
- """
-
- def finished(self):
- """returns True if status is finished"""
-
- def in_progress(self):
- """returns True if status is not finished"""
-
- def progress(self):
- """returns the % progress of the task item"""
-
-# XXX deprecates in favor of IMileStoneAdapter
-class IMileStone(IProgress):
- """represents an ITask's item"""
-
- parent_type = None # specify main task's type
-
- def get_main_task(self):
- """returns the main ITask entity"""
-
- def initial_prevision_date(self):
- """returns the initial expected end of the milestone"""
-
- def eta_date(self):
- """returns expected date of completion based on what remains
- to be done
- """
-
- def completion_date(self):
- """returns date on which the subtask has been completed"""
-
- def contractors(self):
- """returns the list of persons supposed to work on this task"""
-
-# XXX deprecates in favor of IEmbedableAdapter
-class IEmbedable(Interface):
- """interface for embedable entities"""
-
- def embeded_url(self):
- """embed action interface"""
-
-# XXX deprecates in favor of ICalendarViewsAdapter
-class ICalendarViews(Interface):
- """calendar views interface"""
- def matching_dates(self, begin, end):
- """
- :param begin: day considered as begin of the range (`DateTime`)
- :param end: day considered as end of the range (`DateTime`)
-
- :return:
- a list of dates (`DateTime`) in the range [`begin`, `end`] on which
- this entity apply
- """
-
-# XXX deprecates in favor of ICalendarableAdapter
-class ICalendarable(Interface):
- """interface for items that do have a begin date 'start' and an end date 'stop'
- """
-
- @property
- def start(self):
- """return start date"""
-
- @property
- def stop(self):
- """return stop state"""
-
-# XXX deprecates in favor of ICalendarableAdapter
-class ITimetableViews(Interface):
- """timetable views interface"""
- def timetable_date(self):
- """XXX explain
-
- :return: date (`DateTime`)
- """
-
-# XXX deprecates in favor of IGeocodableAdapter
-class IGeocodable(Interface):
- """interface required by geocoding views such as gmap-view"""
-
- @property
- def latitude(self):
- """returns the latitude of the entity"""
-
- @property
- def longitude(self):
- """returns the longitude of the entity"""
-
- def marker_icon(self):
- """returns the icon that should be used as the marker"""
-
-
-# XXX deprecates in favor of IEmailableAdapter
-class IFeed(Interface):
- """interface for entities with rss flux"""
-
- def rss_feed_url(self):
- """"""
-
-# XXX deprecates in favor of IDownloadableAdapter
-class IDownloadable(Interface):
- """interface for downloadable entities"""
-
- def download_url(self): # XXX not really part of this interface
- """return an url to download entity's content"""
- def download_content_type(self):
- """return MIME type of the downloadable content"""
- def download_encoding(self):
- """return encoding of the downloadable content"""
- def download_file_name(self):
- """return file name of the downloadable content"""
- def download_data(self):
- """return actual data of the downloadable content"""
-
-# XXX deprecates in favor of IPrevNextAdapter
-class IPrevNext(Interface):
- """interface for entities which can be linked to a previous and/or next
- entity
- """
-
- def next_entity(self):
- """return the 'next' entity"""
- def previous_entity(self):
- """return the 'previous' entity"""
-
-# XXX deprecates in favor of IBreadCrumbsAdapter
-class IBreadCrumbs(Interface):
-
- def breadcrumbs(self, view, recurs=False):
- pass
-
-# XXX deprecates in favor of ITreeAdapter
-class ITree(Interface):
-
- def parent(self):
- """returns the parent entity"""
-
- def children(self):
- """returns the item's children"""
-
- def children_rql(self):
- """XXX returns RQL to get children"""
-
- def iterchildren(self):
- """iterates over the item's children"""
-
- def is_leaf(self):
- """returns true if this node as no child"""
-
- def is_root(self):
- """returns true if this node has no parent"""
-
- def root(self):
- """returns the root object"""
-
diff -r 2790fb8f7e03 -r e83cbc116352 mail.py
--- a/mail.py Fri Jun 21 14:22:39 2013 +0200
+++ b/mail.py Fri Jan 10 17:12:20 2014 +0100
@@ -90,7 +90,7 @@
email = u''
if uinfo.get('name'):
name = uinfo['name']
- elif config and config['sender-addr']:
+ elif config and config['sender-name']:
name = unicode(config['sender-name'])
else:
name = u''
diff -r 2790fb8f7e03 -r e83cbc116352 migration.py
--- a/migration.py Fri Jun 21 14:22:39 2013 +0200
+++ b/migration.py Fri Jan 10 17:12:20 2014 +0100
@@ -257,7 +257,7 @@
home_key = 'HOME'
if sys.platform == 'win32':
home_key = 'USERPROFILE'
- histfile = os.path.join(os.environ[home_key], ".eshellhist")
+ histfile = os.path.join(os.environ[home_key], ".cwshell_history")
try:
readline.read_history_file(histfile)
except IOError:
diff -r 2790fb8f7e03 -r e83cbc116352 misc/migration/3.18.0_Any.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/migration/3.18.0_Any.py Fri Jan 10 17:12:20 2014 +0100
@@ -0,0 +1,129 @@
+driver = config.sources()['system']['db-driver']
+if not (driver == 'postgres' or driver.startswith('sqlserver')):
+ import sys
+ print >>sys.stderr, 'This migration is not supported for backends other than sqlserver or postgres (yet).'
+ sys.exit(1)
+
+sync_schema_props_perms('defaultval')
+
+def convert_defaultval(cwattr, default):
+ from decimal import Decimal
+ import yams
+ from cubicweb import Binary
+ if default is None:
+ return
+ atype = cwattr.to_entity[0].name
+ if atype == 'Boolean':
+ # boolean attributes with default=False were stored as ''
+ assert default in ('True', 'False', ''), repr(default)
+ default = default == 'True'
+ elif atype in ('Int', 'BigInt'):
+ default = int(default)
+ elif atype == 'Float':
+ default = float(default)
+ elif atype == 'Decimal':
+ default = Decimal(default)
+ elif atype in ('Date', 'Datetime', 'TZDatetime', 'Time'):
+ try:
+ # handle NOW and TODAY, keep them stored as strings
+ yams.KEYWORD_MAP[atype][default.upper()]
+ default = default.upper()
+ except KeyError:
+ # otherwise get an actual date or datetime
+ default = yams.DATE_FACTORY_MAP[atype](default)
+ else:
+ assert atype == 'String', atype
+ default = unicode(default)
+ return Binary.zpickle(default)
+
+dbh = repo.system_source.dbhelper
+
+
+sql('ALTER TABLE cw_cwattribute ADD new_defaultval %s' % dbh.TYPE_MAPPING['Bytes'])
+
+for cwattr in rql('CWAttribute X').entities():
+ olddefault = cwattr.defaultval
+ if olddefault is not None:
+ req = "UPDATE cw_cwattribute SET new_defaultval = %(val)s WHERE cw_eid = %(eid)s"
+ args = {'val': dbh.binary_value(convert_defaultval(cwattr, olddefault).getvalue()), 'eid': cwattr.eid}
+ sql(req, args, ask_confirm=False)
+
+sql('ALTER TABLE cw_cwattribute DROP COLUMN cw_defaultval')
+if driver == 'postgres':
+ sql('ALTER TABLE cw_cwattribute RENAME COLUMN new_defaultval TO cw_defaultval')
+else: # sqlserver
+ sql("sp_rename 'cw_cwattribute.new_defaultval', 'cw_defaultval', 'COLUMN'")
+
+
+# Set object type to "Bytes" for CWAttribute's "defaultval" attribute
+rql('SET X to_entity B WHERE X is CWAttribute, X from_entity Y, Y name "CWAttribute", '
+ 'X relation_type Z, Z name "defaultval", B name "Bytes"')
+
+from yams import buildobjs as ybo
+schema.add_relation_def(ybo.RelationDefinition('CWAttribute', 'defaultval', 'Bytes'))
+schema.del_relation_def('CWAttribute', 'defaultval', 'String')
+
+commit()
+
+for rschema in schema.relations():
+ if rschema.symmetric:
+ subjects = set(repr(e.type) for e in rschema.subjects())
+ objects = set(repr(e.type) for e in rschema.objects())
+ assert subjects == objects
+ martians = set(str(eid) for eid, in sql('SELECT eid_to FROM %s_relation, entities WHERE eid_to = eid AND type NOT IN (%s)' %
+ (rschema.type, ','.join(subjects))))
+ martians |= set(str(eid) for eid, in sql('SELECT eid_from FROM %s_relation, entities WHERE eid_from = eid AND type NOT IN (%s)' %
+ (rschema.type, ','.join(subjects))))
+ if martians:
+ martians = ','.join(martians)
+ print 'deleting broken relations %s for eids %s' % (rschema.type, martians)
+ sql('DELETE FROM %s_relation WHERE eid_from IN (%s) OR eid_to IN (%s)' % (rschema.type, martians, martians))
+ with session.deny_all_hooks_but():
+ rql('SET X %(r)s Y WHERE Y %(r)s X, NOT X %(r)s Y' % {'r': rschema.type})
+ commit()
+
+
+# multi columns unique constraints regeneration
+from cubicweb.server import schemaserial
+
+# syncschema hooks would try to remove indices but
+# 1) we already do that below
+# 2) the hook expects the CWUniqueTogetherConstraint.name attribute that hasn't
+# yet been added
+with session.allow_all_hooks_but('syncschema'):
+ rql('DELETE CWUniqueTogetherConstraint C')
+commit()
+
+add_attribute('CWUniqueTogetherConstraint', 'name')
+
+# low-level wipe code for postgres & sqlserver, plain sql ...
+if driver == 'postgres':
+ for indexname, in sql('select indexname from pg_indexes'):
+ if indexname.startswith('unique_'):
+ print 'dropping index', indexname
+ sql('DROP INDEX %s' % indexname)
+ commit()
+elif driver.startswith('sqlserver'):
+ for viewname, in sql('select name from sys.views'):
+ if viewname.startswith('utv_'):
+ print 'dropping view (index should be cascade-deleted)', viewname
+ sql('DROP VIEW %s' % viewname)
+ commit()
+
+# recreate the constraints, hook will lead to low-level recreation
+for eschema in sorted(schema.entities()):
+ if eschema._unique_together:
+ rql_args = schemaserial.uniquetogether2rqls(eschema)
+ for rql, args in rql_args:
+ args['x'] = eschema.eid
+ session.execute(rql, args)
+ commit()
+
+
+add_relation_definition('CWAttribute', 'add_permission', 'CWGroup')
+add_relation_definition('CWAttribute', 'add_permission', 'RQLExpression')
+
+# all attributes perms have to be refreshed ...
+for rschema in schema.relations():
+ if relation.final:
+ sync_schema_props_perms(rschema.type, syncprops=False)
diff -r 2790fb8f7e03 -r e83cbc116352 mixins.py
--- a/mixins.py Fri Jun 21 14:22:39 2013 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,308 +0,0 @@
-# copyright 2003-2012 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 .
-"""mixins of entity/views organized somewhat in a graph or tree structure"""
-__docformat__ = "restructuredtext en"
-
-from itertools import chain
-
-from logilab.common.decorators import cached
-from logilab.common.deprecation import deprecated, class_deprecated
-
-from cubicweb.predicates import implements
-from cubicweb.interfaces import ITree
-
-
-class TreeMixIn(object):
- """base tree-mixin implementing the tree interface
-
- This mixin has to be inherited explicitly and configured using the
- tree_attribute, parent_target and children_target class attribute to
- benefit from this default implementation
- """
- __metaclass__ = class_deprecated
- __deprecation_warning__ = '[3.9] TreeMixIn is deprecated, use/override ITreeAdapter instead (%(cls)s)'
-
- tree_attribute = None
- # XXX misnamed
- parent_target = 'subject'
- children_target = 'object'
-
- def different_type_children(self, entities=True):
- """return children entities of different type as this entity.
-
- according to the `entities` parameter, return entity objects or the
- equivalent result set
- """
- res = self.related(self.tree_attribute, self.children_target,
- entities=entities)
- if entities:
- return [e for e in res if e.e_schema != self.e_schema]
- return res.filtered_rset(lambda x: x.e_schema != self.e_schema, self.cw_col)
-
- def same_type_children(self, entities=True):
- """return children entities of the same type as this entity.
-
- according to the `entities` parameter, return entity objects or the
- equivalent result set
- """
- res = self.related(self.tree_attribute, self.children_target,
- entities=entities)
- if entities:
- return [e for e in res if e.e_schema == self.e_schema]
- return res.filtered_rset(lambda x: x.e_schema is self.e_schema, self.cw_col)
-
- def iterchildren(self, _done=None):
- if _done is None:
- _done = set()
- for child in self.children():
- if child.eid in _done:
- self.error('loop in %s tree: %s', self.__regid__.lower(), child)
- continue
- yield child
- _done.add(child.eid)
-
- def prefixiter(self, _done=None):
- if _done is None:
- _done = set()
- if self.eid in _done:
- return
- _done.add(self.eid)
- yield self
- for child in self.same_type_children():
- for entity in child.prefixiter(_done):
- yield entity
-
- @cached
- def path(self):
- """returns the list of eids from the root object to this object"""
- path = []
- parent = self
- while parent:
- if parent.eid in path:
- self.error('loop in %s tree: %s', self.__regid__.lower(), parent)
- break
- path.append(parent.eid)
- try:
- # check we are not leaving the tree
- if (parent.tree_attribute != self.tree_attribute or
- parent.parent_target != self.parent_target):
- break
- parent = parent.parent()
- except AttributeError:
- break
-
- path.reverse()
- return path
-
- def iterparents(self, strict=True):
- def _uptoroot(self):
- curr = self
- while True:
- curr = curr.parent()
- if curr is None:
- break
- yield curr
- if not strict:
- return chain([self], _uptoroot(self))
- return _uptoroot(self)
-
- ## ITree interface ########################################################
- def parent(self):
- """return the parent entity if any, else None (e.g. if we are on the
- root
- """
- try:
- return self.related(self.tree_attribute, self.parent_target,
- entities=True)[0]
- except (KeyError, IndexError):
- return None
-
- def children(self, entities=True, sametype=False):
- """return children entities
-
- according to the `entities` parameter, return entity objects or the
- equivalent result set
- """
- if sametype:
- return self.same_type_children(entities)
- else:
- return self.related(self.tree_attribute, self.children_target,
- entities=entities)
-
- def children_rql(self):
- return self.cw_related_rql(self.tree_attribute, self.children_target)
-
- def is_leaf(self):
- return len(self.children()) == 0
-
- def is_root(self):
- return self.parent() is None
-
- def root(self):
- """return the root object"""
- return self._cw.entity_from_eid(self.path()[0])
-
-
-class EmailableMixIn(object):
- """base mixin providing the default get_email() method used by
- the massmailing view
-
- NOTE: The default implementation is based on the
- primary_email / use_email scheme
- """
- @deprecated("[3.9] use entity.cw_adapt_to('IEmailable').get_email()")
- def get_email(self):
- if getattr(self, 'primary_email', None):
- return self.primary_email[0].address
- if getattr(self, 'use_email', None):
- return self.use_email[0].address
- return None
-
-
-"""pluggable mixins system: plug classes registered in MI_REL_TRIGGERS on entity
-classes which have the relation described by the dict's key.
-
-NOTE: pluggable mixins can't override any method of the 'explicit' user classes tree
-(eg without plugged classes). This includes bases Entity and AnyEntity classes.
-"""
-MI_REL_TRIGGERS = {
- ('primary_email', 'subject'): EmailableMixIn,
- ('use_email', 'subject'): EmailableMixIn,
- }
-
-
-# XXX move to cubicweb.web.views.treeview once we delete usage from this file
-def _done_init(done, view, row, col):
- """handle an infinite recursion safety belt"""
- if done is None:
- done = set()
- entity = view.cw_rset.get_entity(row, col)
- if entity.eid in done:
- msg = entity._cw._('loop in %(rel)s relation (%(eid)s)') % {
- 'rel': entity.cw_adapt_to('ITree').tree_relation,
- 'eid': entity.eid
- }
- return None, msg
- done.add(entity.eid)
- return done, entity
-
-
-class TreeViewMixIn(object):
- """a recursive tree view"""
- __metaclass__ = class_deprecated
- __deprecation_warning__ = '[3.9] TreeViewMixIn is deprecated, use/override BaseTreeView instead (%(cls)s)'
-
- __regid__ = 'tree'
- __select__ = implements(ITree, warn=False)
- item_vid = 'treeitem'
-
- def call(self, done=None, **kwargs):
- if done is None:
- done = set()
- super(TreeViewMixIn, self).call(done=done, **kwargs)
-
- def cell_call(self, row, col=0, vid=None, done=None, maxlevel=None, **kwargs):
- assert maxlevel is None or maxlevel > 0
- done, entity = _done_init(done, self, row, col)
- if done is None:
- # entity is actually an error message
- self.w(u'%s ' % entity)
- return
- self.open_item(entity)
- entity.view(vid or self.item_vid, w=self.w, **kwargs)
- if maxlevel is not None:
- maxlevel -= 1
- if maxlevel == 0:
- self.close_item(entity)
- return
- relatedrset = entity.children(entities=False)
- self.wview(self.__regid__, relatedrset, 'null', done=done,
- maxlevel=maxlevel, **kwargs)
- self.close_item(entity)
-
- def open_item(self, entity):
- self.w(u'\n' % entity.cw_etype.lower())
- def close_item(self, entity):
- self.w(u' \n')
-
-
-class TreePathMixIn(object):
- """a recursive path view"""
- __metaclass__ = class_deprecated
- __deprecation_warning__ = '[3.9] TreePathMixIn is deprecated, use/override TreePathView instead (%(cls)s)'
- __regid__ = 'path'
- item_vid = 'oneline'
- separator = u' > '
-
- def call(self, **kwargs):
- self.w(u'')
- super(TreePathMixIn, self).call(**kwargs)
- self.w(u'
')
-
- def cell_call(self, row, col=0, vid=None, done=None, **kwargs):
- done, entity = _done_init(done, self, row, col)
- if done is None:
- # entity is actually an error message
- self.w(u'%s ' % entity)
- return
- parent = entity.parent()
- if parent:
- parent.view(self.__regid__, w=self.w, done=done)
- self.w(self.separator)
- entity.view(vid or self.item_vid, w=self.w)
-
-
-class ProgressMixIn(object):
- """provide a default implementations for IProgress interface methods"""
- __metaclass__ = class_deprecated
- __deprecation_warning__ = '[3.9] ProgressMixIn is deprecated, use/override IProgressAdapter instead (%(cls)s)'
-
- @property
- def cost(self):
- return self.progress_info()['estimated']
-
- @property
- def revised_cost(self):
- return self.progress_info().get('estimatedcorrected', self.cost)
-
- @property
- def done(self):
- return self.progress_info()['done']
-
- @property
- def todo(self):
- return self.progress_info()['todo']
-
- @cached
- def progress_info(self):
- raise NotImplementedError()
-
- def finished(self):
- return not self.in_progress()
-
- def in_progress(self):
- raise NotImplementedError()
-
- def progress(self):
- try:
- return 100. * self.done / self.revised_cost
- except ZeroDivisionError:
- # total cost is 0 : if everything was estimated, task is completed
- if self.progress_info().get('notestimated'):
- return 0.
- return 100
diff -r 2790fb8f7e03 -r e83cbc116352 predicates.py
--- a/predicates.py Fri Jun 21 14:22:39 2013 +0200
+++ b/predicates.py Fri Jan 10 17:12:20 2014 +0100
@@ -204,27 +204,6 @@
# remember, these imports are there for bw compat only
__BACKWARD_COMPAT_IMPORTS = (yes,)
-def score_interface(etypesreg, eclass, iface):
- """Return XXX if the give object (maybe an instance or class) implements
- the interface.
- """
- if getattr(iface, '__registry__', None) == 'etypes':
- # adjust score if the interface is an entity class
- parents, any = etypesreg.parent_classes(eclass.__regid__)
- if iface is eclass:
- return len(parents) + 4
- if iface is any: # Any
- return 1
- for index, basecls in enumerate(reversed(parents)):
- if iface is basecls:
- return index + 3
- return 0
- # XXX iface in implements deprecated in 3.9
- if implements_iface(eclass, iface):
- # implementing an interface takes precedence other special Any interface
- return 2
- return 0
-
# abstract predicates / mixin helpers ###########################################
@@ -745,53 +724,6 @@
return 1 # necessarily true if we're there
-class implements(EClassPredicate):
- """Return non-zero score for entity that are of the given type(s) or
- implements at least one of the given interface(s). If multiple arguments are
- given, matching one of them is enough.
-
- Entity types should be given as string, the corresponding class will be
- fetched from the entity types registry at selection time.
-
- See :class:`~cubicweb.predicates.EClassPredicate` documentation for entity
- class lookup / score rules according to the input context.
-
- .. note::
-
- when interface is an entity class, the score will reflect class
- proximity so the most specific object will be selected.
-
- .. note::
-
- deprecated in cubicweb >= 3.9, use either
- :class:`~cubicweb.predicates.is_instance` or
- :class:`~cubicweb.predicates.adaptable`.
- """
-
- def __init__(self, *expected_ifaces, **kwargs):
- emit_warn = kwargs.pop('warn', True)
- super(implements, self).__init__(**kwargs)
- self.expected_ifaces = expected_ifaces
- if emit_warn:
- warn('[3.9] implements predicate is deprecated, use either '
- 'is_instance or adaptable', DeprecationWarning, stacklevel=2)
-
- def __str__(self):
- return '%s(%s)' % (self.__class__.__name__,
- ','.join(str(s) for s in self.expected_ifaces))
-
- def score_class(self, eclass, req):
- score = 0
- etypesreg = req.vreg['etypes']
- for iface in self.expected_ifaces:
- if isinstance(iface, basestring):
- # entity type
- try:
- iface = etypesreg.etype_class(iface)
- except KeyError:
- continue # entity type not in the schema
- score += score_interface(etypesreg, eclass, iface)
- return score
def _reset_is_instance_cache(vreg):
vreg._is_instance_predicate_cache = {}
@@ -1282,12 +1214,9 @@
','.join(str(s) for s in self.expected))
-def on_fire_transition(etype, tr_name, from_state_name=None):
+def on_fire_transition(etype, tr_names, from_state_name=None):
"""Return 1 when entity of the type `etype` is going through transition of
- the name `tr_name`.
-
- If `from_state_name` is specified, this predicate will also check the
- incoming state.
+ a name included in `tr_names`.
You should use this predicate on 'after_add_entity' hook, since it's actually
looking for addition of `TrInfo` entities. Hence in the hook, `self.entity`
@@ -1297,9 +1226,13 @@
See :class:`cubicweb.entities.wfobjs.TrInfo` for more information.
"""
+ if from_state_name is not None:
+ warn("on_fire_transition's from_state_name argument is unused", DeprecationWarning)
+ if isinstance(tr_names, basestring):
+ tr_names = set((tr_names,))
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
+ return (trinfo.transition and trinfo.transition.name in tr_names
# 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))
diff -r 2790fb8f7e03 -r e83cbc116352 req.py
--- a/req.py Fri Jun 21 14:22:39 2013 +0200
+++ b/req.py Fri Jan 10 17:12:20 2014 +0100
@@ -29,7 +29,10 @@
from logilab.common.deprecation import deprecated
from logilab.common.date import ustrftime, strptime, todate, todatetime
-from cubicweb import Unauthorized, NoSelectableObject, uilib
+from rql.utils import rqlvar_maker
+
+from cubicweb import (Unauthorized, NoSelectableObject, NoResultError,
+ MultipleResultsError, uilib)
from cubicweb.rset import ResultSet
ONESECOND = timedelta(0, 1, 0)
@@ -152,16 +155,16 @@
cls = self.vreg['etypes'].etype_class(etype)
return cls.cw_instantiate(self.execute, **kwargs)
+ @deprecated('[3.18] use find(etype, **kwargs).entities()')
def find_entities(self, etype, **kwargs):
"""find entities of the given type and attribute values.
>>> users = find_entities('CWGroup', name=u'users')
>>> groups = find_entities('CWGroup')
"""
- parts = ['Any X WHERE X is %s' % etype]
- parts.extend('X %(attr)s %%(%(attr)s)s' % {'attr': attr} for attr in kwargs)
- return self.execute(', '.join(parts), kwargs).entities()
+ return self.find(etype, **kwargs).entities()
+ @deprecated('[3.18] use find(etype, **kwargs).one()')
def find_one_entity(self, etype, **kwargs):
"""find one entity of the given type and attribute values.
raise :exc:`FindEntityError` if can not return one and only one entity.
@@ -170,14 +173,43 @@
>>> groups = find_one_entity('CWGroup')
Exception()
"""
+ try:
+ return self.find(etype, **kwargs).one()
+ except (NoResultError, MultipleResultsError) as e:
+ raise FindEntityError("%s: (%s, %s)" % (str(e), etype, kwargs))
+
+ def find(self, etype, **kwargs):
+ """find entities of the given type and attribute values.
+
+ :returns: A :class:`ResultSet`
+
+ >>> users = find('CWGroup', name=u"users").one()
+ >>> groups = find('CWGroup').entities()
+ """
parts = ['Any X WHERE X is %s' % etype]
- parts.extend('X %(attr)s %%(%(attr)s)s' % {'attr': attr} for attr in kwargs)
+ varmaker = rqlvar_maker(defined='X')
+ eschema = self.vreg.schema[etype]
+ for attr, value in kwargs.items():
+ if isinstance(value, list) or isinstance(value, tuple):
+ raise NotImplementedError("List of values are not supported")
+ if hasattr(value, 'eid'):
+ kwargs[attr] = value.eid
+ if attr.startswith('reverse_'):
+ attr = attr[8:]
+ assert attr in eschema.objrels, \
+ '%s not in %s object relations' % (attr, eschema)
+ parts.append(
+ '%(varname)s %(attr)s X, '
+ '%(varname)s eid %%(reverse_%(attr)s)s'
+ % {'attr': attr, 'varname': varmaker.next()})
+ else:
+ assert attr in eschema.subjrels, \
+ '%s not in %s subject relations' % (attr, eschema)
+ parts.append('X %(attr)s %%(%(attr)s)s' % {'attr': attr})
+
rql = ', '.join(parts)
- rset = self.execute(rql, kwargs)
- if len(rset) != 1:
- raise FindEntityError('Found %i entitie(s) when 1 was expected (rql=%s ; %s)'
- % (len(rset), rql, repr(kwargs)))
- return rset.get_entity(0,0)
+
+ return self.execute(rql, kwargs)
def ensure_ro_rql(self, rql):
"""raise an exception if the given rql is not a select query"""
@@ -424,7 +456,7 @@
"""return the root url of the instance
"""
if secure:
- return self.vreg.config.get('https-url', self.vreg.config['base-url'])
+ return self.vreg.config.get('https-url') or self.vreg.config['base-url']
return self.vreg.config['base-url']
# abstract methods to override according to the web front-end #############
diff -r 2790fb8f7e03 -r e83cbc116352 rqlrewrite.py
--- a/rqlrewrite.py Fri Jun 21 14:22:39 2013 +0200
+++ b/rqlrewrite.py Fri Jan 10 17:12:20 2014 +0100
@@ -92,6 +92,7 @@
for etype in possibletypes:
node.append(n.Constant(etype, 'etype'))
else:
+ etype = iter(possibletypes).next()
node = n.Constant(etype, 'etype')
comp = mytyperel.children[1]
comp.replace(comp.children[0], node)
diff -r 2790fb8f7e03 -r e83cbc116352 rset.py
--- a/rset.py Fri Jun 21 14:22:39 2013 +0200
+++ b/rset.py Fri Jan 10 17:12:20 2014 +0100
@@ -23,7 +23,7 @@
from rql import nodes, stmts
-from cubicweb import NotAnEntity
+from cubicweb import NotAnEntity, NoResultError, MultipleResultsError
class ResultSet(object):
@@ -437,6 +437,25 @@
raise NotAnEntity(etype)
return self._build_entity(row, col)
+ def one(self, col=0):
+ """Retrieve exactly one entity from the query.
+
+ If the result set is empty, raises :exc:`NoResultError`.
+ If the result set has more than one row, raises
+ :exc:`MultipleResultsError`.
+
+ :type col: int
+ :param col: The column localising the entity in the unique row
+
+ :return: the partially initialized `Entity` instance
+ """
+ if len(self) == 1:
+ return self.get_entity(0, col)
+ elif len(self) == 0:
+ raise NoResultError("No row was found for one()")
+ else:
+ raise MultipleResultsError("Multiple rows were found for one()")
+
def _build_entity(self, row, col):
"""internal method to get a single entity, returns a partially
initialized Entity instance.
diff -r 2790fb8f7e03 -r e83cbc116352 schema.py
--- a/schema.py Fri Jun 21 14:22:39 2013 +0200
+++ b/schema.py Fri Jan 10 17:12:20 2014 +0100
@@ -21,10 +21,11 @@
_ = unicode
import re
-from os.path import join
+from os.path import join, basename
from logging import getLogger
from warnings import warn
+from logilab.common import tempattr
from logilab.common.decorators import cached, clear_cache, monkeypatch, cachedproperty
from logilab.common.logging_ext import set_log_methods
from logilab.common.deprecation import deprecated, class_moved, moved
@@ -44,6 +45,15 @@
import cubicweb
from cubicweb import ETYPE_NAME_MAP, ValidationError, Unauthorized
+try:
+ from cubicweb import server
+except ImportError:
+ # We need to lookup DEBUG from there,
+ # however a pure dbapi client may not have it.
+ class server(object): pass
+ server.DEBUG = False
+
+
PURE_VIRTUAL_RTYPES = set(('identity', 'has_text',))
VIRTUAL_RTYPES = set(('eid', 'identity', 'has_text',))
@@ -94,6 +104,309 @@
ybo.ETYPE_PROPERTIES += ('eid',)
ybo.RTYPE_PROPERTIES += ('eid',)
+# Bases for manipulating RQL in schema #########################################
+
+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 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 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(','))
+
+
+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
+ # to be defined in concrete classes
+ rqlst = None
+ predefined_variables = None
+
+ def __init__(self, expression, mainvars, eid):
+ """
+ :type mainvars: sequence of RQL variables' names. Can be provided as a
+ comma separated string.
+ :param mainvars: names of the variables being selected.
+
+ """
+ self.eid = eid # eid of the entity representing this rql expression
+ 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.full_rql = self.rqlst.as_string()
+ except RQLSyntaxError:
+ raise RQLSyntaxError(expression)
+ for mainvar in mainvars:
+ # if variable is predefined, an extra reference is inserted
+ # automatically (`VAR eid %(v)s`)
+ if mainvar in self.predefined_variables:
+ min_refs = 3
+ else:
+ min_refs = 2
+ if len(self.rqlst.defined_vars[mainvar].references()) < min_refs:
+ _LOGGER.warn('You did not use the %s variable in your RQL '
+ 'expression %s', mainvar, self)
+ # syntax tree used by read security (inserted in queries when necessary)
+ self.snippet_rqlst = parse(self.minimal_rql, print_errors=False).children[0]
+ # graph of links between variables, used by rql rewriter
+ self.vargraph = vargraph(self.rqlst)
+ # useful for some instrumentation, e.g. localperms permcheck command
+ self.package = ybo.PACKAGE
+
+ def __str__(self):
+ return self.full_rql
+ def __repr__(self):
+ return '%s(%s)' % (self.__class__.__name__, self.full_rql)
+
+ def __lt__(self, other):
+ if hasattr(other, 'expression'):
+ return self.expression < other.expression
+ return True
+
+ def __eq__(self, other):
+ if hasattr(other, 'expression'):
+ return self.expression == other.expression
+ return False
+
+ def __hash__(self):
+ return hash(self.expression)
+
+ def __deepcopy__(self, memo):
+ return self.__class__(self.expression, self.mainvars)
+ def __getstate__(self):
+ return (self.expression, self.mainvars)
+ def __setstate__(self, state):
+ self.__init__(*state)
+
+ @cachedproperty
+ def rqlst(self):
+ select = parse(self.minimal_rql, print_errors=False).children[0]
+ defined = set(split_expression(self.expression))
+ for varname in self.predefined_variables:
+ if varname in defined:
+ select.add_eid_restriction(select.get_variable(varname), varname.lower(), 'Substitute')
+ return select
+
+ # permission rql expression specific stuff #################################
+
+ @cached
+ def transform_has_permission(self):
+ found = None
+ rqlst = self.rqlst
+ for var in rqlst.defined_vars.itervalues():
+ for varref in var.references():
+ rel = varref.relation()
+ if rel is None:
+ continue
+ try:
+ prefix, action, suffix = rel.r_type.split('_')
+ except ValueError:
+ continue
+ if prefix != 'has' or suffix != 'permission' or \
+ not action in ('add', 'delete', 'update', 'read'):
+ continue
+ if found is None:
+ found = []
+ rqlst.save_state()
+ assert rel.children[0].name == 'U'
+ objvar = rel.children[1].children[0].variable
+ rqlst.remove_node(rel)
+ selected = [v.name for v in rqlst.get_selected_variables()]
+ if objvar.name not in selected:
+ colindex = len(selected)
+ rqlst.add_selected(objvar)
+ else:
+ colindex = selected.index(objvar.name)
+ found.append((action, colindex))
+ # remove U eid %(u)s if U is not used in any other relation
+ uvrefs = rqlst.defined_vars['U'].references()
+ if len(uvrefs) == 1:
+ rqlst.remove_node(uvrefs[0].relation())
+ if found is not None:
+ rql = rqlst.as_string()
+ if len(rqlst.selection) == 1 and isinstance(rqlst.where, nodes.Relation):
+ # only "Any X WHERE X eid %(x)s" remaining, no need to execute the rql
+ keyarg = rqlst.selection[0].name.lower()
+ else:
+ keyarg = None
+ rqlst.recover()
+ return rql, found, keyarg
+ return rqlst.as_string(), None, None
+
+ def _check(self, _cw, **kwargs):
+ """return True if the rql expression is matching the given relation
+ between fromeid and toeid
+
+ _cw may be a request or a server side transaction
+ """
+ creating = kwargs.get('creating')
+ if not creating and self.eid is not None:
+ key = (self.eid, tuple(sorted(kwargs.iteritems())))
+ try:
+ return _cw.local_perm_cache[key]
+ except KeyError:
+ pass
+ rql, has_perm_defs, keyarg = self.transform_has_permission()
+ # when creating an entity, expression related to X satisfied
+ if creating and 'X' in self.rqlst.defined_vars:
+ return True
+ if keyarg is None:
+ kwargs.setdefault('u', _cw.user.eid)
+ try:
+ rset = _cw.execute(rql, kwargs, build_descr=True)
+ except NotImplementedError:
+ self.critical('cant check rql expression, unsupported rql %s', rql)
+ if self.eid is not None:
+ _cw.local_perm_cache[key] = False
+ return False
+ except TypeResolverException as ex:
+ # some expression may not be resolvable with current kwargs
+ # (type conflict)
+ self.warning('%s: %s', rql, str(ex))
+ if self.eid is not None:
+ _cw.local_perm_cache[key] = False
+ return False
+ except Unauthorized as ex:
+ self.debug('unauthorized %s: %s', rql, str(ex))
+ if self.eid is not None:
+ _cw.local_perm_cache[key] = False
+ return False
+ else:
+ rset = _cw.eid_rset(kwargs[keyarg])
+ # if no special has_*_permission relation in the rql expression, just
+ # check the result set contains something
+ if has_perm_defs is None:
+ if rset:
+ if self.eid is not None:
+ _cw.local_perm_cache[key] = True
+ return True
+ elif rset:
+ # check every special has_*_permission relation is satisfied
+ get_eschema = _cw.vreg.schema.eschema
+ try:
+ for eaction, col in has_perm_defs:
+ for i in xrange(len(rset)):
+ eschema = get_eschema(rset.description[i][col])
+ eschema.check_perm(_cw, eaction, eid=rset[i][col])
+ if self.eid is not None:
+ _cw.local_perm_cache[key] = True
+ return True
+ except Unauthorized:
+ pass
+ if self.eid is not None:
+ _cw.local_perm_cache[key] = False
+ return False
+
+ @property
+ def minimal_rql(self):
+ return 'Any %s WHERE %s' % (','.join(sorted(self.mainvars)),
+ self.expression)
+
+
+
+# rql expressions for use in permission definition #############################
+
+class ERQLExpression(RQLExpression):
+ predefined_variables = 'XU'
+
+ def __init__(self, expression, mainvars=None, eid=None):
+ RQLExpression.__init__(self, expression, mainvars or 'X', eid)
+
+ def check(self, _cw, eid=None, creating=False, **kwargs):
+ if 'X' in self.rqlst.defined_vars:
+ if eid is None:
+ if creating:
+ return self._check(_cw, creating=True, **kwargs)
+ return False
+ assert creating == False
+ return self._check(_cw, x=eid, **kwargs)
+ return self._check(_cw, **kwargs)
+
+
+def vargraph(rqlst):
+ """ builds an adjacency graph of variables from the rql syntax tree, e.g:
+ Any O,S WHERE T subworkflow_exit S, T subworkflow WF, O state_of WF
+ => {'WF': ['O', 'T'], 'S': ['T'], 'T': ['WF', 'S'], 'O': ['WF']}
+ """
+ vargraph = {}
+ for relation in rqlst.get_nodes(nodes.Relation):
+ try:
+ rhsvarname = relation.children[1].children[0].variable.name
+ lhsvarname = relation.children[0].name
+ except AttributeError:
+ pass
+ else:
+ vargraph.setdefault(lhsvarname, []).append(rhsvarname)
+ vargraph.setdefault(rhsvarname, []).append(lhsvarname)
+ #vargraph[(lhsvarname, rhsvarname)] = relation.r_type
+ return vargraph
+
+
+class GeneratedConstraint(object):
+ def __init__(self, rqlst, mainvars):
+ self.snippet_rqlst = rqlst
+ self.mainvars = mainvars
+ self.vargraph = vargraph(rqlst)
+
+
+class RRQLExpression(RQLExpression):
+ predefined_variables = 'SOU'
+
+ def __init__(self, expression, mainvars=None, eid=None):
+ if mainvars is None:
+ mainvars = guess_rrqlexpr_mainvars(expression)
+ RQLExpression.__init__(self, expression, mainvars, eid)
+
+ def check(self, _cw, fromeid=None, toeid=None):
+ kwargs = {}
+ if 'S' in self.rqlst.defined_vars:
+ if fromeid is None:
+ return False
+ kwargs['s'] = fromeid
+ if 'O' in self.rqlst.defined_vars:
+ if toeid is None:
+ return False
+ kwargs['o'] = toeid
+ return self._check(_cw, **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.
+# These default permissions won't be checked by the security hooks:
+# since they delegate checking to the entity, we can skip actual checks.
+ybo.DEFAULT_ATTRPERMS['update'] = ('managers', ERQLExpression('U has_update_permission X'))
+ybo.DEFAULT_ATTRPERMS['add'] = ('managers', ERQLExpression('U has_add_permission X'))
+
+
PUB_SYSTEM_ENTITY_PERMS = {
'read': ('managers', 'users', 'guests',),
'add': ('managers',),
@@ -107,6 +420,7 @@
}
PUB_SYSTEM_ATTR_PERMS = {
'read': ('managers', 'users', 'guests',),
+ 'add': ('managers',),
'update': ('managers',),
}
RO_REL_PERMS = {
@@ -116,6 +430,7 @@
}
RO_ATTR_PERMS = {
'read': ('managers', 'users', 'guests',),
+ 'add': ybo.DEFAULT_ATTRPERMS['add'],
'update': (),
}
@@ -268,13 +583,25 @@
return False
PermissionMixIn.has_perm = has_perm
+
def check_perm(self, _cw, action, **kwargs):
# NB: _cw may be a server transaction or a request object.
#
# check user is in an allowed group, if so that's enough internal
# transactions should always stop there
+ DBG = False
+ if server.DEBUG & server.DBG_SEC:
+ if action in server._SECURITY_CAPS:
+ _self_str = str(self)
+ if server._SECURITY_ITEMS:
+ if any(item in _self_str for item in server._SECURITY_ITEMS):
+ DBG = True
+ else:
+ DBG = True
groups = self.get_groups(action)
if _cw.user.matching_groups(groups):
+ if DBG:
+ print 'check_perm: %r %r: user matches %s' % (action, _self_str, groups)
return
# if 'owners' in allowed groups, check if the user actually owns this
# object, if so that's enough
@@ -284,8 +611,15 @@
if 'owners' in groups and (
kwargs.get('creating')
or ('eid' in kwargs and _cw.user.owns(kwargs['eid']))):
+ if DBG:
+ print ('check_perm: %r %r: user is owner or creation time' %
+ (action, _self_str))
return
# else if there is some rql expressions, check them
+ if DBG:
+ print ('check_perm: %r %r %s' %
+ (action, _self_str, [(rqlexpr, kwargs, rqlexpr.check(_cw, **kwargs))
+ for rqlexpr in self.get_rqlexprs(action)]))
if any(rqlexpr.check(_cw, **kwargs)
for rqlexpr in self.get_rqlexprs(action)):
return
@@ -630,301 +964,6 @@
def schema_by_eid(self, eid):
return self._eid_index[eid]
-# Bases for manipulating RQL in schema #########################################
-
-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 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 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(','))
-
-
-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
- # to be defined in concrete classes
- rqlst = None
- predefined_variables = None
-
- def __init__(self, expression, mainvars, eid):
- """
- :type mainvars: sequence of RQL variables' names. Can be provided as a
- comma separated string.
- :param mainvars: names of the variables being selected.
-
- """
- self.eid = eid # eid of the entity representing this rql expression
- 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.full_rql = self.rqlst.as_string()
- except RQLSyntaxError:
- raise RQLSyntaxError(expression)
- for mainvar in mainvars:
- # if variable is predefined, an extra reference is inserted
- # automatically (`VAR eid %(v)s`)
- if mainvar in self.predefined_variables:
- min_refs = 3
- else:
- min_refs = 2
- if len(self.rqlst.defined_vars[mainvar].references()) < min_refs:
- _LOGGER.warn('You did not use the %s variable in your RQL '
- 'expression %s', mainvar, self)
- # syntax tree used by read security (inserted in queries when necessary)
- self.snippet_rqlst = parse(self.minimal_rql, print_errors=False).children[0]
- # graph of links between variables, used by rql rewriter
- self.vargraph = vargraph(self.rqlst)
-
- def __str__(self):
- return self.full_rql
- def __repr__(self):
- return '%s(%s)' % (self.__class__.__name__, self.full_rql)
-
- def __lt__(self, other):
- if hasattr(other, 'expression'):
- return self.expression < other.expression
- return True
-
- def __eq__(self, other):
- if hasattr(other, 'expression'):
- return self.expression == other.expression
- return False
-
- def __hash__(self):
- return hash(self.expression)
-
- def __deepcopy__(self, memo):
- return self.__class__(self.expression, self.mainvars)
- def __getstate__(self):
- return (self.expression, self.mainvars)
- def __setstate__(self, state):
- self.__init__(*state)
-
- @cachedproperty
- def rqlst(self):
- select = parse(self.minimal_rql, print_errors=False).children[0]
- defined = set(split_expression(self.expression))
- for varname in self.predefined_variables:
- if varname in defined:
- select.add_eid_restriction(select.get_variable(varname), varname.lower(), 'Substitute')
- return select
-
- # permission rql expression specific stuff #################################
-
- @cached
- def transform_has_permission(self):
- found = None
- rqlst = self.rqlst
- for var in rqlst.defined_vars.itervalues():
- for varref in var.references():
- rel = varref.relation()
- if rel is None:
- continue
- try:
- prefix, action, suffix = rel.r_type.split('_')
- except ValueError:
- continue
- if prefix != 'has' or suffix != 'permission' or \
- not action in ('add', 'delete', 'update', 'read'):
- continue
- if found is None:
- found = []
- rqlst.save_state()
- assert rel.children[0].name == 'U'
- objvar = rel.children[1].children[0].variable
- rqlst.remove_node(rel)
- selected = [v.name for v in rqlst.get_selected_variables()]
- if objvar.name not in selected:
- colindex = len(selected)
- rqlst.add_selected(objvar)
- else:
- colindex = selected.index(objvar.name)
- found.append((action, colindex))
- # remove U eid %(u)s if U is not used in any other relation
- uvrefs = rqlst.defined_vars['U'].references()
- if len(uvrefs) == 1:
- rqlst.remove_node(uvrefs[0].relation())
- if found is not None:
- rql = rqlst.as_string()
- if len(rqlst.selection) == 1 and isinstance(rqlst.where, nodes.Relation):
- # only "Any X WHERE X eid %(x)s" remaining, no need to execute the rql
- keyarg = rqlst.selection[0].name.lower()
- else:
- keyarg = None
- rqlst.recover()
- return rql, found, keyarg
- return rqlst.as_string(), None, None
-
- def _check(self, _cw, **kwargs):
- """return True if the rql expression is matching the given relation
- between fromeid and toeid
-
- _cw may be a request or a server side transaction
- """
- creating = kwargs.get('creating')
- if not creating and self.eid is not None:
- key = (self.eid, tuple(sorted(kwargs.iteritems())))
- try:
- return _cw.local_perm_cache[key]
- except KeyError:
- pass
- rql, has_perm_defs, keyarg = self.transform_has_permission()
- # when creating an entity, expression related to X satisfied
- if creating and 'X' in self.rqlst.defined_vars:
- return True
- if keyarg is None:
- kwargs.setdefault('u', _cw.user.eid)
- try:
- rset = _cw.execute(rql, kwargs, build_descr=True)
- except NotImplementedError:
- self.critical('cant check rql expression, unsupported rql %s', rql)
- if self.eid is not None:
- _cw.local_perm_cache[key] = False
- return False
- except TypeResolverException as ex:
- # some expression may not be resolvable with current kwargs
- # (type conflict)
- self.warning('%s: %s', rql, str(ex))
- if self.eid is not None:
- _cw.local_perm_cache[key] = False
- return False
- except Unauthorized as ex:
- self.debug('unauthorized %s: %s', rql, str(ex))
- if self.eid is not None:
- _cw.local_perm_cache[key] = False
- return False
- else:
- rset = _cw.eid_rset(kwargs[keyarg])
- # if no special has_*_permission relation in the rql expression, just
- # check the result set contains something
- if has_perm_defs is None:
- if rset:
- if self.eid is not None:
- _cw.local_perm_cache[key] = True
- return True
- elif rset:
- # check every special has_*_permission relation is satisfied
- get_eschema = _cw.vreg.schema.eschema
- try:
- for eaction, col in has_perm_defs:
- for i in xrange(len(rset)):
- eschema = get_eschema(rset.description[i][col])
- eschema.check_perm(_cw, eaction, eid=rset[i][col])
- if self.eid is not None:
- _cw.local_perm_cache[key] = True
- return True
- except Unauthorized:
- pass
- if self.eid is not None:
- _cw.local_perm_cache[key] = False
- return False
-
- @property
- def minimal_rql(self):
- return 'Any %s WHERE %s' % (','.join(sorted(self.mainvars)),
- self.expression)
-
-# rql expressions for use in permission definition #############################
-
-class ERQLExpression(RQLExpression):
- predefined_variables = 'XU'
-
- def __init__(self, expression, mainvars=None, eid=None):
- RQLExpression.__init__(self, expression, mainvars or 'X', eid)
-
- def check(self, _cw, eid=None, creating=False, **kwargs):
- if 'X' in self.rqlst.defined_vars:
- if eid is None:
- if creating:
- return self._check(_cw, creating=True, **kwargs)
- return False
- assert creating == False
- return self._check(_cw, x=eid, **kwargs)
- return self._check(_cw, **kwargs)
-
-
-def vargraph(rqlst):
- """ builds an adjacency graph of variables from the rql syntax tree, e.g:
- Any O,S WHERE T subworkflow_exit S, T subworkflow WF, O state_of WF
- => {'WF': ['O', 'T'], 'S': ['T'], 'T': ['WF', 'S'], 'O': ['WF']}
- """
- vargraph = {}
- for relation in rqlst.get_nodes(nodes.Relation):
- try:
- rhsvarname = relation.children[1].children[0].variable.name
- lhsvarname = relation.children[0].name
- except AttributeError:
- pass
- else:
- vargraph.setdefault(lhsvarname, []).append(rhsvarname)
- vargraph.setdefault(rhsvarname, []).append(lhsvarname)
- #vargraph[(lhsvarname, rhsvarname)] = relation.r_type
- return vargraph
-
-
-class GeneratedConstraint(object):
- def __init__(self, rqlst, mainvars):
- self.snippet_rqlst = rqlst
- self.mainvars = mainvars
- self.vargraph = vargraph(rqlst)
-
-
-class RRQLExpression(RQLExpression):
- predefined_variables = 'SOU'
-
- def __init__(self, expression, mainvars=None, eid=None):
- if mainvars is None:
- mainvars = guess_rrqlexpr_mainvars(expression)
- RQLExpression.__init__(self, expression, mainvars, eid)
-
- def check(self, _cw, fromeid=None, toeid=None):
- kwargs = {}
- if 'S' in self.rqlst.defined_vars:
- if fromeid is None:
- return False
- kwargs['s'] = fromeid
- if 'O' in self.rqlst.defined_vars:
- if toeid is None:
- return False
- kwargs['o'] = toeid
- return self._check(_cw, **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 ###########################################
@@ -933,14 +972,11 @@
distinct_query = None
def serialize(self):
- # start with a comma for bw compat,see below
+ # start with a semicolon 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)
@@ -995,9 +1031,6 @@
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)
@@ -1141,7 +1174,8 @@
# bootstraping, ignore cubes
filepath = join(cubicweb.CW_SOFTWARE_ROOT, 'schemas', 'bootstrap.py')
self.info('loading %s', filepath)
- self.handle_file(filepath)
+ with tempattr(ybo, 'PACKAGE', 'cubicweb'): # though we don't care here
+ self.handle_file(filepath)
def unhandled_file(self, filepath):
"""called when a file without handler associated has been found"""
@@ -1181,11 +1215,12 @@
join(cubicweb.CW_SOFTWARE_ROOT, 'schemas', 'workflow.py'),
join(cubicweb.CW_SOFTWARE_ROOT, 'schemas', 'Bookmark.py')):
self.info('loading %s', filepath)
- self.handle_file(filepath)
+ with tempattr(ybo, 'PACKAGE', 'cubicweb'):
+ self.handle_file(filepath)
for cube in cubes:
for filepath in self.get_schema_files(cube):
- self.info('loading %s', filepath)
- self.handle_file(filepath)
+ with tempattr(ybo, 'PACKAGE', basename(cube)):
+ self.handle_file(filepath)
# these are overridden by set_log_methods below
# only defining here to prevent pylint from complaining
diff -r 2790fb8f7e03 -r e83cbc116352 schemas/_regproc.postgres.sql
--- a/schemas/_regproc.postgres.sql Fri Jun 21 14:22:39 2013 +0200
+++ b/schemas/_regproc.postgres.sql Fri Jan 10 17:12:20 2014 +0100
@@ -10,10 +10,15 @@
SELECT array_to_string($1, ', ')
$$ LANGUAGE SQL;;
+
+CREATE FUNCTION cw_array_append_unique (anyarray, anyelement) RETURNS anyarray AS $$
+ SELECT array_append($1, (SELECT $2 WHERE $2 <> ALL($1)))
+$$ LANGUAGE SQL
+
DROP AGGREGATE IF EXISTS group_concat (anyelement) CASCADE;
CREATE AGGREGATE group_concat (
basetype = anyelement,
- sfunc = array_append,
+ sfunc = cw_array_append_unique,
stype = anyarray,
finalfunc = comma_join,
initcond = '{}'
diff -r 2790fb8f7e03 -r e83cbc116352 schemas/bootstrap.py
--- a/schemas/bootstrap.py Fri Jun 21 14:22:39 2013 +0200
+++ b/schemas/bootstrap.py Fri Jan 10 17:12:20 2014 +0100
@@ -83,7 +83,7 @@
indexed = Boolean(description=_('create an index for quick search on this attribute'))
fulltextindexed = Boolean(description=_('index this attribute\'s value in the plain text index'))
internationalizable = Boolean(description=_('is this attribute\'s value translatable'))
- defaultval = String(maxsize=256)
+ defaultval = Bytes(description=_('default value as gziped pickled python object'))
extra_props = Bytes(description=_('additional type specific properties'))
description = RichString(internationalizable=True,
@@ -158,6 +158,7 @@
class CWUniqueTogetherConstraint(EntityType):
"""defines a sql-level multicolumn unique index"""
__permissions__ = PUB_SYSTEM_ENTITY_PERMS
+ name = String(required=True, unique=True, maxsize=64)
constraint_of = SubjectRelation('CWEType', cardinality='1*', composite='object',
inlined=True)
relations = SubjectRelation('CWRType', cardinality='+*',
@@ -235,7 +236,7 @@
"""groups allowed to add entities/relations of this type"""
__permissions__ = PUB_SYSTEM_REL_PERMS
name = 'add_permission'
- subject = ('CWEType', 'CWRelation')
+ subject = ('CWEType', 'CWRelation', 'CWAttribute')
object = 'CWGroup'
cardinality = '**'
@@ -268,7 +269,7 @@
"""rql expression allowing to add entities/relations of this type"""
__permissions__ = PUB_SYSTEM_REL_PERMS
name = 'add_permission'
- subject = ('CWEType', 'CWRelation')
+ subject = ('CWEType', 'CWRelation', 'CWAttribute')
object = 'RQLExpression'
cardinality = '*?'
composite = 'subject'
diff -r 2790fb8f7e03 -r e83cbc116352 server/__init__.py
--- a/server/__init__.py Fri Jun 21 14:22:39 2013 +0200
+++ b/server/__init__.py Fri Jan 10 17:12:20 2014 +0100
@@ -26,6 +26,7 @@
import sys
from os.path import join, exists
from glob import glob
+from contextlib import contextmanager
from logilab.common.modutils import LazyObject
from logilab.common.textutils import splitstrip
@@ -80,14 +81,57 @@
DBG_HOOKS = 16
#: operations
DBG_OPS = 32
+#: security
+DBG_SEC = 64
#: more verbosity
-DBG_MORE = 64
+DBG_MORE = 128
#: all level enabled
-DBG_ALL = DBG_RQL + DBG_SQL + DBG_REPO + DBG_MS + DBG_HOOKS + DBG_OPS + DBG_MORE
+DBG_ALL = DBG_RQL + DBG_SQL + DBG_REPO + DBG_MS + DBG_HOOKS + DBG_OPS + DBG_SEC + DBG_MORE
+
+_SECURITY_ITEMS = []
+_SECURITY_CAPS = ['read', 'add', 'update', 'delete']
#: current debug mode
DEBUG = 0
+@contextmanager
+def tunesecurity(items=(), capabilities=()):
+ """Context manager to use in conjunction with DBG_SEC.
+
+ This allows some tuning of:
+ * the monitored capabilities ('read', 'add', ....)
+ * the object being checked by the security checkers
+
+ When no item is given, all of them will be watched.
+ By default all capabilities are monitored, unless specified.
+
+ Example use::
+
+ from cubicweb.server import debugged, DBG_SEC, tunesecurity
+ with debugged(DBG_SEC):
+ with tunesecurity(items=('Elephant', 'trumps'),
+ capabilities=('update', 'delete')):
+ babar.cw_set(trumps=celeste)
+ flore.cw_delete()
+
+ ==>
+
+ check_perm: 'update' 'relation Elephant.trumps.Elephant'
+ [(ERQLExpression(Any X WHERE U has_update_permission X, X eid %(x)s, U eid %(u)s),
+ {'eid': 2167}, True)]
+ check_perm: 'delete' 'Elephant'
+ [(ERQLExpression(Any X WHERE U has_delete_permission X, X eid %(x)s, U eid %(u)s),
+ {'eid': 2168}, True)]
+
+ """
+ olditems = _SECURITY_ITEMS[:]
+ _SECURITY_ITEMS.extend(list(items))
+ oldactions = _SECURITY_CAPS[:]
+ _SECURITY_CAPS[:] = capabilities
+ yield
+ _SECURITY_ITEMS[:] = olditems
+ _SECURITY_CAPS[:] = oldactions
+
def set_debug(debugmode):
"""change the repository debugging mode"""
global DEBUG
@@ -309,7 +353,6 @@
SOURCE_TYPES = {'native': LazyObject('cubicweb.server.sources.native', 'NativeSQLSource'),
'datafeed': LazyObject('cubicweb.server.sources.datafeed', 'DataFeedSource'),
'ldapfeed': LazyObject('cubicweb.server.sources.ldapfeed', 'LDAPFeedSource'),
- 'ldapuser': LazyObject('cubicweb.server.sources.ldapuser', 'LDAPUserSource'),
'pyrorql': LazyObject('cubicweb.server.sources.pyrorql', 'PyroRQLSource'),
'zmqrql': LazyObject('cubicweb.server.sources.zmqrql', 'ZMQRQLSource'),
}
diff -r 2790fb8f7e03 -r e83cbc116352 server/hook.py
--- a/server/hook.py Fri Jun 21 14:22:39 2013 +0200
+++ b/server/hook.py Fri Jan 10 17:12:20 2014 +0100
@@ -678,16 +678,6 @@
{'x': self.eidfrom, 'p': self.eidto})
-PropagateSubjectRelationHook = class_renamed(
- 'PropagateSubjectRelationHook', PropagateRelationHook,
- '[3.9] PropagateSubjectRelationHook has been renamed to PropagateRelationHook')
-PropagateSubjectRelationAddHook = class_renamed(
- 'PropagateSubjectRelationAddHook', PropagateRelationAddHook,
- '[3.9] PropagateSubjectRelationAddHook has been renamed to PropagateRelationAddHook')
-PropagateSubjectRelationDelHook = class_renamed(
- 'PropagateSubjectRelationDelHook', PropagateRelationDelHook,
- '[3.9] PropagateSubjectRelationDelHook has been renamed to PropagateRelationDelHook')
-
# abstract classes for operation ###############################################
diff -r 2790fb8f7e03 -r e83cbc116352 server/hooksmanager.py
--- a/server/hooksmanager.py Fri Jun 21 14:22:39 2013 +0200
+++ b/server/hooksmanager.py Fri Jan 10 17:12:20 2014 +0100
@@ -17,11 +17,6 @@
# with CubicWeb. If not, see .
from logilab.common.deprecation import class_renamed, class_moved
from cubicweb.server import hook
+
SystemHook = class_renamed('SystemHook', hook.Hook)
-PropagateSubjectRelationHook = class_renamed('PropagateSubjectRelationHook',
- hook.PropagateSubjectRelationHook)
-PropagateSubjectRelationAddHook = class_renamed('PropagateSubjectRelationAddHook',
- hook.PropagateSubjectRelationAddHook)
-PropagateSubjectRelationDelHook = class_renamed('PropagateSubjectRelationDelHook',
- hook.PropagateSubjectRelationDelHook)
Hook = class_moved(hook.Hook)
diff -r 2790fb8f7e03 -r e83cbc116352 server/migractions.py
--- a/server/migractions.py Fri Jun 21 14:22:39 2013 +0200
+++ b/server/migractions.py Fri Jan 10 17:12:20 2014 +0100
@@ -44,7 +44,7 @@
from logilab.common.decorators import cached, clear_cache
from yams.constraints import SizeConstraint
-from yams.schema2sql import eschema2sql, rschema2sql
+from yams.schema2sql import eschema2sql, rschema2sql, unique_index_name
from yams.schema import RelationDefinitionSchema
from cubicweb import CW_SOFTWARE_ROOT, AuthenticationError, ExecutionError
@@ -559,39 +559,41 @@
self._synchronize_rdef_schema(subj, rschema, obj,
syncprops=syncprops, syncperms=syncperms)
if syncprops: # need to process __unique_together__ after rdefs were processed
- repo_unique_together = set([frozenset(ut)
- for ut in repoeschema._unique_together])
- unique_together = set([frozenset(ut)
- for ut in eschema._unique_together])
- for ut in repo_unique_together - unique_together:
- restrictions = []
- substs = {'x': repoeschema.eid}
- for i, col in enumerate(ut):
- restrictions.append('C relations T%(i)d, '
- 'T%(i)d name %%(T%(i)d)s' % {'i': i})
- substs['T%d'%i] = col
- self.rqlexec('DELETE CWUniqueTogetherConstraint C '
- 'WHERE C constraint_of E, '
- ' E eid %%(x)s,'
- ' %s' % ', '.join(restrictions),
- substs)
- def possible_unique_constraint(ut):
- for name in ut:
+ # mappings from constraint name to columns
+ # filesystem (fs) and repository (repo) wise
+ fs = {}
+ repo = {}
+ for cols in eschema._unique_together or ():
+ fs[unique_index_name(repoeschema, cols)] = sorted(cols)
+ schemaentity = self.session.entity_from_eid(repoeschema.eid)
+ for entity in schemaentity.related('constraint_of', 'object',
+ targettypes=('CWUniqueTogetherConstraint',)).entities():
+ repo[entity.name] = sorted(rel.name for rel in entity.relations)
+ added = set(fs) - set(repo)
+ removed = set(repo) - set(fs)
+
+ for name in removed:
+ self.rqlexec('DELETE CWUniqueTogetherConstraint C WHERE C name %(name)s',
+ {'name': name})
+
+ def possible_unique_constraint(cols):
+ for name in cols:
rschema = repoeschema.subjrels.get(name)
if rschema is None:
print 'dont add %s unique constraint on %s, missing %s' % (
- ','.join(ut), eschema, name)
+ ','.join(cols), eschema, name)
return False
if not (rschema.final or rschema.inlined):
- (eschema, name)
print 'dont add %s unique constraint on %s, %s is neither final nor inlined' % (
- ','.join(ut), eschema, name)
+ ','.join(cols), eschema, name)
return False
return True
- for ut in unique_together - repo_unique_together:
- if possible_unique_constraint(ut):
- rql, substs = ss.uniquetogether2rql(eschema, ut)
+
+ for name in added:
+ if possible_unique_constraint(fs[name]):
+ rql, substs = ss._uniquetogether2rql(eschema, fs[name])
substs['x'] = repoeschema.eid
+ substs['name'] = name
self.rqlexec(rql, substs)
def _synchronize_rdef_schema(self, subjtype, rtype, objtype,
@@ -1461,12 +1463,9 @@
# no result to fetch
return
- def rqlexec(self, rql, kwargs=None, cachekey=None, build_descr=True,
+ def rqlexec(self, rql, kwargs=None, build_descr=True,
ask_confirm=False):
"""rql action"""
- if cachekey is not None:
- warn('[3.8] cachekey is deprecated, you can safely remove this argument',
- DeprecationWarning, stacklevel=2)
if not isinstance(rql, (tuple, list)):
rql = ( (rql, kwargs), )
res = None
diff -r 2790fb8f7e03 -r e83cbc116352 server/msplanner.py
--- a/server/msplanner.py Fri Jun 21 14:22:39 2013 +0200
+++ b/server/msplanner.py Fri Jan 10 17:12:20 2014 +0100
@@ -92,6 +92,7 @@
from logilab.common.compat import any
from logilab.common.decorators import cached
+from logilab.common.deprecation import deprecated
from rql import BadRQLQuery
from rql.stmts import Union, Select
@@ -1262,6 +1263,7 @@
inputmap.update(step.outputmap)
+@deprecated('[3.18] old multi-source system will go away in the next version')
class MSPlanner(SSPlanner):
"""MultiSourcesPlanner: build execution plan for rql queries
diff -r 2790fb8f7e03 -r e83cbc116352 server/querier.py
--- a/server/querier.py Fri Jun 21 14:22:39 2013 +0200
+++ b/server/querier.py Fri Jan 10 17:12:20 2014 +0100
@@ -85,6 +85,7 @@
# use `term_etype` since we've to deal with rewritten constants here,
# when used as an external source by another repository.
# XXX what about local read security w/ those rewritten constants...
+ DBG = (server.DEBUG & server.DBG_SEC) and 'read' in server._SECURITY_CAPS
schema = session.repo.schema
if rqlst.where is not None:
for rel in rqlst.where.iget_nodes(Relation):
@@ -102,8 +103,14 @@
term_etype(session, rel.children[1].children[0],
solution, args))
if not session.user.matching_groups(rdef.get_groups('read')):
+ if DBG:
+ print ('check_read_access: %s %s does not match %s' %
+ (rdef, session.user.groups, rdef.get_groups('read')))
# XXX rqlexpr not allowed
raise Unauthorized('read', rel.r_type)
+ if DBG:
+ print ('check_read_access: %s %s matches %s' %
+ (rdef, session.user.groups, rdef.get_groups('read')))
localchecks = {}
# iterate on defined_vars and not on solutions to ignore column aliases
for varname in rqlst.defined_vars:
@@ -115,6 +122,9 @@
if not erqlexprs:
ex = Unauthorized('read', solution[varname])
ex.var = varname
+ if DBG:
+ print ('check_read_access: %s %s %s %s' %
+ (varname, eschema, session.user.groups, eschema.get_groups('read')))
raise ex
# don't insert security on variable only referenced by 'NOT X relation Y' or
# 'NOT EXISTS(X relation Y)'
diff -r 2790fb8f7e03 -r e83cbc116352 server/repository.py
--- a/server/repository.py Fri Jun 21 14:22:39 2013 +0200
+++ b/server/repository.py Fri Jan 10 17:12:20 2014 +0100
@@ -36,6 +36,7 @@
from os.path import join
from datetime import datetime
from time import time, localtime, strftime
+from warnings import warn
from logilab.common.decorators import cached, clear_cache
from logilab.common.compat import any
@@ -299,6 +300,8 @@
# initialized)
source.init(True, sourceent)
if not source.copy_based_source:
+ warn('[3.18] old multi-source system will go away in the next version',
+ DeprecationWarning)
self.sources.append(source)
self.querier.set_planner()
if add_to_cnxsets:
@@ -1592,10 +1595,6 @@
source.delete_relation(session, subject, rtype, object)
rschema = self.schema.rschema(rtype)
session.update_rel_cache_del(subject, rtype, object, rschema.symmetric)
- if rschema.symmetric:
- # on symmetric relation, we can't now in which sense it's
- # stored so try to delete both
- source.delete_relation(session, object, rtype, subject)
if source.should_call_hooks:
self.hm.call_hooks('after_delete_relation', session,
eidfrom=subject, rtype=rtype, eidto=object)
@@ -1665,18 +1664,24 @@
@cached
def rel_type_sources(self, rtype):
+ warn('[3.18] old multi-source system will go away in the next version',
+ DeprecationWarning)
return tuple([source for source in self.sources
if source.support_relation(rtype)
or rtype in source.dont_cross_relations])
@cached
def can_cross_relation(self, rtype):
+ warn('[3.18] old multi-source system will go away in the next version',
+ DeprecationWarning)
return tuple([source for source in self.sources
if source.support_relation(rtype)
and rtype in source.cross_relations])
@cached
def is_multi_sources_relation(self, rtype):
+ warn('[3.18] old multi-source system will go away in the next version',
+ DeprecationWarning)
return any(source for source in self.sources
if not source is self.system_source
and source.support_relation(rtype))
diff -r 2790fb8f7e03 -r e83cbc116352 server/rqlannotation.py
--- a/server/rqlannotation.py Fri Jun 21 14:22:39 2013 +0200
+++ b/server/rqlannotation.py Fri Jan 10 17:12:20 2014 +0100
@@ -130,8 +130,6 @@
# can use N.ecrit_par as principal
if (stinfo['selected'] or len(stinfo['relations']) > 1):
break
- elif rschema.symmetric and stinfo['selected']:
- break
joins.add( (rel, role) )
else:
# if there is at least one ambigous relation and no other to
diff -r 2790fb8f7e03 -r e83cbc116352 server/schemaserial.py
--- a/server/schemaserial.py Fri Jun 21 14:22:39 2013 +0200
+++ b/server/schemaserial.py Fri Jan 10 17:12:20 2014 +0100
@@ -25,9 +25,10 @@
from logilab.common.shellutils import ProgressBar
-from yams import BadSchemaDefinition, schema as schemamod, buildobjs as ybo
+from yams import (BadSchemaDefinition, schema as schemamod, buildobjs as ybo,
+ schema2sql as y2sql)
-from cubicweb import CW_SOFTWARE_ROOT, Binary
+from cubicweb import CW_SOFTWARE_ROOT, Binary, typed_eid
from cubicweb.schema import (KNOWN_RPROPERTIES, CONSTRAINTS, ETYPE_NAME_MAP,
VIRTUAL_RTYPES, PURE_VIRTUAL_RTYPES)
from cubicweb.server import sqlutils
@@ -77,8 +78,6 @@
def cstrtype_mapping(cursor):
"""cached constraint types mapping"""
map = dict(cursor.execute('Any T, X WHERE X is CWConstraintType, X name T'))
- if not 'BoundConstraint' in map:
- map['BoundConstraint'] = map['BoundaryConstraint']
return map
# schema / perms deserialization ##############################################
@@ -214,6 +213,11 @@
rdefeid, seid, reid, oeid, card, ord, desc, idx, ftidx, i18n, default = values
typeparams = extra_props.get(rdefeid)
typeparams = json.load(typeparams) if typeparams else {}
+ if default is not None:
+ if isinstance(default, Binary):
+ # while migrating from 3.17 to 3.18, we still have to
+ # handle String defaults
+ default = default.unzpickle()
_add_rdef(rdefeid, seid, reid, oeid,
cardinality=card, description=desc, order=ord,
indexed=idx, fulltextindexed=ftidx, internationalizable=i18n,
@@ -234,28 +238,24 @@
if rdefs is not None:
set_perms(rdefs, permsidx)
unique_togethers = {}
- try:
- rset = session.execute(
- 'Any X,E,R WHERE '
- 'X is CWUniqueTogetherConstraint, '
- 'X constraint_of E, X relations R', build_descr=False)
- except Exception:
- session.rollback() # first migration introducing CWUniqueTogetherConstraint cw 3.9.6
- else:
- for values in rset:
- uniquecstreid, eeid, releid = values
- eschema = schema.schema_by_eid(eeid)
- relations = unique_togethers.setdefault(uniquecstreid, (eschema, []))
- rel = ertidx[releid]
- if isinstance(rel, schemamod.RelationDefinitionSchema):
- # not yet migrated 3.9 database ('relations' target type changed
- # to CWRType in 3.10)
- rtype = rel.rtype.type
- else:
- rtype = str(rel)
- relations[1].append(rtype)
- for eschema, unique_together in unique_togethers.itervalues():
- eschema._unique_together.append(tuple(sorted(unique_together)))
+ rset = session.execute(
+ 'Any X,E,R WHERE '
+ 'X is CWUniqueTogetherConstraint, '
+ 'X constraint_of E, X relations R', build_descr=False)
+ for values in rset:
+ uniquecstreid, eeid, releid = values
+ eschema = schema.schema_by_eid(eeid)
+ relations = unique_togethers.setdefault(uniquecstreid, (eschema, []))
+ rel = ertidx[releid]
+ if isinstance(rel, schemamod.RelationDefinitionSchema):
+ # not yet migrated 3.9 database ('relations' target type changed
+ # to CWRType in 3.10)
+ rtype = rel.rtype.type
+ else:
+ rtype = str(rel)
+ relations[1].append(rtype)
+ for eschema, unique_together in unique_togethers.itervalues():
+ eschema._unique_together.append(tuple(sorted(unique_together)))
schema.infer_specialization_rules()
session.commit()
schema.reading_from_database = False
@@ -304,9 +304,6 @@
except KeyError:
return
for action, somethings in thispermsdict.iteritems():
- # XXX cw < 3.6.1 bw compat
- if isinstance(erschema, schemamod.RelationDefinitionSchema) and erschema.final and action == 'add':
- action = 'update'
erschema.permissions[action] = tuple(
isinstance(p, tuple) and erschema.rql_expression(*p) or p
for p in somethings)
@@ -344,13 +341,10 @@
cstrtypemap = {}
rql = 'INSERT CWConstraintType X: X name %(ct)s'
for cstrtype in CONSTRAINTS:
- if cstrtype == 'BoundConstraint':
- continue # XXX deprecated in yams 0.29 / cw 3.8.1
cstrtypemap[cstrtype] = execute(rql, {'ct': unicode(cstrtype)},
build_descr=False)[0][0]
if pb is not None:
pb.update()
- cstrtypemap['BoundConstraint'] = cstrtypemap['BoundaryConstraint']
# serialize relations
for rschema in schema.relations():
# skip virtual relations such as eid, has_text and identity
@@ -371,8 +365,8 @@
pb.update()
# serialize unique_together constraints
for eschema in eschemas:
- for unique_together in eschema._unique_together:
- execschemarql(execute, eschema, [uniquetogether2rql(eschema, unique_together)])
+ if eschema._unique_together:
+ execschemarql(execute, eschema, uniquetogether2rqls(eschema))
# serialize yams inheritance relationships
for rql, kwargs in specialize2rql(schema):
execute(rql, kwargs, build_descr=False)
@@ -431,7 +425,15 @@
values = {'x': eschema.eid, 'et': specialized_type.eid}
yield 'SET X specializes ET WHERE X eid %(x)s, ET eid %(et)s', values
-def uniquetogether2rql(eschema, unique_together):
+def uniquetogether2rqls(eschema):
+ rql_args = []
+ for columns in eschema._unique_together:
+ rql, args = _uniquetogether2rql(eschema, columns)
+ args['name'] = y2sql.unique_index_name(eschema, columns)
+ rql_args.append((rql, args))
+ return rql_args
+
+def _uniquetogether2rql(eschema, unique_together):
relations = []
restrictions = []
substs = {}
@@ -443,10 +445,8 @@
restrictions.append('%(rtype)s name %%(%(rtype)s)s' % {'rtype': rtype})
relations = ', '.join(relations)
restrictions = ', '.join(restrictions)
- rql = ('INSERT CWUniqueTogetherConstraint C: '
- ' C constraint_of X, %s '
- 'WHERE '
- ' X eid %%(x)s, %s')
+ rql = ('INSERT CWUniqueTogetherConstraint C: C name %%(name)s, C constraint_of X, %s '
+ 'WHERE X eid %%(x)s, %s')
return rql % (relations, restrictions), substs
@@ -536,10 +536,7 @@
elif isinstance(value, str):
value = unicode(value)
if value is not None and prop == 'default':
- if value is False:
- value = u''
- if not isinstance(value, unicode):
- value = unicode(value)
+ value = Binary.zpickle(value)
values[amap.get(prop, prop)] = value
if extra:
values['extra_props'] = Binary(json.dumps(extra))
diff -r 2790fb8f7e03 -r e83cbc116352 server/serverctl.py
--- a/server/serverctl.py Fri Jun 21 14:22:39 2013 +0200
+++ b/server/serverctl.py Fri Jan 10 17:12:20 2014 +0100
@@ -1065,6 +1065,25 @@
if val:
print key, ':', val
+
+
+def permissionshandler(relation, perms):
+ from yams.schema import RelationDefinitionSchema
+ from yams.buildobjs import DEFAULT_ATTRPERMS
+ from cubicweb.schema import (PUB_SYSTEM_ENTITY_PERMS, PUB_SYSTEM_REL_PERMS,
+ PUB_SYSTEM_ATTR_PERMS, RO_REL_PERMS, RO_ATTR_PERMS)
+ defaultrelperms = (DEFAULT_ATTRPERMS, PUB_SYSTEM_REL_PERMS,
+ PUB_SYSTEM_ATTR_PERMS, RO_REL_PERMS, RO_ATTR_PERMS)
+ defaulteperms = (PUB_SYSTEM_ENTITY_PERMS,)
+ # canonicalize vs str/unicode
+ for p in ('read', 'add', 'update', 'delete'):
+ rule = perms.get(p)
+ if rule:
+ perms[p] = tuple(str(x) if isinstance(x, basestring) else x
+ for x in rule)
+ return perms, perms in defaultrelperms or perms in defaulteperms
+
+
class SchemaDiffCommand(Command):
"""Generate a diff between schema and fsschema description.
@@ -1085,7 +1104,7 @@
repo, cnx = repo_cnx(config)
session = repo._get_session(cnx.sessionid, setcnxset=True)
fsschema = config.load_schema(expand_cubes=True)
- schema_diff(repo.schema, fsschema, diff_tool)
+ schema_diff(fsschema, repo.schema, permissionshandler, diff_tool, ignore=('eid',))
for cmdclass in (CreateInstanceDBCommand, InitInstanceCommand,
diff -r 2790fb8f7e03 -r e83cbc116352 server/session.py
--- a/server/session.py Fri Jun 21 14:22:39 2013 +0200
+++ b/server/session.py Fri Jan 10 17:12:20 2014 +0100
@@ -1082,7 +1082,7 @@
def keep_cnxset_mode(self, mode):
"""set `mode`, e.g. how the session will keep its connections set:
- * if mode == 'write', the connections set is freed after each ready
+ * if mode == 'write', the connections set is freed after each read
query, but kept until the transaction's end (eg commit or rollback)
when a write query is detected (eg INSERT/SET/DELETE queries)
@@ -1174,14 +1174,11 @@
source_from_eid = tx_meth('source_from_eid')
- def execute(self, rql, kwargs=None, eid_key=None, build_descr=True):
+ def execute(self, rql, kwargs=None, build_descr=True):
"""db-api like method directly linked to the querier execute method.
See :meth:`cubicweb.dbapi.Cursor.execute` documentation.
"""
- if eid_key is not None:
- warn('[3.8] eid_key is deprecated, you can safely remove this argument',
- DeprecationWarning, stacklevel=2)
self.timestamp = time() # update timestamp
rset = self._execute(self, rql, kwargs, build_descr)
rset.req = self
diff -r 2790fb8f7e03 -r e83cbc116352 server/sources/__init__.py
--- a/server/sources/__init__.py Fri Jun 21 14:22:39 2013 +0200
+++ b/server/sources/__init__.py Fri Jan 10 17:12:20 2014 +0100
@@ -183,7 +183,7 @@
elif value is not None:
# type check
try:
- value = configuration.convert(value, optdict, optname)
+ value = configuration._validate(value, optdict, optname)
except Exception as ex:
msg = unicode(ex) # XXX internationalization
raise ValidationError(eid, {role_name('config', 'subject'): msg})
diff -r 2790fb8f7e03 -r e83cbc116352 server/sources/ldapuser.py
--- a/server/sources/ldapuser.py Fri Jun 21 14:22:39 2013 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,551 +0,0 @@
-# copyright 2003-2013 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 .
-"""cubicweb ldap user source
-
-this source is for now limited to a read-only CWUser source
-"""
-from __future__ import division, with_statement
-from base64 import b64decode
-
-import ldap
-from ldap.filter import escape_filter_chars
-
-from rql.nodes import Relation, VariableRef, Constant, Function
-
-import warnings
-from cubicweb import UnknownEid, RepositoryError
-from cubicweb.server import ldaputils
-from cubicweb.server.utils import cartesian_product
-from cubicweb.server.sources import (AbstractSource, TrFunc, GlobTrFunc,
- TimedCache)
-
-# search scopes
-BASE = ldap.SCOPE_BASE
-ONELEVEL = ldap.SCOPE_ONELEVEL
-SUBTREE = ldap.SCOPE_SUBTREE
-
-# map ldap protocol to their standard port
-PROTO_PORT = {'ldap': 389,
- 'ldaps': 636,
- 'ldapi': None,
- }
-
-
-# module is lazily imported
-warnings.warn('Imminent drop of ldapuser. Switch to ldapfeed now!',
- DeprecationWarning)
-
-
-class LDAPUserSource(ldaputils.LDAPSourceMixIn, AbstractSource):
- """LDAP read-only CWUser source"""
- support_entities = {'CWUser': False}
-
- options = ldaputils.LDAPSourceMixIn.options + (
-
- ('synchronization-interval',
- {'type' : 'time',
- 'default': '1d',
- 'help': 'interval between synchronization with the ldap \
-directory (default to once a day).',
- 'group': 'ldap-source', 'level': 3,
- }),
- ('cache-life-time',
- {'type' : 'time',
- 'default': '2h',
- 'help': 'life time of query cache (default to two hours).',
- 'group': 'ldap-source', 'level': 3,
- }),
-
- )
-
- def update_config(self, source_entity, typedconfig):
- """update configuration from source entity. `typedconfig` is config
- properly typed with defaults set
- """
- super(LDAPUserSource, self).update_config(source_entity, typedconfig)
- self._interval = typedconfig['synchronization-interval']
- self._cache_ttl = max(71, typedconfig['cache-life-time'])
- self.reset_caches()
- # XXX copy from datafeed source
- if source_entity is not None:
- self._entity_update(source_entity)
- self.config = typedconfig
- # /end XXX
-
- def reset_caches(self):
- """method called during test to reset potential source caches"""
- self._cache = {}
- self._query_cache = TimedCache(self._cache_ttl)
-
- def init(self, activated, source_entity):
- """method called by the repository once ready to handle request"""
- super(LDAPUserSource, self).init(activated, source_entity)
- if activated:
- self.info('ldap init')
- # set minimum period of 5min 1s (the additional second is to
- # minimize resonnance effet)
- if self.user_rev_attrs['email']:
- self.repo.looping_task(max(301, self._interval), self.synchronize)
- self.repo.looping_task(self._cache_ttl // 10,
- self._query_cache.clear_expired)
-
- def synchronize(self):
- with self.repo.internal_session() as session:
- self.pull_data(session)
-
- def pull_data(self, session, force=False, raise_on_error=False):
- """synchronize content known by this repository with content in the
- external repository
- """
- self.info('synchronizing ldap source %s', self.uri)
- ldap_emailattr = self.user_rev_attrs['email']
- assert ldap_emailattr
- execute = session.execute
- cursor = session.system_sql("SELECT eid, extid FROM entities WHERE "
- "source='%s'" % self.uri)
- for eid, b64extid in cursor.fetchall():
- extid = b64decode(b64extid)
- self.debug('ldap eid %s', eid)
- # if no result found, _search automatically delete entity information
- res = self._search(session, extid, BASE)
- self.debug('ldap search %s', res)
- if res:
- ldapemailaddr = res[0].get(ldap_emailattr)
- if ldapemailaddr:
- if isinstance(ldapemailaddr, list):
- ldapemailaddr = ldapemailaddr[0] # XXX consider only the first email in the list
- rset = execute('Any X,A WHERE '
- 'X address A, U use_email X, U eid %(u)s',
- {'u': eid})
- ldapemailaddr = unicode(ldapemailaddr)
- for emaileid, emailaddr, in rset:
- if emailaddr == ldapemailaddr:
- break
- else:
- self.debug('updating email address of user %s to %s',
- extid, ldapemailaddr)
- emailrset = execute('EmailAddress A WHERE A address %(addr)s',
- {'addr': ldapemailaddr})
- if emailrset:
- execute('SET U use_email X WHERE '
- 'X eid %(x)s, U eid %(u)s',
- {'x': emailrset[0][0], 'u': eid})
- elif rset:
- if not execute('SET X address %(addr)s WHERE '
- 'U primary_email X, U eid %(u)s',
- {'addr': ldapemailaddr, 'u': eid}):
- execute('SET X address %(addr)s WHERE '
- 'X eid %(x)s',
- {'addr': ldapemailaddr, 'x': rset[0][0]})
- else:
- # no email found, create it
- _insert_email(session, ldapemailaddr, eid)
- session.commit()
-
- def ldap_name(self, var):
- if var.stinfo['relations']:
- relname = iter(var.stinfo['relations']).next().r_type
- return self.user_rev_attrs.get(relname)
- return None
-
- def prepare_columns(self, mainvars, rqlst):
- """return two list describing how to build the final results
- from the result of an ldap search (ie a list of dictionary)
- """
- columns = []
- global_transforms = []
- for i, term in enumerate(rqlst.selection):
- if isinstance(term, Constant):
- columns.append(term)
- continue
- if isinstance(term, Function): # LOWER, UPPER, COUNT...
- var = term.get_nodes(VariableRef)[0]
- var = var.variable
- try:
- mainvar = var.stinfo['attrvar'].name
- except AttributeError: # no attrvar set
- mainvar = var.name
- assert mainvar in mainvars
- trname = term.name
- ldapname = self.ldap_name(var)
- if trname in ('COUNT', 'MIN', 'MAX', 'SUM'):
- global_transforms.append(GlobTrFunc(trname, i, ldapname))
- columns.append((mainvar, ldapname))
- continue
- if trname in ('LOWER', 'UPPER'):
- columns.append((mainvar, TrFunc(trname, i, ldapname)))
- continue
- raise NotImplementedError('no support for %s function' % trname)
- if term.name in mainvars:
- columns.append((term.name, 'dn'))
- continue
- var = term.variable
- mainvar = var.stinfo['attrvar'].name
- columns.append((mainvar, self.ldap_name(var)))
- #else:
- # # probably a bug in rql splitting if we arrive here
- # raise NotImplementedError
- return columns, global_transforms
-
- def syntax_tree_search(self, session, union,
- args=None, cachekey=None, varmap=None, debug=0):
- """return result from this source for a rql query (actually from a rql
- syntax tree and a solution dictionary mapping each used variable to a
- possible type). If cachekey is given, the query necessary to fetch the
- results (but not the results themselves) may be cached using this key.
- """
- self.debug('ldap syntax tree search')
- # XXX not handled : transform/aggregat function, join on multiple users...
- assert len(union.children) == 1, 'union not supported'
- rqlst = union.children[0]
- assert not rqlst.with_, 'subquery not supported'
- rqlkey = rqlst.as_string(kwargs=args)
- try:
- results = self._query_cache[rqlkey]
- except KeyError:
- try:
- results = self.rqlst_search(session, rqlst, args)
- self._query_cache[rqlkey] = results
- except ldap.SERVER_DOWN:
- # cant connect to server
- msg = session._("can't connect to source %s, some data may be missing")
- session.set_shared_data('sources_error', msg % self.uri, txdata=True)
- return []
- return results
-
- def rqlst_search(self, session, rqlst, args):
- mainvars = []
- for varname in rqlst.defined_vars:
- for sol in rqlst.solutions:
- if sol[varname] == 'CWUser':
- mainvars.append(varname)
- break
- assert mainvars, rqlst
- columns, globtransforms = self.prepare_columns(mainvars, rqlst)
- eidfilters = [lambda x: x > 0]
- allresults = []
- generator = RQL2LDAPFilter(self, session, args, mainvars)
- for mainvar in mainvars:
- # handle restriction
- try:
- eidfilters_, ldapfilter = generator.generate(rqlst, mainvar)
- except GotDN as ex:
- assert ex.dn, 'no dn!'
- try:
- res = [self._cache[ex.dn]]
- except KeyError:
- res = self._search(session, ex.dn, BASE)
- except UnknownEid as ex:
- # raised when we are looking for the dn of an eid which is not
- # coming from this source
- res = []
- else:
- eidfilters += eidfilters_
- res = self._search(session, self.user_base_dn,
- self.user_base_scope, ldapfilter)
- allresults.append(res)
- # 1. get eid for each dn and filter according to that eid if necessary
- for i, res in enumerate(allresults):
- filteredres = []
- for resdict in res:
- # get sure the entity exists in the system table
- eid = self.repo.extid2eid(self, resdict['dn'], 'CWUser', session)
- for eidfilter in eidfilters:
- if not eidfilter(eid):
- break
- else:
- resdict['eid'] = eid
- filteredres.append(resdict)
- allresults[i] = filteredres
- # 2. merge result for each "mainvar": cartesian product
- allresults = cartesian_product(allresults)
- # 3. build final result according to column definition
- result = []
- for rawline in allresults:
- rawline = dict(zip(mainvars, rawline))
- line = []
- for varname, ldapname in columns:
- if ldapname is None:
- value = None # no mapping available
- elif ldapname == 'dn':
- value = rawline[varname]['eid']
- elif isinstance(ldapname, Constant):
- if ldapname.type == 'Substitute':
- value = args[ldapname.value]
- else:
- value = ldapname.value
- elif isinstance(ldapname, TrFunc):
- value = ldapname.apply(rawline[varname])
- else:
- value = rawline[varname].get(ldapname)
- line.append(value)
- result.append(line)
- for trfunc in globtransforms:
- result = trfunc.apply(result)
- #print '--> ldap result', result
- return result
-
- def _process_ldap_item(self, dn, iterator):
- itemdict = super(LDAPUserSource, self)._process_ldap_item(dn, iterator)
- self._cache[dn] = itemdict
- return itemdict
-
- def _process_no_such_object(self, session, dn):
- eid = self.repo.extid2eid(self, dn, 'CWUser', session, insert=False)
- if eid:
- self.warning('deleting ldap user with eid %s and dn %s', eid, dn)
- entity = session.entity_from_eid(eid, 'CWUser')
- self.repo.delete_info(session, entity, self.uri)
- self.reset_caches()
-
- def before_entity_insertion(self, session, lid, etype, eid, sourceparams):
- """called by the repository when an eid has been attributed for an
- entity stored here but the entity has not been inserted in the system
- table yet.
-
- This method must return the an Entity instance representation of this
- entity.
- """
- self.debug('ldap before entity insertion')
- entity = super(LDAPUserSource, self).before_entity_insertion(
- session, lid, etype, eid, sourceparams)
- res = self._search(session, lid, BASE)[0]
- for attr in entity.e_schema.indexable_attributes():
- entity.cw_edited[attr] = res[self.user_rev_attrs[attr]]
- return entity
-
- def after_entity_insertion(self, session, lid, entity, sourceparams):
- """called by the repository after an entity stored here has been
- inserted in the system table.
- """
- self.debug('ldap after entity insertion')
- super(LDAPUserSource, self).after_entity_insertion(
- session, lid, entity, sourceparams)
- for group in self.user_default_groups:
- session.execute('SET X in_group G WHERE X eid %(x)s, G name %(group)s',
- {'x': entity.eid, 'group': group})
- # search for existant email first
- try:
- # lid = dn
- emailaddr = self._cache[lid][self.user_rev_attrs['email']]
- except KeyError:
- return
- if isinstance(emailaddr, list):
- emailaddr = emailaddr[0] # XXX consider only the first email in the list
- rset = session.execute('EmailAddress X WHERE X address %(addr)s',
- {'addr': emailaddr})
- if rset:
- session.execute('SET U primary_email X WHERE U eid %(u)s, X eid %(x)s',
- {'x': rset[0][0], 'u': entity.eid})
- else:
- # not found, create it
- _insert_email(session, emailaddr, entity.eid)
-
- def update_entity(self, session, entity):
- """replace an entity in the source"""
- raise RepositoryError('this source is read only')
-
- def delete_entity(self, session, entity):
- """delete an entity from the source"""
- raise RepositoryError('this source is read only')
-
-
-def _insert_email(session, emailaddr, ueid):
- session.execute('INSERT EmailAddress X: X address %(addr)s, U primary_email X '
- 'WHERE U eid %(x)s', {'addr': emailaddr, 'x': ueid})
-
-class GotDN(Exception):
- """exception used when a dn localizing the searched user has been found"""
- def __init__(self, dn):
- self.dn = dn
-
-
-class RQL2LDAPFilter(object):
- """generate an LDAP filter for a rql query"""
- def __init__(self, source, session, args=None, mainvars=()):
- self.source = source
- self.repo = source.repo
- self._ldap_attrs = source.user_rev_attrs
- self._base_filters = source.base_filters
- self._session = session
- if args is None:
- args = {}
- self._args = args
- self.mainvars = mainvars
-
- def generate(self, selection, mainvarname):
- self._filters = res = self._base_filters[:]
- self._mainvarname = mainvarname
- self._eidfilters = []
- self._done_not = set()
- restriction = selection.where
- if isinstance(restriction, Relation):
- # only a single relation, need to append result here (no AND/OR)
- filter = restriction.accept(self)
- if filter is not None:
- res.append(filter)
- elif restriction:
- restriction.accept(self)
- if len(res) > 1:
- return self._eidfilters, '(&%s)' % ''.join(res)
- return self._eidfilters, res[0]
-
- def visit_and(self, et):
- """generate filter for a AND subtree"""
- for c in et.children:
- part = c.accept(self)
- if part:
- self._filters.append(part)
-
- def visit_or(self, ou):
- """generate filter for a OR subtree"""
- res = []
- for c in ou.children:
- part = c.accept(self)
- if part:
- res.append(part)
- if res:
- if len(res) > 1:
- part = '(|%s)' % ''.join(res)
- else:
- part = res[0]
- self._filters.append(part)
-
- def visit_not(self, node):
- """generate filter for a OR subtree"""
- part = node.children[0].accept(self)
- if part:
- self._filters.append('(!(%s))'% part)
-
- def visit_relation(self, relation):
- """generate filter for a relation"""
- rtype = relation.r_type
- # don't care of type constraint statement (i.e. relation_type = 'is')
- if rtype == 'is':
- return ''
- lhs, rhs = relation.get_parts()
- # attribute relation
- if self.source.schema.rschema(rtype).final:
- # dunno what to do here, don't pretend anything else
- if lhs.name != self._mainvarname:
- if lhs.name in self.mainvars:
- # XXX check we don't have variable as rhs
- return
- raise NotImplementedError
- rhs_vars = rhs.get_nodes(VariableRef)
- if rhs_vars:
- if len(rhs_vars) > 1:
- raise NotImplementedError
- # selected variable, nothing to do here
- return
- # no variables in the RHS
- if isinstance(rhs.children[0], Function):
- res = rhs.children[0].accept(self)
- elif rtype != 'has_text':
- res = self._visit_attribute_relation(relation)
- else:
- raise NotImplementedError(relation)
- # regular relation XXX todo: in_group
- else:
- raise NotImplementedError(relation)
- return res
-
- def _visit_attribute_relation(self, relation):
- """generate filter for an attribute relation"""
- lhs, rhs = relation.get_parts()
- lhsvar = lhs.variable
- if relation.r_type == 'eid':
- # XXX hack
- # skip comparison sign
- eid = int(rhs.children[0].accept(self))
- if relation.neged(strict=True):
- self._done_not.add(relation.parent)
- self._eidfilters.append(lambda x: not x == eid)
- return
- if rhs.operator != '=':
- filter = {'>': lambda x: x > eid,
- '>=': lambda x: x >= eid,
- '<': lambda x: x < eid,
- '<=': lambda x: x <= eid,
- }[rhs.operator]
- self._eidfilters.append(filter)
- return
- dn = self.repo.eid2extid(self.source, eid, self._session)
- raise GotDN(dn)
- try:
- filter = '(%s%s)' % (self._ldap_attrs[relation.r_type],
- rhs.accept(self))
- except KeyError:
- # unsupported attribute
- self.source.warning('%s source can\'t handle relation %s, no '
- 'results will be returned from this source',
- self.source.uri, relation)
- raise UnknownEid # trick to return no result
- return filter
-
- def visit_comparison(self, cmp):
- """generate filter for a comparaison"""
- return '%s%s'% (cmp.operator, cmp.children[0].accept(self))
-
- def visit_mathexpression(self, mexpr):
- """generate filter for a mathematic expression"""
- raise NotImplementedError
-
- def visit_function(self, function):
- """generate filter name for a function"""
- if function.name == 'IN':
- return self.visit_in(function)
- raise NotImplementedError
-
- def visit_in(self, function):
- grandpapa = function.parent.parent
- ldapattr = self._ldap_attrs[grandpapa.r_type]
- res = []
- for c in function.children:
- part = c.accept(self)
- if part:
- res.append(part)
- if res:
- if len(res) > 1:
- part = '(|%s)' % ''.join('(%s=%s)' % (ldapattr, v) for v in res)
- else:
- part = '(%s=%s)' % (ldapattr, res[0])
- return part
-
- def visit_constant(self, constant):
- """generate filter name for a constant"""
- value = constant.value
- if constant.type is None:
- raise NotImplementedError
- if constant.type == 'Date':
- raise NotImplementedError
- #value = self.keyword_map[value]()
- elif constant.type == 'Substitute':
- value = self._args[constant.value]
- else:
- value = constant.value
- if isinstance(value, unicode):
- value = value.encode('utf8')
- else:
- value = str(value)
- return escape_filter_chars(value)
-
- def visit_variableref(self, variableref):
- """get the sql name for a variable reference"""
- pass
-
diff -r 2790fb8f7e03 -r e83cbc116352 server/sources/native.py
--- a/server/sources/native.py Fri Jun 21 14:22:39 2013 +0200
+++ b/server/sources/native.py Fri Jan 10 17:12:20 2014 +0100
@@ -757,24 +757,17 @@
if ex.__class__.__name__ == 'IntegrityError':
# need string comparison because of various backends
for arg in ex.args:
- if 'SQL Server' in arg:
- mo = re.search("'unique_cw_[^ ]+'", arg)
- else: # postgres
- mo = re.search('"unique_cw_[^ ]+"', arg)
+ # postgres, sqlserver
+ mo = re.search("unique_[a-z0-9]{32}", arg)
if mo is not None:
- index_name = mo.group(0)[1:-1] # eat the surrounding " pair
- elements = index_name.split('_cw_')[1:]
- etype = elements[0]
- rtypes = elements[1:]
- raise UniqueTogetherError(etype, rtypes)
+ raise UniqueTogetherError(session, cstrname=mo.group(0))
# sqlite
mo = re.search('columns (.*) are not unique', arg)
if mo is not None: # sqlite in use
# we left chop the 'cw_' prefix of attribute names
rtypes = [c.strip()[3:]
for c in mo.group(1).split(',')]
- etype = '???'
- raise UniqueTogetherError(etype, rtypes)
+ raise UniqueTogetherError(session, rtypes=rtypes)
raise
return cursor
diff -r 2790fb8f7e03 -r e83cbc116352 server/sources/pyrorql.py
--- a/server/sources/pyrorql.py Fri Jun 21 14:22:39 2013 +0200
+++ b/server/sources/pyrorql.py Fri Jan 10 17:12:20 2014 +0100
@@ -20,6 +20,11 @@
__docformat__ = "restructuredtext en"
_ = unicode
+# module is lazily imported
+import warnings
+warnings.warn('Imminent drop of pyrorql source. Switch to datafeed now!',
+ DeprecationWarning)
+
import threading
from Pyro.errors import PyroError, ConnectionClosedError
diff -r 2790fb8f7e03 -r e83cbc116352 server/sources/rql2sql.py
--- a/server/sources/rql2sql.py Fri Jun 21 14:22:39 2013 +0200
+++ b/server/sources/rql2sql.py Fri Jan 10 17:12:20 2014 +0100
@@ -242,12 +242,6 @@
rhsconst = None # ColumnAlias
return lhs, lhsconst, rhs, rhsconst
-def switch_relation_field(sql, table=''):
- switchedsql = sql.replace(table + '.eid_from', '__eid_from__')
- switchedsql = switchedsql.replace(table + '.eid_to',
- table + '.eid_from')
- return switchedsql.replace('__eid_from__', table + '.eid_to')
-
def sort_term_selection(sorts, rqlst, groups):
# XXX beurk
if isinstance(rqlst, list):
@@ -1132,8 +1126,6 @@
sqls += self._process_relation_term(relation, rid, lhsvar, lhsconst, 'eid_from')
sqls += self._process_relation_term(relation, rid, rhsvar, rhsconst, 'eid_to')
sql = ' AND '.join(sqls)
- if rschema.symmetric:
- sql = '(%s OR %s)' % (sql, switch_relation_field(sql))
return sql
def _visit_outer_join_relation(self, relation, rschema):
diff -r 2790fb8f7e03 -r e83cbc116352 server/sqlutils.py
--- a/server/sqlutils.py Fri Jun 21 14:22:39 2013 +0200
+++ b/server/sqlutils.py Fri Jan 10 17:12:20 2014 +0100
@@ -41,11 +41,6 @@
SQL_PREFIX = 'cw_'
def _run_command(cmd):
- """backup/restore command are string w/ lgc < 0.47, lists with earlier versions
- """
- if isinstance(cmd, basestring):
- print '->', cmd
- return subprocess.call(cmd, shell=True)
print ' '.join(cmd)
return subprocess.call(cmd)
@@ -332,10 +327,10 @@
class group_concat(object):
def __init__(self):
- self.values = []
+ self.values = set()
def step(self, value):
if value is not None:
- self.values.append(value)
+ self.values.add(value)
def finalize(self):
return ', '.join(unicode(v) for v in self.values)
diff -r 2790fb8f7e03 -r e83cbc116352 server/test/data/migratedapp/schema.py
--- a/server/test/data/migratedapp/schema.py Fri Jun 21 14:22:39 2013 +0200
+++ b/server/test/data/migratedapp/schema.py Fri Jan 10 17:12:20 2014 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -16,6 +16,7 @@
# You should have received a copy of the GNU Lesser General Public License along
# with CubicWeb. If not, see .
"""cw.server.migraction test"""
+import datetime as dt
from yams.buildobjs import (EntityType, RelationType, RelationDefinition,
SubjectRelation, Bytes,
RichString, String, Int, Boolean, Datetime, Date)
@@ -38,13 +39,27 @@
concerne = SubjectRelation('Societe')
opt_attr = Bytes()
-class concerne(RelationType):
+class Societe(WorkflowableEntityType):
__permissions__ = {
- 'read': ('managers', 'users', 'guests'),
- 'add': ('managers', RRQLExpression('U has_update_permission S')),
- 'delete': ('managers', RRQLExpression('O owned_by U')),
+ 'read': ('managers', 'users', 'guests'),
+ 'update': ('managers', 'owners'),
+ 'delete': ('managers', 'owners'),
+ 'add': ('managers', 'users',)
}
+ nom = String(maxsize=64, fulltextindexed=True)
+ web = String(maxsize=128)
+ tel = Int()
+ fax = Int()
+ rncs = String(maxsize=128)
+ ad1 = String(maxsize=128)
+ ad2 = String(maxsize=128)
+ ad3 = String(maxsize=128)
+ cp = String(maxsize=12)
+ ville= String(maxsize=32)
+# Division and SubDivision are gone
+
+# New
class Para(EntityType):
para = String(maxsize=512)
newattr = String()
@@ -62,43 +77,18 @@
'PE require_permission P, P name "add_note", '
'P require_group G'),)}
- whatever = Int(default=2) # keep it before `date` for unittest_migraction.test_add_attribute_int
+ whatever = Int(default=0) # keep it before `date` for unittest_migraction.test_add_attribute_int
+ yesno = Boolean(default=False)
date = Datetime()
type = String(maxsize=1)
unique_id = String(maxsize=1, required=True, unique=True)
mydate = Date(default='TODAY')
+ oldstyledefaultdate = Date(default='2013/01/01')
+ newstyledefaultdate = Date(default=dt.date(2013, 1, 1))
shortpara = String(maxsize=64, default='hop')
ecrit_par = SubjectRelation('Personne', constraints=[RQLConstraint('S concerne A, O concerne A')])
attachment = SubjectRelation('File')
-class Text(Para):
- __specializes_schema__ = True
- summary = String(maxsize=512)
-
-class ecrit_par(RelationType):
- __permissions__ = {'read': ('managers', 'users', 'guests',),
- 'delete': ('managers', ),
- 'add': ('managers',
- RRQLExpression('O require_permission P, P name "add_note", '
- 'U in_group G, P require_group G'),)
- }
- inlined = True
- cardinality = '?*'
-
-
-class Folder2(EntityType):
- """folders are used to classify entities. They may be defined as a tree.
- When you include the Folder entity, all application specific entities
- may then be classified using the "filed_under" relation.
- """
- name = String(required=True, indexed=True, internationalizable=True,
- constraints=[UniqueConstraint(), SizeConstraint(64)])
- description = RichString(fulltextindexed=True)
-
-class filed_under2(RelationDefinition):
- subject ='*'
- object = 'Folder2'
-
class Personne(EntityType):
__unique_together__ = [('nom', 'prenom', 'datenaiss')]
@@ -120,32 +110,71 @@
concerne2 = SubjectRelation(('Affaire', 'Note'), cardinality='1*')
connait = SubjectRelation('Personne', symmetric=True)
+class concerne(RelationType):
+ __permissions__ = {
+ 'read': ('managers', 'users', 'guests'),
+ 'add': ('managers', RRQLExpression('U has_update_permission S')),
+ 'delete': ('managers', RRQLExpression('O owned_by U')),
+ }
+# `Old` entity type is gonce
+# `comments` is gone
+# `fiche` is gone
+# `multisource_*` rdefs are gone
+# `see_also_*` rdefs are gone
+
+class evaluee(RelationDefinition):
+ subject = ('Personne', 'CWUser', 'Societe')
+ object = ('Note')
+
+class ecrit_par(RelationType):
+ __permissions__ = {'read': ('managers', 'users', 'guests',),
+ 'delete': ('managers', ),
+ 'add': ('managers',
+ RRQLExpression('O require_permission P, P name "add_note", '
+ 'U in_group G, P require_group G'),)
+ }
+ inlined = True
+ cardinality = '?*'
+
+# `copain` rdef is gone
+# `tags` rdef is gone
+# `filed_under` rdef is gone
+# `require_permission` rdef is gone
+# `require_state` rdef is gone
+# `personne_composite` rdef is gone
+# `personne_inlined` rdef is gone
+# `login_user` rdef is gone
+# `ambiguous_inlined` rdef is gone
+
+# New
+class Text(Para):
+ __specializes_schema__ = True
+ summary = String(maxsize=512)
+
+
+# New
+class Folder2(EntityType):
+ """folders are used to classify entities. They may be defined as a tree.
+ When you include the Folder entity, all application specific entities
+ may then be classified using the "filed_under" relation.
+ """
+ name = String(required=True, indexed=True, internationalizable=True,
+ constraints=[UniqueConstraint(), SizeConstraint(64)])
+ description = RichString(fulltextindexed=True)
+
+# New
+class filed_under2(RelationDefinition):
+ subject ='*'
+ object = 'Folder2'
+
+
+# New
class New(EntityType):
new_name = String()
-class Societe(WorkflowableEntityType):
- __permissions__ = {
- 'read': ('managers', 'users', 'guests'),
- 'update': ('managers', 'owners'),
- 'delete': ('managers', 'owners'),
- 'add': ('managers', 'users',)
- }
- nom = String(maxsize=64, fulltextindexed=True)
- web = String(maxsize=128)
- tel = Int()
- fax = Int()
- rncs = String(maxsize=128)
- ad1 = String(maxsize=128)
- ad2 = String(maxsize=128)
- ad3 = String(maxsize=128)
- cp = String(maxsize=12)
- ville= String(maxsize=32)
-
+# New
class same_as(RelationDefinition):
subject = ('Societe',)
object = 'ExternalUri'
-class evaluee(RelationDefinition):
- subject = ('Personne', 'CWUser', 'Societe')
- object = ('Note')
diff -r 2790fb8f7e03 -r e83cbc116352 server/test/data/schema.py
--- a/server/test/data/schema.py Fri Jun 21 14:22:39 2013 +0200
+++ b/server/test/data/schema.py Fri Jan 10 17:12:20 2014 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 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,12 @@
'read': ('managers', 'users', 'guests'),
'update': ('managers', ERQLExpression('X in_state S, S name "todo"')),
})
-
+ something = String(maxsize=1,
+ __permissions__ = {
+ 'read': ('managers', 'users', 'guests'),
+ 'add': (ERQLExpression('NOT X para NULL'),),
+ 'update': ('managers', 'owners')
+ })
migrated_from = SubjectRelation('Note')
attachment = SubjectRelation('File')
inline1 = SubjectRelation('Affaire', inlined=True, cardinality='?*',
@@ -119,6 +124,7 @@
tzdatenaiss = TZDatetime()
test = Boolean(__permissions__={
'read': ('managers', 'users', 'guests'),
+ 'add': ('managers',),
'update': ('managers',),
})
description = String()
diff -r 2790fb8f7e03 -r e83cbc116352 server/test/unittest_ldapsource.py
--- a/server/test/unittest_ldapsource.py Fri Jun 21 14:22:39 2013 +0200
+++ b/server/test/unittest_ldapsource.py Fri Jan 10 17:12:20 2014 +0100
@@ -33,7 +33,6 @@
from cubicweb.devtools.httptest import get_available_port
from cubicweb.devtools import get_test_db_handler
-from cubicweb.server.sources.ldapuser import GlobTrFunc, UnknownEid, RQL2LDAPFilter
CONFIG_LDAPFEED = u'''
user-base-dn=ou=People,dc=cubicweb,dc=test
@@ -453,386 +452,6 @@
self.setUpClass()
-class LDAPUserSourceTC(LDAPFeedTestBase):
- test_db_id = 'ldap-user'
- tags = CubicWebTC.tags | Tags(('ldap'))
-
- @classmethod
- def pre_setup_database(cls, session, config):
- session.create_entity('CWSource', name=u'ldap', type=u'ldapuser',
- url=URL, config=CONFIG_LDAPUSER)
- session.commit()
- # XXX keep it there
- session.execute('CWUser U')
-
- def setup_database(self):
- # XXX a traceback may appear in the logs of the test due to
- # the _init_repo method that may fail to connect to the ldap
- # source if its URI has changed (from what is stored in the
- # database). This TB is NOT a failure or so.
- with self.session.repo.internal_session(safe=True) as session:
- session.execute('SET S url %(url)s, S config %(conf)s '
- 'WHERE S is CWSource, S name "ldap"',
- {"conf": CONFIG_LDAPUSER, 'url': URL} )
- session.commit()
- self.pull()
-
- def assertMetadata(self, entity):
- self.assertEqual(entity.creation_date, None)
- self.assertEqual(entity.modification_date, None)
-
- def test_synchronize(self):
- source = self.repo.sources_by_uri['ldap']
- source.synchronize()
-
- def test_base(self):
- # check a known one
- rset = self.sexecute('CWUser X WHERE X login %(login)s', {'login': 'syt'})
- e = rset.get_entity(0, 0)
- self.assertEqual(e.login, 'syt')
- e.complete()
- self.assertMetadata(e)
- self.assertEqual(e.firstname, None)
- self.assertEqual(e.surname, None)
- self.assertEqual(e.in_group[0].name, 'users')
- self.assertEqual(e.owned_by[0].login, 'syt')
- self.assertEqual(e.created_by, ())
- addresses = [pe.address for pe in e.use_email]
- addresses.sort()
- # should habe two element but ldapuser seems buggy. It's going to be dropped anyway.
- self.assertEqual(['sylvain.thenault@logilab.fr',], # 'syt@logilab.fr'],
- addresses)
- self.assertIn(e.primary_email[0].address,
- ['sylvain.thenault@logilab.fr', 'syt@logilab.fr'])
- # email content should be indexed on the user
- rset = self.sexecute('CWUser X WHERE X has_text "thenault"')
- self.assertEqual(rset.rows, [[e.eid]])
-
- def test_not(self):
- eid = self.sexecute('CWUser X WHERE X login %(login)s', {'login': 'syt'})[0][0]
- rset = self.sexecute('CWUser X WHERE NOT X eid %s' % eid)
- self.assert_(rset)
- self.assert_(not eid in (r[0] for r in rset))
-
- def test_multiple(self):
- seid = self.sexecute('CWUser X WHERE X login %(login)s', {'login': 'syt'})[0][0]
- aeid = self.sexecute('CWUser X WHERE X login %(login)s', {'login': 'adim'})[0][0]
- rset = self.sexecute('CWUser X, Y WHERE X login %(syt)s, Y login %(adim)s',
- {'syt': 'syt', 'adim': 'adim'})
- self.assertEqual(rset.rows, [[seid, aeid]])
- rset = self.sexecute('Any X,Y,L WHERE X login L, X login %(syt)s, Y login %(adim)s',
- {'syt': 'syt', 'adim': 'adim'})
- self.assertEqual(rset.rows, [[seid, aeid, 'syt']])
-
- def test_in(self):
- seid = self.sexecute('CWUser X WHERE X login %(login)s', {'login': 'syt'})[0][0]
- aeid = self.sexecute('CWUser X WHERE X login %(login)s', {'login': 'adim'})[0][0]
- rset = self.sexecute('Any X,L ORDERBY L WHERE X login IN("%s", "%s"), X login L' % ('syt', 'adim'))
- self.assertEqual(rset.rows, [[aeid, 'adim'], [seid, 'syt']])
-
- def test_relations(self):
- eid = self.sexecute('CWUser X WHERE X login %(login)s', {'login': 'syt'})[0][0]
- rset = self.sexecute('Any X,E WHERE X is CWUser, X login L, X primary_email E')
- self.assert_(eid in (r[0] for r in rset))
- rset = self.sexecute('Any X,L,E WHERE X is CWUser, X login L, X primary_email E')
- self.assert_('syt' in (r[1] for r in rset))
-
- def test_count(self):
- nbusers = self.sexecute('Any COUNT(X) WHERE X is CWUser')[0][0]
- # just check this is a possible number
- self.assert_(nbusers > 1, nbusers)
- self.assert_(nbusers < 30, nbusers)
-
- def test_upper(self):
- eid = self.sexecute('CWUser X WHERE X login %(login)s', {'login': 'syt'})[0][0]
- rset = self.sexecute('Any UPPER(L) WHERE X eid %s, X login L' % eid)
- self.assertEqual(rset[0][0], 'syt'.upper())
-
- def test_unknown_attr(self):
- eid = self.sexecute('CWUser X WHERE X login %(login)s', {'login': 'syt'})[0][0]
- rset = self.sexecute('Any L,C,M WHERE X eid %s, X login L, '
- 'X creation_date C, X modification_date M' % eid)
- self.assertEqual(rset[0][0], 'syt')
- self.assertEqual(rset[0][1], None)
- self.assertEqual(rset[0][2], None)
-
- def test_sort(self):
- logins = [l for l, in self.sexecute('Any L ORDERBY L WHERE X login L')]
- self.assertEqual(logins, sorted(logins))
-
- def test_lower_sort(self):
- logins = [l for l, in self.sexecute('Any L ORDERBY lower(L) WHERE X login L')]
- self.assertEqual(logins, sorted(logins))
-
- def test_or(self):
- rset = self.sexecute('DISTINCT Any X WHERE X login %(login)s OR (X in_group G, G name "managers")',
- {'login': 'syt'})
- self.assertEqual(len(rset), 2, rset.rows) # syt + admin
-
- def test_nonregr_set_owned_by(self):
- # test that when a user coming from ldap is triggering a transition
- # the related TrInfo has correct owner information
- self.sexecute('SET X in_group G WHERE X login %(syt)s, G name "managers"', {'syt': 'syt'})
- self.commit()
- syt = self.sexecute('CWUser X WHERE X login %(login)s', {'login': 'syt'}).get_entity(0, 0)
- self.assertEqual([g.name for g in syt.in_group], ['managers', 'users'])
- cnx = self.login('syt', password='syt')
- cu = cnx.cursor()
- adim = cu.execute('CWUser X WHERE X login %(login)s', {'login': 'adim'}).get_entity(0, 0)
- iworkflowable = adim.cw_adapt_to('IWorkflowable')
- iworkflowable.fire_transition('deactivate')
- try:
- cnx.commit()
- adim.cw_clear_all_caches()
- self.assertEqual(adim.in_state[0].name, 'deactivated')
- trinfo = iworkflowable.latest_trinfo()
- self.assertEqual(trinfo.owned_by[0].login, 'syt')
- # select from_state to skip the user's creation TrInfo
- rset = self.sexecute('Any U ORDERBY D DESC WHERE WF wf_info_for X,'
- 'WF creation_date D, WF from_state FS,'
- 'WF owned_by U?, X eid %(x)s',
- {'x': adim.eid})
- self.assertEqual(rset.rows, [[syt.eid]])
- finally:
- # restore db state
- self.restore_connection()
- adim = self.sexecute('CWUser X WHERE X login %(login)s', {'login': 'adim'}).get_entity(0, 0)
- adim.cw_adapt_to('IWorkflowable').fire_transition('activate')
- self.sexecute('DELETE X in_group G WHERE X login %(syt)s, G name "managers"', {'syt': 'syt'})
-
- def test_same_column_names(self):
- self.sexecute('Any X, Y WHERE X copain Y, X login "comme", Y login "cochon"')
-
- def test_multiple_entities_from_different_sources(self):
- req = self.request()
- self.create_user(req, 'cochon')
- self.assertTrue(self.sexecute('Any X,Y WHERE X login %(syt)s, Y login "cochon"', {'syt': 'syt'}))
-
- def test_exists1(self):
- self.session.set_cnxset()
- self.session.create_entity('CWGroup', name=u'bougloup1')
- self.session.create_entity('CWGroup', name=u'bougloup2')
- self.sexecute('SET U in_group G WHERE G name ~= "bougloup%", U login "admin"')
- self.sexecute('SET U in_group G WHERE G name = "bougloup1", U login %(syt)s', {'syt': 'syt'})
- rset = self.sexecute('Any L,SN ORDERBY L WHERE X in_state S, '
- 'S name SN, X login L, EXISTS(X in_group G, G name ~= "bougloup%")')
- self.assertEqual(rset.rows, [['admin', 'activated'], ['syt', 'activated']])
-
- def test_exists2(self):
- 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):
- 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.assertTrue(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'})
- self.assertTrue(self.sexecute('Any X, Y WHERE X copain Y, X login %(syt)s, Y login "cochon"', {'syt': 'syt'}))
- rset = self.sexecute('Any GN,L WHERE X in_group G, X login L, G name GN, G name "managers" '
- 'OR EXISTS(X copain T, T login in ("comme", "cochon"))')
- self.assertEqual(sorted(rset.rows), [['managers', 'admin'], ['users', 'comme'], ['users', 'syt']])
-
- def test_exists4(self):
- 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"')
- self.sexecute('SET X copain Y WHERE X login %(syt)s, Y login "billy"', {'syt': 'syt'})
- # search for group name, login where
- # CWUser copain with "comme" or "cochon" AND same login as the copain
- # OR
- # CWUser in_state activated AND not copain with billy
- #
- # SO we expect everybody but "comme" and "syt"
- rset= self.sexecute('Any GN,L WHERE X in_group G, X login L, G name GN, '
- 'EXISTS(X copain T, T login L, T login in ("comme", "cochon")) OR '
- 'EXISTS(X in_state S, S name "activated", NOT X copain T2, T2 login "billy")')
- all = self.sexecute('Any GN, L WHERE X in_group G, X login L, G name GN')
- all.rows.remove(['users', 'comme'])
- all.rows.remove(['users', 'syt'])
- self.assertEqual(sorted(rset.rows), sorted(all.rows))
-
- def test_exists5(self):
- 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"')
- self.sexecute('SET X copain Y WHERE X login %(syt)s, Y login "cochon"', {'syt': 'syt'})
- rset= self.sexecute('Any L WHERE X login L, '
- 'EXISTS(X copain T, T login in ("comme", "cochon")) AND '
- 'NOT EXISTS(X copain T2, T2 login "billy")')
- self.assertEqual(sorted(rset.rows), [['cochon'], ['syt']])
- rset= self.sexecute('Any GN,L WHERE X in_group G, X login L, G name GN, '
- 'EXISTS(X copain T, T login in ("comme", "cochon")) AND '
- 'NOT EXISTS(X copain T2, T2 login "billy")')
- self.assertEqual(sorted(rset.rows), [['guests', 'cochon'],
- ['users', 'cochon'],
- ['users', 'syt']])
-
- def test_cd_restriction(self):
- rset = self.sexecute('CWUser X WHERE X creation_date > "2009-02-01"')
- # admin/anon but no ldap user since it doesn't support creation_date
- self.assertEqual(sorted(e.login for e in rset.entities()),
- ['admin', 'anon'])
-
- def test_union(self):
- afeids = self.sexecute('State X')
- ueids = self.sexecute('CWUser X')
- rset = self.sexecute('(Any X WHERE X is State) UNION (Any X WHERE X is CWUser)')
- self.assertEqual(sorted(r[0] for r in rset.rows),
- sorted(r[0] for r in afeids + ueids))
-
- def _init_security_test(self):
- req = self.request()
- self.create_user(req, 'iaminguestsgrouponly', groups=('guests',))
- cnx = self.login('iaminguestsgrouponly')
- return cnx.cursor()
-
- def test_security1(self):
- cu = self._init_security_test()
- rset = cu.execute('CWUser X WHERE X login %(login)s', {'login': 'syt'})
- self.assertEqual(rset.rows, [])
- rset = cu.execute('Any X WHERE X login "iaminguestsgrouponly"')
- self.assertEqual(len(rset.rows), 1)
-
- def test_security2(self):
- cu = self._init_security_test()
- rset = cu.execute('Any X WHERE X has_text %(syt)s', {'syt': 'syt'})
- self.assertEqual(rset.rows, [])
- rset = cu.execute('Any X WHERE X has_text "iaminguestsgrouponly"')
- self.assertEqual(len(rset.rows), 1)
-
- def test_security3(self):
- cu = self._init_security_test()
- rset = cu.execute('Any F WHERE X has_text %(syt)s, X firstname F', {'syt': 'syt'})
- self.assertEqual(rset.rows, [])
- rset = cu.execute('Any F WHERE X has_text "iaminguestsgrouponly", X firstname F')
- self.assertEqual(rset.rows, [[None]])
-
- def test_nonregr1(self):
- self.sexecute('Any X,AA ORDERBY AA DESC WHERE E eid %(x)s, E owned_by X, '
- 'X modification_date AA',
- {'x': self.session.user.eid})
-
- def test_nonregr2(self):
- self.sexecute('Any X,L,AA WHERE E eid %(x)s, E owned_by X, '
- 'X login L, X modification_date AA',
- {'x': self.session.user.eid})
-
- def test_nonregr3(self):
- self.sexecute('Any X,AA ORDERBY AA DESC WHERE E eid %(x)s, '
- 'X modification_date AA',
- {'x': self.session.user.eid})
-
- def test_nonregr4(self):
- emaileid = self.sexecute('INSERT EmailAddress X: X address "toto@logilab.org"')[0][0]
- self.sexecute('Any X,AA WHERE X use_email Y, Y eid %(x)s, X modification_date AA',
- {'x': emaileid})
-
- def test_nonregr5(self):
- # original jpl query:
- # Any X, NOW - CD, P WHERE P is Project, U interested_in P, U is CWUser,
- # U login "sthenault", X concerns P, X creation_date CD ORDERBY CD DESC LIMIT 5
- rql = ('Any X, NOW - CD, P ORDERBY CD DESC LIMIT 5 WHERE P bookmarked_by U, '
- 'U login "%s", P is X, X creation_date CD') % self.session.user.login
- self.sexecute(rql, )#{'x': })
-
- def test_nonregr6(self):
- self.sexecute('Any B,U,UL GROUPBY B,U,UL WHERE B created_by U?, B is File '
- 'WITH U,UL BEING (Any U,UL WHERE ME eid %(x)s, (EXISTS(U identity ME) '
- 'OR (EXISTS(U in_group G, G name IN("managers", "staff")))) '
- 'OR (EXISTS(U in_group H, ME in_group H, NOT H name "users")), U login UL, U is CWUser)',
- {'x': self.session.user.eid})
-
-class GlobTrFuncTC(TestCase):
-
- def test_count(self):
- trfunc = GlobTrFunc('count', 0)
- res = trfunc.apply([[1], [2], [3], [4]])
- self.assertEqual(res, [[4]])
- trfunc = GlobTrFunc('count', 1)
- res = trfunc.apply([[1, 2], [2, 4], [3, 6], [1, 5]])
- self.assertEqual(res, [[1, 2], [2, 1], [3, 1]])
-
- def test_sum(self):
- trfunc = GlobTrFunc('sum', 0)
- res = trfunc.apply([[1], [2], [3], [4]])
- self.assertEqual(res, [[10]])
- trfunc = GlobTrFunc('sum', 1)
- res = trfunc.apply([[1, 2], [2, 4], [3, 6], [1, 5]])
- self.assertEqual(res, [[1, 7], [2, 4], [3, 6]])
-
- def test_min(self):
- trfunc = GlobTrFunc('min', 0)
- res = trfunc.apply([[1], [2], [3], [4]])
- self.assertEqual(res, [[1]])
- trfunc = GlobTrFunc('min', 1)
- res = trfunc.apply([[1, 2], [2, 4], [3, 6], [1, 5]])
- self.assertEqual(res, [[1, 2], [2, 4], [3, 6]])
-
- def test_max(self):
- trfunc = GlobTrFunc('max', 0)
- res = trfunc.apply([[1], [2], [3], [4]])
- self.assertEqual(res, [[4]])
- trfunc = GlobTrFunc('max', 1)
- res = trfunc.apply([[1, 2], [2, 4], [3, 6], [1, 5]])
- self.assertEqual(res, [[1, 5], [2, 4], [3, 6]])
-
-
-class RQL2LDAPFilterTC(RQLGeneratorTC):
-
- tags = RQLGeneratorTC.tags | Tags(('ldap'))
-
- @property
- def schema(self):
- """return the application schema"""
- return self._schema
-
- def setUp(self):
- self.handler = get_test_db_handler(LDAPUserSourceTC.config)
- self.handler.build_db_cache('ldap-user', LDAPUserSourceTC.pre_setup_database)
- self.handler.restore_database('ldap-user')
- self._repo = repo = self.handler.get_repo()
- self._schema = repo.schema
- super(RQL2LDAPFilterTC, self).setUp()
- ldapsource = repo.sources[-1]
- self.cnxset = repo._get_cnxset()
- session = mock_object(cnxset=self.cnxset)
- self.o = RQL2LDAPFilter(ldapsource, session)
- self.ldapclasses = ''.join(ldapsource.base_filters)
-
- def tearDown(self):
- self._repo.turn_repo_off()
- super(RQL2LDAPFilterTC, self).tearDown()
-
- def test_base(self):
- rqlst = self._prepare('CWUser X WHERE X login "toto"').children[0]
- self.assertEqual(self.o.generate(rqlst, 'X')[1],
- '(&%s(uid=toto))' % self.ldapclasses)
-
- def test_kwargs(self):
- rqlst = self._prepare('CWUser X WHERE X login %(x)s').children[0]
- self.o._args = {'x': "toto"}
- self.assertEqual(self.o.generate(rqlst, 'X')[1],
- '(&%s(uid=toto))' % self.ldapclasses)
-
- def test_get_attr(self):
- rqlst = self._prepare('Any X WHERE E firstname X, E eid 12').children[0]
- self.assertRaises(UnknownEid, self.o.generate, rqlst, 'E')
-
if __name__ == '__main__':
unittest_main()
diff -r 2790fb8f7e03 -r e83cbc116352 server/test/unittest_migractions.py
--- a/server/test/unittest_migractions.py Fri Jun 21 14:22:39 2013 +0200
+++ b/server/test/unittest_migractions.py Fri Jan 10 17:12:20 2014 +0100
@@ -72,6 +72,22 @@
CubicWebTC.tearDown(self)
self.repo.vreg['etypes'].clear_caches()
+ def test_add_attribute_bool(self):
+ self.assertFalse('yesno' in self.schema)
+ self.session.create_entity('Note')
+ self.commit()
+ self.mh.cmd_add_attribute('Note', 'yesno')
+ self.assertTrue('yesno' in self.schema)
+ self.assertEqual(self.schema['yesno'].subjects(), ('Note',))
+ self.assertEqual(self.schema['yesno'].objects(), ('Boolean',))
+ self.assertEqual(self.schema['Note'].default('yesno'), False)
+ # test default value set on existing entities
+ note = self.session.execute('Note X').get_entity(0, 0)
+ self.assertEqual(note.yesno, False)
+ # test default value set for next entities
+ self.assertEqual(self.session.create_entity('Note').yesno, False)
+ self.mh.rollback()
+
def test_add_attribute_int(self):
self.assertFalse('whatever' in self.schema)
self.session.create_entity('Note')
@@ -82,12 +98,13 @@
self.assertTrue('whatever' in self.schema)
self.assertEqual(self.schema['whatever'].subjects(), ('Note',))
self.assertEqual(self.schema['whatever'].objects(), ('Int',))
- self.assertEqual(self.schema['Note'].default('whatever'), 2)
+ self.assertEqual(self.schema['Note'].default('whatever'), 0)
# test default value set on existing entities
note = self.session.execute('Note X').get_entity(0, 0)
- self.assertEqual(note.whatever, 2)
+ self.assertIsInstance(note.whatever, int)
+ self.assertEqual(note.whatever, 0)
# test default value set for next entities
- self.assertEqual(self.session.create_entity('Note').whatever, 2)
+ self.assertEqual(self.session.create_entity('Note').whatever, 0)
# test attribute order
orderdict2 = dict(self.mh.rqlexec('Any RTN, O WHERE X name "Note", RDEF from_entity X, '
'RDEF relation_type RT, RDEF ordernum O, RT name RTN'))
@@ -127,9 +144,14 @@
def test_add_datetime_with_default_value_attribute(self):
self.assertFalse('mydate' in self.schema)
- self.assertFalse('shortpara' in self.schema)
+ self.assertFalse('oldstyledefaultdate' in self.schema)
+ self.assertFalse('newstyledefaultdate' in self.schema)
self.mh.cmd_add_attribute('Note', 'mydate')
+ self.mh.cmd_add_attribute('Note', 'oldstyledefaultdate')
+ self.mh.cmd_add_attribute('Note', 'newstyledefaultdate')
self.assertTrue('mydate' in self.schema)
+ self.assertTrue('oldstyledefaultdate' in self.schema)
+ self.assertTrue('newstyledefaultdate' in self.schema)
self.assertEqual(self.schema['mydate'].subjects(), ('Note', ))
self.assertEqual(self.schema['mydate'].objects(), ('Date', ))
testdate = date(2005, 12, 13)
@@ -137,8 +159,13 @@
eid2 = self.mh.rqlexec('INSERT Note N: N mydate %(mydate)s', {'mydate' : testdate})[0][0]
d1 = self.mh.rqlexec('Any D WHERE X eid %(x)s, X mydate D', {'x': eid1})[0][0]
d2 = self.mh.rqlexec('Any D WHERE X eid %(x)s, X mydate D', {'x': eid2})[0][0]
+ d3 = self.mh.rqlexec('Any D WHERE X eid %(x)s, X oldstyledefaultdate D', {'x': eid1})[0][0]
+ d4 = self.mh.rqlexec('Any D WHERE X eid %(x)s, X newstyledefaultdate D', {'x': eid1})[0][0]
self.assertEqual(d1, date.today())
self.assertEqual(d2, testdate)
+ myfavoritedate = date(2013, 1, 1)
+ self.assertEqual(d3, myfavoritedate)
+ self.assertEqual(d4, myfavoritedate)
self.mh.rollback()
def test_drop_chosen_constraints_ctxmanager(self):
@@ -389,8 +416,8 @@
self.assertEqual(eexpr.reverse_read_permission, ())
self.assertEqual(eexpr.reverse_delete_permission, ())
self.assertEqual(eexpr.reverse_update_permission, ())
- # no more rqlexpr to delete and add para attribute
- self.assertFalse(self._rrqlexpr_rset('add', 'para'))
+ self.assertTrue(self._rrqlexpr_rset('add', 'para'))
+ # no rqlexpr to delete para attribute
self.assertFalse(self._rrqlexpr_rset('delete', 'para'))
# new rql expr to add ecrit_par relation
rexpr = self._rrqlexpr_entity('add', 'ecrit_par')
@@ -418,19 +445,24 @@
self.assertEqual(len(self._rrqlexpr_rset('delete', 'concerne')), len(delete_concerne_rqlexpr))
self.assertEqual(len(self._rrqlexpr_rset('add', 'concerne')), len(add_concerne_rqlexpr))
# * migrschema involve:
- # * 7 rqlexprs deletion (2 in (Affaire read + Societe + travaille) + 1
- # in para attribute)
+ # * 7 erqlexprs deletions (2 in (Affaire + Societe + Note.para) + 1 Note.something
+ # * 2 rrqlexprs deletions (travaille)
# * 1 update (Affaire update)
# * 2 new (Note add, ecrit_par add)
- # * 2 implicit new for attributes update_permission (Note.para, Personne.test)
+ # * 2 implicit new for attributes (Note.para, Person.test)
# remaining orphan rql expr which should be deleted at commit (composite relation)
- self.assertEqual(cursor.execute('Any COUNT(X) WHERE X is RQLExpression, '
- 'NOT ET1 read_permission X, NOT ET2 add_permission X, '
- 'NOT ET3 delete_permission X, NOT ET4 update_permission X')[0][0],
- 7+1)
+ # unattached expressions -> pending deletion on commit
+ self.assertEqual(cursor.execute('Any COUNT(X) WHERE X is RQLExpression, X exprtype "ERQLExpression",'
+ 'NOT ET1 read_permission X, NOT ET2 add_permission X, '
+ 'NOT ET3 delete_permission X, NOT ET4 update_permission X')[0][0],
+ 7)
+ self.assertEqual(cursor.execute('Any COUNT(X) WHERE X is RQLExpression, X exprtype "RRQLExpression",'
+ 'NOT ET1 read_permission X, NOT ET2 add_permission X, '
+ 'NOT ET3 delete_permission X, NOT ET4 update_permission X')[0][0],
+ 2)
# finally
self.assertEqual(cursor.execute('Any COUNT(X) WHERE X is RQLExpression')[0][0],
- nbrqlexpr_start + 1 + 2 + 2)
+ nbrqlexpr_start + 1 + 2 + 2 + 2)
self.mh.commit()
# unique_together test
self.assertEqual(len(self.schema.eschema('Personne')._unique_together), 1)
diff -r 2790fb8f7e03 -r e83cbc116352 server/test/unittest_querier.py
--- a/server/test/unittest_querier.py Fri Jun 21 14:22:39 2013 +0200
+++ b/server/test/unittest_querier.py Fri Jan 10 17:12:20 2014 +0100
@@ -576,7 +576,7 @@
self.assertListEqual(rset.rows,
[[u'description_format', 12],
[u'description', 13],
- [u'name', 16],
+ [u'name', 17],
[u'created_by', 43],
[u'creation_date', 43],
[u'cw_source', 43],
diff -r 2790fb8f7e03 -r e83cbc116352 server/test/unittest_repository.py
--- a/server/test/unittest_repository.py Fri Jun 21 14:22:39 2013 +0200
+++ b/server/test/unittest_repository.py Fri Jan 10 17:12:20 2014 +0100
@@ -278,7 +278,7 @@
'creation_date', 'modification_date', 'cwuri',
'owned_by', 'created_by', 'cw_source',
'update_permission', 'read_permission',
- 'in_basket'))
+ 'add_permission', 'in_basket'))
self.assertListEqual(['relation_type',
'from_entity', 'to_entity',
'constrained_by',
diff -r 2790fb8f7e03 -r e83cbc116352 server/test/unittest_rql2sql.py
--- a/server/test/unittest_rql2sql.py Fri Jun 21 14:22:39 2013 +0200
+++ b/server/test/unittest_rql2sql.py Fri Jan 10 17:12:20 2014 +0100
@@ -1070,68 +1070,6 @@
FROM cw_Personne AS _P'''),
]
-SYMMETRIC = [
- ('Any P WHERE X eid 0, X connait P',
- '''SELECT DISTINCT _P.cw_eid
-FROM connait_relation AS rel_connait0, cw_Personne AS _P
-WHERE (rel_connait0.eid_from=0 AND rel_connait0.eid_to=_P.cw_eid OR rel_connait0.eid_to=0 AND rel_connait0.eid_from=_P.cw_eid)'''
- ),
-
- ('Any P WHERE X connait P',
- '''SELECT DISTINCT _P.cw_eid
-FROM connait_relation AS rel_connait0, cw_Personne AS _P
-WHERE (rel_connait0.eid_to=_P.cw_eid OR rel_connait0.eid_from=_P.cw_eid)'''
- ),
-
- ('Any X WHERE X connait P',
- '''SELECT DISTINCT _X.cw_eid
-FROM connait_relation AS rel_connait0, cw_Personne AS _X
-WHERE (rel_connait0.eid_from=_X.cw_eid OR rel_connait0.eid_to=_X.cw_eid)'''
- ),
-
- ('Any P WHERE X eid 0, NOT X connait P',
- '''SELECT _P.cw_eid
-FROM cw_Personne AS _P
-WHERE NOT (EXISTS(SELECT 1 FROM connait_relation AS rel_connait0 WHERE (rel_connait0.eid_from=0 AND rel_connait0.eid_to=_P.cw_eid OR rel_connait0.eid_to=0 AND rel_connait0.eid_from=_P.cw_eid)))'''),
-
- ('Any P WHERE NOT X connait P',
- '''SELECT _P.cw_eid
-FROM cw_Personne AS _P
-WHERE NOT (EXISTS(SELECT 1 FROM connait_relation AS rel_connait0 WHERE (rel_connait0.eid_to=_P.cw_eid OR rel_connait0.eid_from=_P.cw_eid)))'''),
-
- ('Any X WHERE NOT X connait P',
- '''SELECT _X.cw_eid
-FROM cw_Personne AS _X
-WHERE NOT (EXISTS(SELECT 1 FROM connait_relation AS rel_connait0 WHERE (rel_connait0.eid_from=_X.cw_eid OR rel_connait0.eid_to=_X.cw_eid)))'''),
-
- ('Any P WHERE X connait P, P nom "nom"',
- '''SELECT DISTINCT _P.cw_eid
-FROM connait_relation AS rel_connait0, cw_Personne AS _P
-WHERE (rel_connait0.eid_to=_P.cw_eid OR rel_connait0.eid_from=_P.cw_eid) AND _P.cw_nom=nom'''),
-
- ('Any X WHERE X connait P, P nom "nom"',
- '''SELECT DISTINCT _X.cw_eid
-FROM connait_relation AS rel_connait0, cw_Personne AS _P, cw_Personne AS _X
-WHERE (rel_connait0.eid_from=_X.cw_eid AND rel_connait0.eid_to=_P.cw_eid OR rel_connait0.eid_to=_X.cw_eid AND rel_connait0.eid_from=_P.cw_eid) AND _P.cw_nom=nom'''
- ),
-
- ('DISTINCT Any P WHERE P connait S OR S connait P, S nom "chouette"',
- '''SELECT DISTINCT _P.cw_eid
-FROM connait_relation AS rel_connait0, cw_Personne AS _P, cw_Personne AS _S
-WHERE (rel_connait0.eid_from=_P.cw_eid AND rel_connait0.eid_to=_S.cw_eid OR rel_connait0.eid_to=_P.cw_eid AND rel_connait0.eid_from=_S.cw_eid) AND _S.cw_nom=chouette'''
- )
- ]
-
-SYMMETRIC_WITH_LIMIT = [
- ('Any X ORDERBY X DESC LIMIT 9 WHERE E eid 0, E connait X',
- '''SELECT DISTINCT _X.cw_eid
-FROM connait_relation AS rel_connait0, cw_Personne AS _X
-WHERE (rel_connait0.eid_from=0 AND rel_connait0.eid_to=_X.cw_eid OR rel_connait0.eid_to=0 AND rel_connait0.eid_from=_X.cw_eid)
-ORDER BY 1 DESC
-LIMIT 9'''
- ),
-]
-
INLINE = [
('Any P WHERE N eid 1, N ecrit_par P, NOT P owned_by P2',
@@ -1578,10 +1516,6 @@
rqlst = self._prepare(rql)
self.assertRaises(BadRQLQuery, self.o.generate, rqlst)
- def test_symmetric(self):
- for t in self._parse(SYMMETRIC + SYMMETRIC_WITH_LIMIT):
- yield t
-
def test_inline(self):
for t in self._parse(INLINE):
yield t
@@ -1806,10 +1740,6 @@
'''SELECT DATEPART(WEEKDAY, _P.cw_creation_date)
FROM cw_Personne AS _P''')
- def test_symmetric(self):
- for t in self._parse(SYMMETRIC):
- yield t
-
def test_basic_parse(self):
for t in self._parse(BASIC):# + BASIC_WITH_LIMIT):
yield t
diff -r 2790fb8f7e03 -r e83cbc116352 server/test/unittest_schemaserial.py
--- a/server/test/unittest_schemaserial.py Fri Jun 21 14:22:39 2013 +0200
+++ b/server/test/unittest_schemaserial.py Fri Jan 10 17:12:20 2014 +0100
@@ -22,6 +22,7 @@
from logilab.common.testlib import TestCase, unittest_main
+from cubicweb import Binary
from cubicweb.schema import CubicWebSchemaLoader
from cubicweb.devtools import TestServerConfiguration
@@ -132,7 +133,12 @@
'description': u'groups allowed to add entities/relations of this type', 'composite': None, 'ordernum': 9999, 'cardinality': u'**'}),
('INSERT CWRelation X: X cardinality %(cardinality)s,X composite %(composite)s,X description %(description)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE eid %(se)s,ER eid %(rt)s,OE eid %(oe)s',
{'se': None, 'rt': None, 'oe': None,
- 'description': u'rql expression allowing to add entities/relations of this type', 'composite': 'subject', 'ordernum': 9999, 'cardinality': u'*?'})],
+ 'description': u'rql expression allowing to add entities/relations of this type', 'composite': 'subject', 'ordernum': 9999, 'cardinality': u'*?'}),
+ ('INSERT CWRelation X: X cardinality %(cardinality)s,X composite %(composite)s,X description %(description)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE eid %(se)s,ER eid %(rt)s,OE eid %(oe)s',
+ {'cardinality': u'**', 'composite': None, 'description': u'groups allowed to add entities/relations of this type',
+ 'oe': None, 'ordernum': 9999, 'rt': None, 'se': None}),
+ ('INSERT CWRelation X: X cardinality %(cardinality)s,X composite %(composite)s,X description %(description)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE eid %(se)s,ER eid %(rt)s,OE eid %(oe)s',
+ {'cardinality': u'*?', 'composite': u'subject', 'description': u'rql expression allowing to add entities/relations of this type', 'oe': None, 'ordernum': 9999, 'rt': None, 'se': None})],
list(rschema2rql(schema.rschema('add_permission'), cstrtypemap)))
def test_rschema2rql3(self):
@@ -188,7 +194,6 @@
self.assertIn('extra_props', got[1][1])
# this extr
extra_props = got[1][1]['extra_props']
- from cubicweb import Binary
self.assertIsInstance(extra_props, Binary)
got[1][1]['extra_props'] = got[1][1]['extra_props'].getvalue()
self.assertListEqual(expected, got)
@@ -197,7 +202,8 @@
self.assertListEqual([
('INSERT CWAttribute X: X cardinality %(cardinality)s,X defaultval %(defaultval)s,X description %(description)s,X fulltextindexed %(fulltextindexed)s,X indexed %(indexed)s,X internationalizable %(internationalizable)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE eid %(se)s,ER eid %(rt)s,OE eid %(oe)s',
{'se': None, 'rt': None, 'oe': None,
- 'description': u'', 'internationalizable': True, 'fulltextindexed': False, 'ordernum': 3, 'defaultval': u'text/plain', 'indexed': False, 'cardinality': u'?1'}),
+ 'description': u'', 'internationalizable': True, 'fulltextindexed': False,
+ 'ordernum': 3, 'defaultval': Binary('text/plain'), 'indexed': False, 'cardinality': u'?1'}),
('INSERT CWConstraint X: X value %(value)s, X cstrtype CT, EDEF constrained_by X WHERE CT eid %(ct)s, EDEF eid %(x)s',
{'x': None, 'value': u'None', 'ct': 'FormatConstraint_eid'}),
('INSERT CWConstraint X: X value %(value)s, X cstrtype CT, EDEF constrained_by X WHERE CT eid %(ct)s, EDEF eid %(x)s',
@@ -265,6 +271,7 @@
self.assertListEqual([('SET X read_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 0}),
('SET X read_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 1}),
('SET X read_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 2}),
+ ('SET X add_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 0}),
('SET X update_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 0})],
[(rql, kwargs)
for rql, kwargs in erperms2rql(schema.rschema('name').rdef('CWEType', 'String'),
diff -r 2790fb8f7e03 -r e83cbc116352 server/test/unittest_security.py
--- a/server/test/unittest_security.py Fri Jun 21 14:22:39 2013 +0200
+++ b/server/test/unittest_security.py Fri Jan 10 17:12:20 2014 +0100
@@ -413,6 +413,16 @@
self.commit()
cu.execute("SET X para 'chouette' WHERE X eid %(x)s", {'x': note2.eid})
self.commit()
+ cu.execute("INSERT Note X: X something 'A'")
+ self.assertRaises(Unauthorized, self.commit)
+ cu.execute("INSERT Note X: X para 'zogzog', X something 'A'")
+ self.commit()
+ note = cu.execute("INSERT Note X").get_entity(0,0)
+ self.commit()
+ note.cw_set(something=u'B')
+ self.commit()
+ note.cw_set(something=None, para=u'zogzog')
+ self.commit()
def test_attribute_read_security(self):
# anon not allowed to see users'login, but they can see users
diff -r 2790fb8f7e03 -r e83cbc116352 skeleton/i18n/en.po
--- a/skeleton/i18n/en.po Fri Jun 21 14:22:39 2013 +0200
+++ b/skeleton/i18n/en.po Fri Jan 10 17:12:20 2014 +0100
@@ -5,4 +5,5 @@
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.5\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI +ZONE\n"
diff -r 2790fb8f7e03 -r e83cbc116352 skeleton/i18n/es.po
--- a/skeleton/i18n/es.po Fri Jun 21 14:22:39 2013 +0200
+++ b/skeleton/i18n/es.po Fri Jan 10 17:12:20 2014 +0100
@@ -5,4 +5,5 @@
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.5\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI +ZONE\n"
diff -r 2790fb8f7e03 -r e83cbc116352 skeleton/i18n/fr.po
--- a/skeleton/i18n/fr.po Fri Jun 21 14:22:39 2013 +0200
+++ b/skeleton/i18n/fr.po Fri Jan 10 17:12:20 2014 +0100
@@ -5,4 +5,5 @@
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.5\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI +ZONE\n"
diff -r 2790fb8f7e03 -r e83cbc116352 sobjects/notification.py
--- a/sobjects/notification.py Fri Jun 21 14:22:39 2013 +0200
+++ b/sobjects/notification.py Fri Jan 10 17:12:20 2014 +0100
@@ -206,7 +206,7 @@
kwargs.update({'user': self.user_data['login'],
'eid': entity.eid,
'etype': entity.dc_type(),
- 'url': entity.absolute_url(),
+ 'url': entity.absolute_url(__secure__=True),
'title': entity.dc_long_title(),})
return kwargs
diff -r 2790fb8f7e03 -r e83cbc116352 test/data/migration/0.1.0_web.py
--- a/test/data/migration/0.1.0_web.py Fri Jun 21 14:22:39 2013 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-# copyright 2003-2010 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 .
-"""web only
-
-"""
diff -r 2790fb8f7e03 -r e83cbc116352 test/data/schema.py
--- a/test/data/schema.py Fri Jun 21 14:22:39 2013 +0200
+++ b/test/data/schema.py Fri Jan 10 17:12:20 2014 +0100
@@ -41,7 +41,7 @@
constraints=[RQLConstraint('NOT EXISTS(O contrat_exclusif S)')])
dirige = SubjectRelation('Societe', cardinality='??',
constraints=[RQLConstraint('S actionnaire O')])
- associe = SubjectRelation('Personne', cardinality='1*',
+ associe = SubjectRelation('Personne', cardinality='?*',
constraints=[RQLConstraint('S actionnaire SOC, O actionnaire SOC')])
class Ami(EntityType):
diff -r 2790fb8f7e03 -r e83cbc116352 test/unittest_entity.py
--- a/test/unittest_entity.py Fri Jun 21 14:22:39 2013 +0200
+++ b/test/unittest_entity.py Fri Jan 10 17:12:20 2014 +0100
@@ -133,6 +133,12 @@
self.assertEqual(sorted(user._cw_related_cache), ['in_group_subject', 'primary_email_subject'])
for group in groups:
self.assertFalse('in_group_subject' in group._cw_related_cache, list(group._cw_related_cache))
+ user.cw_clear_all_caches()
+ user.related('in_group', entities=True)
+ self.assertIn('in_group_subject', user._cw_related_cache)
+ user.cw_clear_all_caches()
+ user.related('in_group', targettypes=('CWGroup',), entities=True)
+ self.assertNotIn('in_group_subject', user._cw_related_cache)
def test_related_limit(self):
req = self.request()
@@ -146,6 +152,18 @@
self.assertEqual(len(p.related('tags', 'object', entities=True, limit=2)), 2)
self.assertEqual(len(p.related('tags', 'object', entities=True)), 4)
+ def test_related_targettypes(self):
+ req = self.request()
+ p = req.create_entity('Personne', nom=u'Loxodonta', prenom=u'Babar')
+ n = req.create_entity('Note', type=u'scratch', ecrit_par=p)
+ t = req.create_entity('Tag', name=u'a tag', tags=(p, n))
+ self.commit()
+ req = self.request()
+ t = req.entity_from_eid(t.eid)
+ self.assertEqual(2, t.related('tags').rowcount)
+ self.assertEqual(1, t.related('tags', targettypes=('Personne',)).rowcount)
+ self.assertEqual(1, t.related('tags', targettypes=('Note',)).rowcount)
+
def test_cw_instantiate_relation(self):
req = self.request()
p1 = req.create_entity('Personne', nom=u'di')
@@ -189,43 +207,43 @@
# testing basic fetch_attrs attribute
self.assertEqual(Personne.fetch_rql(user),
'Any X,AA,AB,AC ORDERBY AA '
- 'WHERE X is Personne, X nom AA, X prenom AB, X modification_date AC')
+ 'WHERE X is_instance_of 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')
+ self.assertEqual(Personne.fetch_rql(user), 'Any X WHERE X is_instance_of Personne')
# testing one non final relation
Personne.fetch_attrs = ('nom', 'prenom', 'travaille')
self.assertEqual(Personne.fetch_rql(user),
'Any X,AA,AB,AC,AD ORDERBY AA '
- 'WHERE X is Personne, X nom AA, X prenom AB, X travaille AC?, AC nom AD')
+ 'WHERE X is_instance_of Personne, X nom AA, X prenom AB, X travaille AC?, AC nom AD')
# 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 ORDERBY AA '
- 'WHERE X is Personne, X nom AA, X prenom AB, X travaille AC?, AC nom AD, '
+ 'WHERE X is_instance_of 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')
self.assertEqual(Personne.fetch_rql(user),
'Any X,AA,AB,AC,AD,AE,AF ORDERBY AA,AF DESC '
- 'WHERE X is Personne, X nom AA, X prenom AB, X travaille AC?, AC nom AD, '
+ 'WHERE X is_instance_of Personne, X nom AA, X prenom AB, X travaille AC?, AC nom AD, '
'AC evaluee AE?, AE modification_date AF'
)
# testing symmetric relation
Personne.fetch_attrs = ('nom', 'connait')
self.assertEqual(Personne.fetch_rql(user), 'Any X,AA,AB ORDERBY AA '
- 'WHERE X is Personne, X nom AA, X connait AB?')
+ 'WHERE X is_instance_of Personne, X nom AA, X connait AB?')
# testing optional relation
peschema.subjrels['travaille'].rdef(peschema, seschema).cardinality = '?*'
Personne.fetch_attrs = ('nom', 'prenom', 'travaille')
Societe.fetch_attrs = ('nom',)
self.assertEqual(Personne.fetch_rql(user),
- 'Any X,AA,AB,AC,AD ORDERBY AA WHERE X is Personne, X nom AA, X prenom AB, X travaille AC?, AC nom AD')
+ 'Any X,AA,AB,AC,AD ORDERBY AA WHERE X is_instance_of Personne, X nom AA, X prenom AB, X travaille AC?, AC nom AD')
# testing relation with cardinality > 1
peschema.subjrels['travaille'].rdef(peschema, seschema).cardinality = '**'
self.assertEqual(Personne.fetch_rql(user),
- 'Any X,AA,AB ORDERBY AA WHERE X is Personne, X nom AA, X prenom AB')
+ 'Any X,AA,AB ORDERBY AA WHERE X is_instance_of Personne, X nom AA, X prenom AB')
# XXX test unauthorized attribute
finally:
# fetch_attrs restored by generic tearDown
@@ -289,7 +307,7 @@
rql = user.cw_unrelated_rql('use_email', 'EmailAddress', 'subject')[0]
self.assertEqual(rql, 'Any O,AA,AB,AC ORDERBY AC DESC '
'WHERE NOT A use_email O, S eid %(x)s, '
- 'O is EmailAddress, O address AA, O alias AB, O modification_date AC')
+ 'O is_instance_of EmailAddress, O address AA, O alias AB, O modification_date AC')
def test_unrelated_rql_security_1_user(self):
req = self.request()
@@ -299,7 +317,7 @@
rql = user.cw_unrelated_rql('use_email', 'EmailAddress', 'subject')[0]
self.assertEqual(rql, 'Any O,AA,AB,AC ORDERBY AC DESC '
'WHERE NOT A use_email O, S eid %(x)s, '
- 'O is EmailAddress, O address AA, O alias AB, O modification_date AC')
+ 'O is_instance_of 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 '
@@ -320,7 +338,7 @@
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 '
- 'WHERE NOT S use_email O, O eid %(x)s, S is CWUser, '
+ 'WHERE NOT S use_email O, O eid %(x)s, S is_instance_of CWUser, '
'S login AA, S firstname AB, S surname AC, S modification_date AD')
self.login('anon')
rperms = self.schema['EmailAddress'].permissions['read']
@@ -354,7 +372,7 @@
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')
+ 'O is_instance_of 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())
@@ -374,7 +392,7 @@
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 is_instance_of Personne, O nom AA, O prenom AB, '
'O modification_date AC')
def test_unrelated_rql_constraints_edition_subject(self):
@@ -417,7 +435,7 @@
rql, args = person.cw_unrelated_rql('actionnaire', 'Societe', 'subject',
lt_infos=lt_infos)
self.assertEqual(u'Any O ORDERBY O WHERE NOT A actionnaire O, '
- u'O is Societe, NOT EXISTS(O eid %(O)s), '
+ u'O is_instance_of Societe, NOT EXISTS(O eid %(O)s), '
u'A is Personne', rql)
self.assertEqual({'O': soc.eid}, args)
@@ -430,7 +448,7 @@
rql, args = soc.cw_unrelated_rql('actionnaire', 'Personne', 'object',
lt_infos=lt_infos)
self.assertEqual(u'Any S ORDERBY S WHERE NOT S actionnaire A, '
- u'S is Personne, NOT EXISTS(S eid %(S)s), '
+ u'S is_instance_of Personne, NOT EXISTS(S eid %(S)s), '
u'A is Societe', rql)
self.assertEqual({'S': person.eid}, args)
@@ -443,7 +461,7 @@
rql, args = soc.cw_unrelated_rql('dirige', 'Personne', 'object',
lt_infos=lt_infos)
self.assertEqual(u'Any S ORDERBY S WHERE NOT S dirige A, '
- u'S is Personne, EXISTS(S eid %(S)s), '
+ u'S is_instance_of Personne, EXISTS(S eid %(S)s), '
u'A is Societe', rql)
self.assertEqual({'S': person.eid}, args)
@@ -453,7 +471,7 @@
self.vreg['etypes'].etype_class('Personne').fetch_attrs = ()
soc = req.create_entity('Societe', nom=u'logilab')
rql, args = person.cw_unrelated_rql('associe', 'Personne', 'subject')
- self.assertEqual(u'Any O ORDERBY O WHERE O is Personne', rql)
+ self.assertEqual(u'Any O ORDERBY O WHERE O is_instance_of Personne', rql)
self.assertEqual({}, args)
def test_unrelated_rql_s_linkto_s_unused_info(self):
@@ -464,7 +482,7 @@
lt_infos = {('dirige', 'subject'): [other_p.eid]}
rql, args = person.cw_unrelated_rql('associe', 'Personne', 'subject',
lt_infos=lt_infos)
- self.assertEqual(u'Any O ORDERBY O WHERE O is Personne', rql)
+ self.assertEqual(u'Any O ORDERBY O WHERE O is_instance_of Personne', rql)
def test_unrelated_base(self):
req = self.request()
@@ -742,7 +760,7 @@
self.assertEqual(card.absolute_url(),
'http://testing.fr/cubicweb/%s' % card.eid)
- def test_create_entity(self):
+ def test_create_and_compare_entity(self):
req = self.request()
p1 = req.create_entity('Personne', nom=u'fayolle', prenom=u'alexandre')
p2 = req.create_entity('Personne', nom=u'campeas', prenom=u'aurelien')
@@ -756,6 +774,15 @@
self.assertEqual(sorted([c.nom for c in p.evaluee]), ['campeas', 'fayolle'])
self.assertEqual([c.type for c in p.reverse_ecrit_par], ['z'])
+ req = self.request()
+ auc = req.execute('Personne P WHERE P prenom "aurelien"').get_entity(0,0)
+ persons = set()
+ persons.add(p1)
+ persons.add(p2)
+ persons.add(auc)
+ self.assertEqual(2, len(persons))
+ self.assertNotEqual(p1, p2)
+ self.assertEqual(p2, auc)
if __name__ == '__main__':
diff -r 2790fb8f7e03 -r e83cbc116352 test/unittest_mail.py
--- a/test/unittest_mail.py Fri Jun 21 14:22:39 2013 +0200
+++ b/test/unittest_mail.py Fri Jan 10 17:12:20 2014 +0100
@@ -31,7 +31,7 @@
def getlogin():
- """avoid usinng os.getlogin() because of strange tty / stdin problems
+ """avoid using os.getlogin() because of strange tty / stdin problems
(man 3 getlogin)
Another solution would be to use $LOGNAME, $USER or $USERNAME
"""
diff -r 2790fb8f7e03 -r e83cbc116352 test/unittest_migration.py
--- a/test/unittest_migration.py Fri Jun 21 14:22:39 2013 +0200
+++ b/test/unittest_migration.py Fri Jan 10 17:12:20 2014 +0100
@@ -79,10 +79,6 @@
self.assert_(not isinstance(config.migration_handler(), ServerMigrationHelper))
self.assertIsInstance(config.migration_handler(), MigrationHelper)
config = self.config
- config.__class__.name = 'twisted'
- self.assertListEqual(filter_scripts(config, TMIGRDIR, (0,0,4), (0,1,0)),
- [((0, 1 ,0), TMIGRDIR+'0.1.0_common.py'),
- ((0, 1 ,0), TMIGRDIR+'0.1.0_web.py')])
config.__class__.name = 'repository'
self.assertListEqual(filter_scripts(config, TMIGRDIR, (0,0,4), (0,1,0)),
[((0, 1 ,0), TMIGRDIR+'0.1.0_Any.py'),
@@ -92,8 +88,7 @@
self.assertListEqual(filter_scripts(config, TMIGRDIR, (0,0,4), (0,1,0)),
[((0, 1 ,0), TMIGRDIR+'0.1.0_Any.py'),
((0, 1 ,0), TMIGRDIR+'0.1.0_common.py'),
- ((0, 1 ,0), TMIGRDIR+'0.1.0_repository.py'),
- ((0, 1 ,0), TMIGRDIR+'0.1.0_web.py')])
+ ((0, 1 ,0), TMIGRDIR+'0.1.0_repository.py')])
config.__class__.name = 'repository'
diff -r 2790fb8f7e03 -r e83cbc116352 test/unittest_req.py
--- a/test/unittest_req.py Fri Jun 21 14:22:39 2013 +0200
+++ b/test/unittest_req.py Fri Jan 10 17:12:20 2014 +0100
@@ -18,7 +18,7 @@
from logilab.common.testlib import TestCase, unittest_main
from cubicweb import ObjectNotFound
-from cubicweb.req import RequestSessionBase
+from cubicweb.req import RequestSessionBase, FindEntityError
from cubicweb.devtools.testlib import CubicWebTC
from cubicweb import Unauthorized
@@ -53,11 +53,97 @@
class RequestCWTC(CubicWebTC):
+
+ def test_base_url(self):
+ base_url = self.config['base-url']
+ self.assertEqual(self.session.base_url(), base_url)
+ assert 'https-url' not in self.config
+ self.assertEqual(self.session.base_url(secure=True), base_url)
+ secure_base_url = base_url.replace('http', 'https')
+ self.config.global_set_option('https-url', secure_base_url)
+ self.assertEqual(self.session.base_url(secure=True), secure_base_url)
+
def test_view_catch_ex(self):
req = self.request()
rset = self.execute('CWUser X WHERE X login "hop"')
self.assertEqual(req.view('oneline', rset, 'null'), '')
self.assertRaises(ObjectNotFound, req.view, 'onelinee', rset, 'null')
+ def test_find_one_entity(self):
+ self.request().create_entity(
+ 'CWUser', login=u'cdevienne', upassword=u'cdevienne',
+ surname=u'de Vienne', firstname=u'Christophe',
+ in_group=self.request().find('CWGroup', name=u'users').one())
+
+ self.request().create_entity(
+ 'CWUser', login=u'adim', upassword='adim', surname=u'di mascio',
+ firstname=u'adrien',
+ in_group=self.request().find('CWGroup', name=u'users').one())
+
+ u = self.request().find_one_entity('CWUser', login=u'cdevienne')
+ self.assertEqual(u.firstname, u"Christophe")
+
+ with self.assertRaises(FindEntityError):
+ self.request().find_one_entity('CWUser', login=u'patanok')
+
+ with self.assertRaises(FindEntityError):
+ self.request().find_one_entity('CWUser')
+
+ def test_find_entities(self):
+ self.request().create_entity(
+ 'CWUser', login=u'cdevienne', upassword=u'cdevienne',
+ surname=u'de Vienne', firstname=u'Christophe',
+ in_group=self.request().find('CWGroup', name=u'users').one())
+
+ self.request().create_entity(
+ 'CWUser', login=u'adim', upassword='adim', surname=u'di mascio',
+ firstname=u'adrien',
+ in_group=self.request().find('CWGroup', name=u'users').one())
+
+ l = list(self.request().find_entities('CWUser', login=u'cdevienne'))
+ self.assertEqual(1, len(l))
+ self.assertEqual(l[0].firstname, u"Christophe")
+
+ l = list(self.request().find_entities('CWUser', login=u'patanok'))
+ self.assertEqual(0, len(l))
+
+ l = list(self.request().find_entities('CWUser'))
+ self.assertEqual(4, len(l))
+
+ def test_find(self):
+ self.request().create_entity(
+ 'CWUser', login=u'cdevienne', upassword=u'cdevienne',
+ surname=u'de Vienne', firstname=u'Christophe',
+ in_group=self.request().find('CWGroup', name=u'users').one())
+
+ self.request().create_entity(
+ 'CWUser', login=u'adim', upassword='adim', surname=u'di mascio',
+ firstname=u'adrien',
+ in_group=self.request().find('CWGroup', name=u'users').one())
+
+ u = self.request().find('CWUser', login=u'cdevienne').one()
+ self.assertEqual(u.firstname, u"Christophe")
+
+ users = list(self.request().find('CWUser').entities())
+ self.assertEqual(len(users), 4)
+
+ groups = list(
+ self.request().find('CWGroup', reverse_in_group=u).entities())
+ self.assertEqual(len(groups), 1)
+ self.assertEqual(groups[0].name, u'users')
+
+ users = self.request().find('CWUser', in_group=groups[0]).entities()
+ users = list(users)
+ self.assertEqual(len(users), 2)
+
+ with self.assertRaises(AssertionError):
+ self.request().find('CWUser', chapeau=u"melon")
+
+ with self.assertRaises(AssertionError):
+ self.request().find('CWUser', reverse_buddy=users[0])
+
+ with self.assertRaises(NotImplementedError):
+ self.request().find('CWUser', in_group=[1, 2])
+
if __name__ == '__main__':
unittest_main()
diff -r 2790fb8f7e03 -r e83cbc116352 test/unittest_rqlrewrite.py
--- a/test/unittest_rqlrewrite.py Fri Jun 21 14:22:39 2013 +0200
+++ b/test/unittest_rqlrewrite.py Fri Jan 10 17:12:20 2014 +0100
@@ -486,6 +486,13 @@
rqlst = parse('Any A, R WHERE A ref R, S is Affaire')
rewrite(rqlst, {('A', 'X'): (c_ok, c_bad)}, {})
+ def test_nonregr_is_instance_of(self):
+ user_expr = ERQLExpression('NOT X in_group AF, AF name "guests"')
+ rqlst = parse('Any O WHERE S use_email O, S is CWUser, O is_instance_of EmailAddress')
+ rewrite(rqlst, {('S', 'X'): (user_expr,)}, {})
+ self.assertEqual(rqlst.as_string(),
+ 'Any O WHERE S use_email O, S is CWUser, O is EmailAddress, '
+ 'EXISTS(NOT S in_group A, A name "guests", A is CWGroup)')
from cubicweb.devtools.testlib import CubicWebTC
from logilab.common.decorators import classproperty
diff -r 2790fb8f7e03 -r e83cbc116352 test/unittest_rset.py
--- a/test/unittest_rset.py Fri Jun 21 14:22:39 2013 +0200
+++ b/test/unittest_rset.py Fri Jan 10 17:12:20 2014 +0100
@@ -28,6 +28,8 @@
from cubicweb.devtools.testlib import CubicWebTC
from cubicweb.rset import NotAnEntity, ResultSet, attr_desc_iterator
+from cubicweb import NoResultError, MultipleResultsError
+
def pprelcachedict(d):
res = {}
@@ -368,6 +370,39 @@
attr = etype == 'Bookmark' and 'title' or 'name'
self.assertEqual(entity.cw_attr_cache[attr], n)
+ def test_one(self):
+ self.request().create_entity('CWUser', login=u'cdevienne',
+ upassword=u'cdevienne',
+ surname=u'de Vienne',
+ firstname=u'Christophe')
+ e = self.execute('Any X WHERE X login "cdevienne"').one()
+
+ self.assertEqual(e.surname, u'de Vienne')
+
+ e = self.execute(
+ 'Any X, N WHERE X login "cdevienne", X surname N').one()
+ self.assertEqual(e.surname, u'de Vienne')
+
+ e = self.execute(
+ 'Any N, X WHERE X login "cdevienne", X surname N').one(col=1)
+ self.assertEqual(e.surname, u'de Vienne')
+
+ def test_one_no_rows(self):
+ with self.assertRaises(NoResultError):
+ self.execute('Any X WHERE X login "patanok"').one()
+
+ def test_one_multiple_rows(self):
+ self.request().create_entity(
+ 'CWUser', login=u'cdevienne', upassword=u'cdevienne',
+ surname=u'de Vienne', firstname=u'Christophe')
+
+ self.request().create_entity(
+ 'CWUser', login=u'adim', upassword='adim', surname=u'di mascio',
+ firstname=u'adrien')
+
+ with self.assertRaises(MultipleResultsError):
+ self.execute('Any X WHERE X is CWUser').one()
+
def test_related_entity_optional(self):
e = self.request().create_entity('Bookmark', title=u'aaaa', path=u'path')
rset = self.execute('Any B,U,L WHERE B bookmarked_by U?, U login L')
diff -r 2790fb8f7e03 -r e83cbc116352 view.py
--- a/view.py Fri Jun 21 14:22:39 2013 +0200
+++ b/view.py Fri Jan 10 17:12:20 2014 +0100
@@ -558,34 +558,6 @@
__registry__ = 'adapters'
-def implements_adapter_compat(iface):
- def _pre39_compat(func):
- def decorated(self, *args, **kwargs):
- entity = self.entity
- if hasattr(entity, func.__name__):
- warn('[3.9] %s method is deprecated, define it on a custom '
- '%s for %s instead' % (func.__name__, iface,
- entity.__class__),
- DeprecationWarning)
- member = getattr(entity, func.__name__)
- if callable(member):
- return member(*args, **kwargs)
- return member
- return func(self, *args, **kwargs)
- decorated.decorated = func
- return decorated
- return _pre39_compat
-
-
-def unwrap_adapter_compat(cls):
- parent = cls.__bases__[0]
- for member_name in dir(parent):
- member = getattr(parent, member_name)
- if isinstance(member, types.MethodType) and hasattr(member.im_func, 'decorated') and not member_name in cls.__dict__:
- method = new.instancemethod(member.im_func.decorated, None, cls)
- setattr(cls, member_name, method)
-
-
class auto_unwrap_bw_compat(type):
def __new__(mcs, name, bases, classdict):
cls = type.__new__(mcs, name, bases, classdict)
@@ -596,7 +568,6 @@
class EntityAdapter(Adapter):
"""base class for entity adapters (eg adapt an entity to an interface)"""
- __metaclass__ = auto_unwrap_bw_compat
def __init__(self, _cw, **kwargs):
try:
self.entity = kwargs.pop('entity')
diff -r 2790fb8f7e03 -r e83cbc116352 web/__init__.py
--- a/web/__init__.py Fri Jun 21 14:22:39 2013 +0200
+++ b/web/__init__.py Fri Jan 10 17:12:20 2014 +0100
@@ -31,8 +31,6 @@
from cubicweb.uilib import eid_param
assert json_dumps is not None, 'no json module installed'
-dumps = deprecated('[3.9] use cubicweb.utils.json_dumps instead of dumps')(
- json_dumps)
INTERNAL_FIELD_VALUE = '__cubicweb_internal_field__'
diff -r 2790fb8f7e03 -r e83cbc116352 web/data/cubicweb.ajax.box.js
--- a/web/data/cubicweb.ajax.box.js Fri Jun 21 14:22:39 2013 +0200
+++ b/web/data/cubicweb.ajax.box.js Fri Jan 10 17:12:20 2014 +0100
@@ -81,12 +81,12 @@
});
$input.cwautocomplete(unrelated, {multiple: Boolean(separator)});
var buttons = DIV({'class' : "sgformbuttons"},
- A({href : "javascript: noop();",
+ A({href : "javascript: $.noop();",
onclick : cw.utils.strFuncCall('ajaxBoxValidateSelectorInput',
boxid, eid, separator, addfname, msg)},
oklabel),
' / ',
- A({'href' : "javascript: noop();",
+ A({'href' : "javascript: $.noop();",
'onclick' : '$("#' + holderid + '").empty()'},
cancellabel));
holder.append(buttons);
diff -r 2790fb8f7e03 -r e83cbc116352 web/data/cubicweb.ajax.js
--- a/web/data/cubicweb.ajax.js Fri Jun 21 14:22:39 2013 +0200
+++ b/web/data/cubicweb.ajax.js Fri Jan 10 17:12:20 2014 +0100
@@ -339,11 +339,6 @@
cw.log('loadxhtml called without an element');
}
var callback = null;
- if (form && form.callback) {
- cw.log('[3.9] callback given through form.callback is deprecated, add ' + 'callback on the defered');
- callback = form.callback;
- delete form.callback;
- }
var node = this.get(0); // only consider the first element
if (cursor) {
setProgressCursor();
@@ -734,13 +729,7 @@
/* DEPRECATED *****************************************************************/
-preprocessAjaxLoad = cw.utils.deprecatedFunction(
- '[3.9] preprocessAjaxLoad() is deprecated, use loadAjaxHtmlHead instead',
- function(node, newdomnode) {
- return loadAjaxHtmlHead(newdomnode);
- }
-);
-
+// still used in cwo and keyword cubes at least
reloadComponent = cw.utils.deprecatedFunction(
'[3.9] reloadComponent() is deprecated, use loadxhtml instead',
function(compid, rql, registry, nodeid, extraargs) {
@@ -754,52 +743,6 @@
}
);
-reloadBox = cw.utils.deprecatedFunction(
- '[3.9] reloadBox() is deprecated, use loadxhtml instead',
- function(boxid, rql) {
- return reloadComponent(boxid, rql, 'ctxcomponents', boxid);
- }
-);
-
-replacePageChunk = cw.utils.deprecatedFunction(
- '[3.9] replacePageChunk() is deprecated, use loadxhtml instead',
- function(nodeId, rql, vid, extraparams, /* ... */ swap, callback) {
- var params = null;
- if (callback) {
- params = {
- callback: callback
- };
- }
- var node = jQuery('#' + nodeId)[0];
- var props = {};
- if (node) {
- props['rql'] = rql;
- props['fname'] = 'view';
- props['pageid'] = pageid;
- if (vid) {
- props['vid'] = vid;
- }
- if (extraparams) {
- jQuery.extend(props, extraparams);
- }
- // FIXME we need to do asURL(props) manually instead of
- // passing `props` directly to loadxml because replacePageChunk
- // is sometimes called (abusively) with some extra parameters in `vid`
- var mode = swap ? 'swap': 'replace';
- var url = AJAX_BASE_URL + asURL(props);
- jQuery(node).loadxhtml(url, params, 'get', mode);
- } else {
- cw.log('Node', nodeId, 'not found');
- }
- }
-);
-
-loadxhtml = cw.utils.deprecatedFunction(
- '[3.9] loadxhtml() function is deprecated, use loadxhtml method instead',
- function(nodeid, url, /* ... */ replacemode) {
- jQuery('#' + nodeid).loadxhtml(url, null, 'post', replacemode);
- }
-);
function remoteExec(fname /* ... */) {
setProgressCursor();
diff -r 2790fb8f7e03 -r e83cbc116352 web/data/cubicweb.calendar.css
--- a/web/data/cubicweb.calendar.css Fri Jun 21 14:22:39 2013 +0200
+++ b/web/data/cubicweb.calendar.css Fri Jan 10 17:12:20 2014 +0100
@@ -231,6 +231,8 @@
font-weight:bold;
padding-bottom:0.2em;
background: %(incontextBoxBodyBgColor)s;
+ border-top-left-radius: 6px;
+ border-top-right-radius: 6px;
}
.calendar th.month a{
diff -r 2790fb8f7e03 -r e83cbc116352 web/data/cubicweb.compat.js
--- a/web/data/cubicweb.compat.js Fri Jun 21 14:22:39 2013 +0200
+++ b/web/data/cubicweb.compat.js Fri Jan 10 17:12:20 2014 +0100
@@ -1,34 +1,3 @@
-cw.utils.movedToNamespace(['log', 'jqNode', 'getNode', 'evalJSON', 'urlEncode',
- 'swapDOM'], cw);
-cw.utils.movedToNamespace(['nodeWalkDepthFirst', 'formContents', 'isArray',
- 'isString', 'isArrayLike', 'sliceList',
- 'toISOTimestamp'], cw.utils);
-
-
-if ($.noop === undefined) {
- function noop() {}
-} else {
- noop = cw.utils.deprecatedFunction(
- '[3.9] noop() is deprecated, use $.noop() instead (XXX requires jQuery 1.4)',
- $.noop);
-}
-
-// ========== ARRAY EXTENSIONS ========== ///
-Array.prototype.contains = cw.utils.deprecatedFunction(
- '[3.9] array.contains(elt) is deprecated, use $.inArray(elt, array)!=-1 instead',
- function(element) {
- return jQuery.inArray(element, this) != - 1;
- }
-);
-
-// ========== END OF ARRAY EXTENSIONS ========== ///
-forEach = cw.utils.deprecatedFunction(
- '[3.9] forEach() is deprecated, use $.each() instead',
- function(array, func) {
- return $.each(array, func);
- }
-);
-
/**
* .. function:: cw.utils.deprecatedFunction(msg, function)
*
@@ -41,64 +10,20 @@
* [ ["a", "b", "c"], ["d", "e"] ]
*/
// XXX why not the same argument order as $.map and forEach ?
-map = cw.utils.deprecatedFunction(
- '[3.9] map() is deprecated, use $.map instead',
- function(func, array) {
- var result = [];
- for (var i = 0, length = array.length; i < length; i++) {
- result.push(func(array[i]));
- }
- return result;
- }
-);
-findValue = cw.utils.deprecatedFunction(
- '[3.9] findValue(array, elt) is deprecated, use $.inArray(elt, array) instead',
- function(array, element) {
- return jQuery.inArray(element, array);
- }
-);
-
-filter = cw.utils.deprecatedFunction(
- '[3.9] filter(func, array) is deprecated, use $.grep(array, f) instead',
- function(func, array) {
- return $.grep(array, func);
+function map(func, array) {
+ var result = [];
+ for (var i = 0, length = array.length; i < length; i++) {
+ result.push(func(array[i]));
}
-);
-
-addElementClass = cw.utils.deprecatedFunction(
- '[3.9] addElementClass(node, cls) is deprecated, use $(node).addClass(cls) instead',
- function(node, klass) {
- $(node).addClass(klass);
- }
-);
+ return result;
+}
-removeElementClass = cw.utils.deprecatedFunction(
- '[3.9] removeElementClass(node, cls) is deprecated, use $(node).removeClass(cls) instead',
- function(node, klass) {
- $(node).removeClass(klass);
- }
-);
-hasElementClass = cw.utils.deprecatedFunction(
- '[3.9] hasElementClass(node, cls) is deprecated, use $(node).hasClass(cls)',
- function(node, klass) {
- return $(node).hasClass(klass);
- }
-);
-
+// skm cube still uses this
getNodeAttribute = cw.utils.deprecatedFunction(
'[3.9] getNodeAttribute(node, attr) is deprecated, use $(node).attr(attr)',
function(node, attribute) {
return $(node).attr(attribute);
}
);
-
-/**
- * The only known usage of KEYS is in the tag cube. Once cubicweb-tag 1.7.0 is out,
- * this current definition can be removed.
- */
-var KEYS = {
- KEY_ESC: 27,
- KEY_ENTER: 13
-};
diff -r 2790fb8f7e03 -r e83cbc116352 web/data/cubicweb.css
--- a/web/data/cubicweb.css Fri Jun 21 14:22:39 2013 +0200
+++ b/web/data/cubicweb.css Fri Jan 10 17:12:20 2014 +0100
@@ -17,9 +17,6 @@
}
h1, h2, h3 { margin-top:0; margin-bottom:0; }
-/* got rhythm ? beat of 12*1.25 = 15 px */
-.rhythm_bg { background: url("%(baseRhythmBg)s") repeat ! important; }
-
h1,
.vtitle {
font-size: %(h1FontSize)s;
@@ -297,7 +294,6 @@
div#pageContent {
clear: both;
- /* margin-top:-1px; *//* enable when testing rhythm */
background: %(pageContentBgColor)s;
border: 1px solid %(pageContentBorderColor)s;
padding: 0 %(pageContentPadding)s %(pageContentPadding)s;
@@ -358,17 +354,20 @@
div.shadow{
height: 14px;
- background: url("shadow.gif") no-repeat top right;
}
div.sideBoxTitle {
background: %(incontextBoxBodyBg)s;
display: block;
font-weight: bold;
+ border-top-left-radius: 6px;
+ border-top-right-radius: 6px;
}
div.sideBox {
margin-bottom: 1em;
+ border-top-left-radius: 6px;
+ border-top-right-radius: 6px;
}
ul.sideBox,
@@ -403,6 +402,8 @@
div.boxTitle {
overflow: hidden;
font-weight: bold;
+ border-top-left-radius: 6px;
+ border-top-right-radius: 6px;
}
div.boxTitle span {
@@ -467,7 +468,10 @@
#navColumnLeft div.boxFooter, #navColumnRight div.boxFooter{
height: 14px;
- background: url("shadow.gif") no-repeat top right;
+}
+
+.boxBody, .boxTitle, #pageContent, #appMsg {
+ box-shadow: 1px 1px 3px Gray;
}
/* boxes lists and menus */
diff -r 2790fb8f7e03 -r e83cbc116352 web/data/cubicweb.facets.css
--- a/web/data/cubicweb.facets.css Fri Jun 21 14:22:39 2013 +0200
+++ b/web/data/cubicweb.facets.css Fri Jan 10 17:12:20 2014 +0100
@@ -8,6 +8,8 @@
background: #fff;
padding: %(facet_Padding)s;
margin-bottom: %(facet_MarginBottom)s;
+ border-top-left-radius: 5px;
+ border-bottom-right-radius: 7px;
}
.facetGroup {
diff -r 2790fb8f7e03 -r e83cbc116352 web/data/cubicweb.facets.js
--- a/web/data/cubicweb.facets.js Fri Jun 21 14:22:39 2013 +0200
+++ b/web/data/cubicweb.facets.js Fri Jan 10 17:12:20 2014 +0100
@@ -169,7 +169,6 @@
if ($('#'+divid).length) {
var $loadingDiv = $(DIV({id:'facetLoading'},
facetLoadingMsg));
- $loadingDiv.corner();
$($('#'+divid).get(0).parentNode).append($loadingDiv);
}
form.find('div.facet').each(function() {
@@ -327,7 +326,6 @@
if ($('div.facetBody').length) {
var $loadingDiv = $(DIV({id:'facetLoading'},
facetLoadingMsg));
- $loadingDiv.corner();
$('body').append($loadingDiv);
}
});
diff -r 2790fb8f7e03 -r e83cbc116352 web/data/cubicweb.htmlhelpers.js
--- a/web/data/cubicweb.htmlhelpers.js Fri Jun 21 14:22:39 2013 +0200
+++ b/web/data/cubicweb.htmlhelpers.js Fri Jan 10 17:12:20 2014 +0100
@@ -78,10 +78,10 @@
// generate a list of couple key=value if key is multivalued
if (cw.utils.isArrayLike(value)) {
for (var i = 0; i < value.length; i++) {
- chunks.push(key + '=' + urlEncode(value[i]));
+ chunks.push(key + '=' + cw.urlEncode(value[i]));
}
} else {
- chunks.push(key + '=' + urlEncode(value));
+ chunks.push(key + '=' + cw.urlEncode(value));
}
}
return chunks.join('&');
@@ -195,19 +195,4 @@
}
}
}
-//============= page loading events ==========================================//
-cw.rounded = [['div.sideBoxBody', 'bottom 6px'],
- ['div.boxTitle, div.sideBoxTitle, th.month', 'top 6px']];
-function roundedCorners(node) {
- if (jQuery.fn.corner !== undefined) {
- node = jQuery(node);
- for (var r = 0; r < cw.rounded.length; r++) {
- node.find(cw.rounded[r][0]).corner(cw.rounded[r][1]);
- }
- }
-}
-
-jQuery(document).ready(function() {
- roundedCorners(this.body);
-});
diff -r 2790fb8f7e03 -r e83cbc116352 web/data/cubicweb.js
--- a/web/data/cubicweb.js Fri Jun 21 14:22:39 2013 +0200
+++ b/web/data/cubicweb.js Fri Jan 10 17:12:20 2014 +0100
@@ -45,6 +45,15 @@
return null;
},
+ // escapes string selectors (e.g. "foo.[subject]:42" -> "foo\.\[subject\]\:42"
+ escape: function(selector) {
+ if (typeof(selector) == 'string') {
+ return selector.replace( /(:|\.|\[|\])/g, "\\$1" );
+ }
+ // cw.log('non string selector', selector);
+ return '';
+ },
+
getNode: function (node) {
if (typeof(node) == 'string') {
return document.getElementById(node);
@@ -105,15 +114,6 @@
};
},
- movedToNamespace: function (funcnames, namespace) {
- for (var i = 0; i < funcnames.length; i++) {
- var funcname = funcnames[i];
- var msg = ('[3.9] ' + funcname + ' is deprecated, use ' +
- namespace.__name__ + '.' + funcname + ' instead');
- window[funcname] = cw.utils.deprecatedFunction(msg, namespace[funcname]);
- }
- },
-
createDomFunction: function (tag) {
function builddom(params, children) {
var node = document.createElement(tag);
@@ -388,14 +388,6 @@
});
-String.prototype.startsWith = cw.utils.deprecatedFunction('[3.9] str.startsWith() is deprecated, use str.startswith() instead', function (prefix) {
- return this.startswith(prefix);
-});
-
-String.prototype.endsWith = cw.utils.deprecatedFunction('[3.9] str.endsWith() is deprecated, use str.endswith() instead', function (suffix) {
- return this.endswith(prefix);
-});
-
/** DOM factories ************************************************************/
A = cw.utils.createDomFunction('a');
BUTTON = cw.utils.createDomFunction('button');
@@ -472,7 +464,8 @@
return node;
}
-// XXX avoid crashes / backward compat
+// cubes: tag, keyword and apycot seem to use this, including require/provide
+// backward compat
CubicWeb = cw;
jQuery.extend(cw, {
diff -r 2790fb8f7e03 -r e83cbc116352 web/data/cubicweb.old.css
--- a/web/data/cubicweb.old.css Fri Jun 21 14:22:39 2013 +0200
+++ b/web/data/cubicweb.old.css Fri Jan 10 17:12:20 2014 +0100
@@ -411,13 +411,14 @@
div.shadow{
height: 14px;
- background: url("shadow.gif") no-repeat top right;
}
div.sideBoxTitle {
background: #cfceb7;
display: block;
font: bold 100% Georgia;
+ border-top-left-radius: 6px;
+ border-top-right-radius: 6px;
}
div.sideBox {
@@ -434,6 +435,8 @@
div.sideBoxBody {
padding: 0.2em 5px;
background: #eeedd9;
+ border-bottom-left-radius: 6px;
+ border-bottom-right-radius: 6px;
}
div.sideBoxBody a {
@@ -457,6 +460,8 @@
div.boxTitle {
overflow: hidden;
font-weight: bold;
+ border-top-left-radius: 6px;
+ border-top-right-radius: 6px;
}
div.boxTitle span {
@@ -521,7 +526,14 @@
#navColumnLeft div.boxFooter, #navColumnRight div.boxFooter{
height: 14px;
- background: url("shadow.gif") no-repeat top right;
+}
+
+.navboxes {
+ padding: 2px;
+}
+
+.boxBody, .boxTitle, #pageContent, #appMsg {
+ box-shadow: 1px 1px 3px Gray;
}
/* boxes lists and menus */
diff -r 2790fb8f7e03 -r e83cbc116352 web/data/cubicweb.pictograms.css
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/data/cubicweb.pictograms.css Fri Jan 10 17:12:20 2014 +0100
@@ -0,0 +1,343 @@
+/*The included Entypo font have been created by Daniel Bruce (www.entypo.com) as
+proposed by fontello (https://github.com/fontello/entypo).
+The Entypo pictograms are licensed under CC BY 3.0 and the font under
+SIL Open Font License.*/
+@font-face {
+ font-family: 'entypo';
+ src: url('entypo.eot?8644018');
+ src: url('entypo.eot?8644018#iefix') format('embedded-opentype'),
+ url('entypo.woff?8644018') format('woff'),
+ url('entypo.ttf?8644018') format('truetype'),
+ url('entypo.svg?8644018#entypo') format('svg');
+ font-weight: normal;
+ font-style: normal;
+}
+/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */
+/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */
+/*
+@media screen and (-webkit-min-device-pixel-ratio:0) {
+ @font-face {
+ font-family: 'entypo';
+ src: url('../font/entypo.svg?8644018#entypo') format('svg');
+ }
+}
+*/
+
+[class^="icon-"]:before,
+[class*=" icon-"]:before {
+ font-family: "entypo";
+ font-style: normal;
+ font-weight: normal;
+ speak: none;
+
+ display: inline-block;
+ text-decoration: inherit;
+ width: 1em;
+ margin-right: .1em;
+ text-align: center;
+ /* opacity: .8; */
+
+ /* For safety - reset parent styles, that can break glyph codes*/
+ font-variant: normal;
+ text-transform: none;
+
+ /* fix buttons height, for twitter bootstrap */
+ line-height: 1em;
+
+ /* Animation center compensation - magrins should be symmetric */
+ /* remove if not needed */
+ margin-left: .1em;
+
+ /* you can be more comfortable with increased icons size */
+ font-size: 160%;
+ vertical-align: middle;
+
+ /* Uncomment for 3D effect */
+ /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */
+}
+
+.icon-note:before { content: '\266a'; } /* '♪' */
+.icon-note-beamed:before { content: '\266b'; } /* '♫' */
+.icon-music:before { content: '\1f3b5'; } /* '\1f3b5' */
+.icon-search:before { content: '\1f50d'; } /* '\1f50d' */
+.icon-flashlight:before { content: '\1f526'; } /* '\1f526' */
+.icon-mail:before { content: '\2709'; } /* '✉' */
+.icon-heart:before { content: '\2665'; } /* '♥' */
+.icon-heart-empty:before { content: '\2661'; } /* '♡' */
+.icon-star:before { content: '\2605'; } /* '★' */
+.icon-star-empty:before { content: '\2606'; } /* '☆' */
+.icon-user:before { content: '\1f464'; }
+.icon-users:before { content: '\1f465'; }
+.icon-user-add:before { content: '\e700'; } /* '' */
+.icon-video:before { content: '\1f3ac'; }
+.icon-picture:before { content: '\1f304'; }
+.icon-camera:before { content: '\1f4f7'; }
+.icon-layout:before { content: '\268f'; } /* '⚏' */
+.icon-menu:before { content: '\2630'; } /* '☰' */
+.icon-check:before { content: '\2713'; } /* '✓' */
+.icon-cancel:before { content: '\2715'; } /* '✕' */
+.icon-cancel-circled:before { content: '\2716'; } /* '✖' */
+.icon-cancel-squared:before { content: '\274e'; } /* '❎' */
+.icon-plus:before { content: '\2b'; } /* '+' */
+.icon-plus-circled:before { content: '\2795'; } /* '➕' */
+.icon-plus-squared:before { content: '\229e'; } /* '⊞' */
+.icon-minus:before { content: '\2d'; } /* '-' */
+.icon-minus-circled:before { content: '\2796'; } /* '➖' */
+.icon-minus-squared:before { content: '\229f'; } /* '⊟' */
+.icon-help:before { content: '\2753'; } /* '❓' */
+.icon-help-circled:before { content: '\e704'; } /* '' */
+.icon-info:before { content: '\2139'; } /* 'ℹ' */
+.icon-info-circled:before { content: '\e705'; } /* '' */
+.icon-back:before { content: '\1f519'; }
+.icon-home:before { content: '\2302'; } /* '⌂' */
+.icon-link:before { content: '\1f517'; }
+.icon-attach:before { content: '\1f4ce'; }
+.icon-lock:before { content: '\1f512'; }
+.icon-lock-open:before { content: '\1f513'; }
+.icon-eye:before { content: '\e70a'; } /* '' */
+.icon-tag:before { content: '\e70c'; } /* '' */
+.icon-bookmark:before { content: '\1f516'; }
+.icon-bookmarks:before { content: '\1f4d1'; }
+.icon-flag:before { content: '\2691'; } /* '⚑' */
+.icon-thumbs-up:before { content: '\1f44d'; }
+.icon-thumbs-down:before { content: '\1f44e'; }
+.icon-download:before { content: '\1f4e5'; }
+.icon-upload:before { content: '\1f4e4'; }
+.icon-upload-cloud:before { content: '\e711'; } /* '' */
+.icon-reply:before { content: '\e712'; } /* '' */
+.icon-reply-all:before { content: '\e713'; } /* '' */
+.icon-forward:before { content: '\27a6'; } /* '➦' */
+.icon-quote:before { content: '\275e'; } /* '❞' */
+.icon-code:before { content: '\e714'; } /* '' */
+.icon-export:before { content: '\e715'; } /* '' */
+.icon-pencil:before { content: '\270e'; } /* '✎' */
+.icon-feather:before { content: '\2712'; } /* '✒' */
+.icon-print:before { content: '\e716'; } /* '' */
+.icon-retweet:before { content: '\e717'; } /* '' */
+.icon-keyboard:before { content: '\2328'; } /* '⌨' */
+.icon-comment:before { content: '\e718'; } /* '' */
+.icon-chat:before { content: '\e720'; } /* '' */
+.icon-bell:before { content: '\1f514'; }
+.icon-attention:before { content: '\26a0'; } /* '⚠' */
+.icon-alert:before { content: '\1f4a5'; }
+.icon-vcard:before { content: '\e722'; } /* '' */
+.icon-address:before { content: '\e723'; } /* '' */
+.icon-location:before { content: '\e724'; } /* '' */
+.icon-map:before { content: '\e727'; } /* '' */
+.icon-direction:before { content: '\27a2'; } /* '➢' */
+.icon-compass:before { content: '\e728'; } /* '' */
+.icon-cup:before { content: '\2615'; } /* '☕' */
+.icon-trash:before { content: '\e729'; } /* '' */
+.icon-doc:before { content: '\e730'; } /* '' */
+.icon-docs:before { content: '\e736'; } /* '' */
+.icon-doc-landscape:before { content: '\e737'; } /* '' */
+.icon-doc-text:before { content: '\1f4c4'; }
+.icon-doc-text-inv:before { content: '\e731'; } /* '' */
+.icon-newspaper:before { content: '\1f4f0'; }
+.icon-book-open:before { content: '\1f4d6'; }
+.icon-book:before { content: '\1f4d5'; }
+.icon-folder:before { content: '\1f4c1'; }
+.icon-archive:before { content: '\e738'; } /* '' */
+.icon-box:before { content: '\1f4e6'; }
+.icon-rss:before { content: '\e73a'; } /* '' */
+.icon-phone:before { content: '\1f4de'; }
+.icon-cog:before { content: '\2699'; } /* '⚙' */
+.icon-tools:before { content: '\2692'; } /* '⚒' */
+.icon-share:before { content: '\e73c'; } /* '' */
+.icon-shareable:before { content: '\e73e'; } /* '' */
+.icon-basket:before { content: '\e73d'; } /* '' */
+.icon-bag:before { content: '\1f45c'; }
+.icon-calendar:before { content: '\1f4c5'; }
+.icon-login:before { content: '\e740'; } /* '' */
+.icon-logout:before { content: '\e741'; } /* '' */
+.icon-mic:before { content: '\1f3a4'; }
+.icon-mute:before { content: '\1f507'; }
+.icon-sound:before { content: '\1f50a'; }
+.icon-volume:before { content: '\e742'; } /* '' */
+.icon-clock:before { content: '\1f554'; }
+.icon-hourglass:before { content: '\23f3'; } /* '⏳' */
+.icon-lamp:before { content: '\1f4a1'; }
+.icon-light-down:before { content: '\1f505'; }
+.icon-light-up:before { content: '\1f506'; }
+.icon-adjust:before { content: '\25d1'; } /* '◑' */
+.icon-block:before { content: '\1f6ab'; }
+.icon-resize-full:before { content: '\e744'; } /* '' */
+.icon-resize-small:before { content: '\e746'; } /* '' */
+.icon-popup:before { content: '\e74c'; } /* '' */
+.icon-publish:before { content: '\e74d'; } /* '' */
+.icon-window:before { content: '\e74e'; } /* '' */
+.icon-arrow-combo:before { content: '\e74f'; } /* '' */
+.icon-down-circled:before { content: '\e758'; } /* '' */
+.icon-left-circled:before { content: '\e759'; } /* '' */
+.icon-right-circled:before { content: '\e75a'; } /* '' */
+.icon-up-circled:before { content: '\e75b'; } /* '' */
+.icon-down-open:before { content: '\e75c'; } /* '' */
+.icon-left-open:before { content: '\e75d'; } /* '' */
+.icon-right-open:before { content: '\e75e'; } /* '' */
+.icon-up-open:before { content: '\e75f'; } /* '' */
+.icon-down-open-mini:before { content: '\e760'; } /* '' */
+.icon-left-open-mini:before { content: '\e761'; } /* '' */
+.icon-right-open-mini:before { content: '\e762'; } /* '' */
+.icon-up-open-mini:before { content: '\e763'; } /* '' */
+.icon-down-open-big:before { content: '\e764'; } /* '' */
+.icon-left-open-big:before { content: '\e765'; } /* '' */
+.icon-right-open-big:before { content: '\e766'; } /* '' */
+.icon-up-open-big:before { content: '\e767'; } /* '' */
+.icon-down:before { content: '\2b07'; } /* '⬇' */
+.icon-left:before { content: '\2b05'; } /* '⬅' */
+.icon-right:before { content: '\27a1'; } /* '➡' */
+.icon-up:before { content: '\2b06'; } /* '⬆' */
+.icon-down-dir:before { content: '\25be'; } /* '▾' */
+.icon-left-dir:before { content: '\25c2'; } /* '◂' */
+.icon-right-dir:before { content: '\25b8'; } /* '▸' */
+.icon-up-dir:before { content: '\25b4'; } /* '▴' */
+.icon-down-bold:before { content: '\e4b0'; } /* '' */
+.icon-left-bold:before { content: '\e4ad'; } /* '' */
+.icon-right-bold:before { content: '\e4ae'; } /* '' */
+.icon-up-bold:before { content: '\e4af'; } /* '' */
+.icon-down-thin:before { content: '\2193'; } /* '↓' */
+.icon-left-thin:before { content: '\2190'; } /* '←' */
+.icon-right-thin:before { content: '\2192'; } /* '→' */
+.icon-up-thin:before { content: '\2191'; } /* '↑' */
+.icon-ccw:before { content: '\27f2'; } /* '⟲' */
+.icon-cw:before { content: '\27f3'; } /* '⟳' */
+.icon-arrows-ccw:before { content: '\1f504'; }
+.icon-level-down:before { content: '\21b3'; } /* '↳' */
+.icon-level-up:before { content: '\21b0'; } /* '↰' */
+.icon-shuffle:before { content: '\1f500'; }
+.icon-loop:before { content: '\1f501'; }
+.icon-switch:before { content: '\21c6'; } /* '⇆' */
+.icon-play:before { content: '\25b6'; } /* '▶' */
+.icon-stop:before { content: '\25a0'; } /* '■' */
+.icon-pause:before { content: '\2389'; } /* '⎉' */
+.icon-record:before { content: '\26ab'; } /* '⚫' */
+.icon-to-end:before { content: '\23ed'; } /* '⏭' */
+.icon-to-start:before { content: '\23ee'; } /* '⏮' */
+.icon-fast-forward:before { content: '\23e9'; } /* '⏩' */
+.icon-fast-backward:before { content: '\23ea'; } /* '⏪' */
+.icon-progress-0:before { content: '\e768'; } /* '' */
+.icon-progress-1:before { content: '\e769'; } /* '' */
+.icon-progress-2:before { content: '\e76a'; } /* '' */
+.icon-progress-3:before { content: '\e76b'; } /* '' */
+.icon-target:before { content: '\1f3af'; }
+.icon-palette:before { content: '\1f3a8'; }
+.icon-list:before { content: '\e005'; } /* '' */
+.icon-list-add:before { content: '\e003'; } /* '' */
+.icon-signal:before { content: '\1f4f6'; }
+.icon-trophy:before { content: '\1f3c6'; }
+.icon-battery:before { content: '\1f50b'; }
+.icon-back-in-time:before { content: '\e771'; } /* '' */
+.icon-monitor:before { content: '\1f4bb'; }
+.icon-mobile:before { content: '\1f4f1'; }
+.icon-network:before { content: '\e776'; } /* '' */
+.icon-cd:before { content: '\1f4bf'; }
+.icon-inbox:before { content: '\e777'; } /* '' */
+.icon-install:before { content: '\e778'; } /* '' */
+.icon-globe:before { content: '\1f30e'; }
+.icon-cloud:before { content: '\2601'; } /* '☁' */
+.icon-cloud-thunder:before { content: '\26c8'; } /* '⛈' */
+.icon-flash:before { content: '\26a1'; } /* '⚡' */
+.icon-moon:before { content: '\263d'; } /* '☽' */
+.icon-flight:before { content: '\2708'; } /* '✈' */
+.icon-paper-plane:before { content: '\e79b'; } /* '' */
+.icon-leaf:before { content: '\1f342'; }
+.icon-lifebuoy:before { content: '\e788'; } /* '' */
+.icon-mouse:before { content: '\e789'; } /* '' */
+.icon-briefcase:before { content: '\1f4bc'; }
+.icon-suitcase:before { content: '\e78e'; } /* '' */
+.icon-dot:before { content: '\e78b'; } /* '' */
+.icon-dot-2:before { content: '\e78c'; } /* '' */
+.icon-dot-3:before { content: '\e78d'; } /* '' */
+.icon-brush:before { content: '\e79a'; } /* '' */
+.icon-magnet:before { content: '\e7a1'; } /* '' */
+.icon-infinity:before { content: '\221e'; } /* '∞' */
+.icon-erase:before { content: '\232b'; } /* '⌫' */
+.icon-chart-pie:before { content: '\e751'; } /* '' */
+.icon-chart-line:before { content: '\1f4c8'; }
+.icon-chart-bar:before { content: '\1f4ca'; }
+.icon-chart-area:before { content: '\1f53e'; }
+.icon-tape:before { content: '\2707'; } /* '✇' */
+.icon-graduation-cap:before { content: '\1f393'; }
+.icon-language:before { content: '\e752'; } /* '' */
+.icon-ticket:before { content: '\1f3ab'; }
+.icon-water:before { content: '\1f4a6'; }
+.icon-droplet:before { content: '\1f4a7'; }
+.icon-air:before { content: '\e753'; } /* '' */
+.icon-credit-card:before { content: '\1f4b3'; }
+.icon-floppy:before { content: '\1f4be'; }
+.icon-clipboard:before { content: '\1f4cb'; }
+.icon-megaphone:before { content: '\1f4e3'; }
+.icon-database:before { content: '\e754'; } /* '' */
+.icon-drive:before { content: '\e755'; } /* '' */
+.icon-bucket:before { content: '\e756'; } /* '' */
+.icon-thermometer:before { content: '\e757'; } /* '' */
+.icon-key:before { content: '\1f511'; }
+.icon-flow-cascade:before { content: '\e790'; } /* '' */
+.icon-flow-branch:before { content: '\e791'; } /* '' */
+.icon-flow-tree:before { content: '\e792'; } /* '' */
+.icon-flow-line:before { content: '\e793'; } /* '' */
+.icon-flow-parallel:before { content: '\e794'; } /* '' */
+.icon-rocket:before { content: '\1f680'; }
+.icon-gauge:before { content: '\e7a2'; } /* '' */
+.icon-traffic-cone:before { content: '\e7a3'; } /* '' */
+.icon-cc:before { content: '\e7a5'; } /* '' */
+.icon-cc-by:before { content: '\e7a6'; } /* '' */
+.icon-cc-nc:before { content: '\e7a7'; } /* '' */
+.icon-cc-nc-eu:before { content: '\e7a8'; } /* '' */
+.icon-cc-nc-jp:before { content: '\e7a9'; } /* '' */
+.icon-cc-sa:before { content: '\e7aa'; } /* '' */
+.icon-cc-nd:before { content: '\e7ab'; } /* '' */
+.icon-cc-pd:before { content: '\e7ac'; } /* '' */
+.icon-cc-zero:before { content: '\e7ad'; } /* '' */
+.icon-cc-share:before { content: '\e7ae'; } /* '' */
+.icon-cc-remix:before { content: '\e7af'; } /* '' */
+.icon-github:before { content: '\f300'; } /* '' */
+.icon-github-circled:before { content: '\f301'; } /* '' */
+.icon-flickr:before { content: '\f303'; } /* '' */
+.icon-flickr-circled:before { content: '\f304'; } /* '' */
+.icon-vimeo:before { content: '\f306'; } /* '' */
+.icon-vimeo-circled:before { content: '\f307'; } /* '' */
+.icon-twitter:before { content: '\f309'; } /* '' */
+.icon-twitter-circled:before { content: '\f30a'; } /* '' */
+.icon-facebook:before { content: '\f30c'; } /* '' */
+.icon-facebook-circled:before { content: '\f30d'; } /* '' */
+.icon-facebook-squared:before { content: '\f30e'; } /* '' */
+.icon-gplus:before { content: '\f30f'; } /* '' */
+.icon-gplus-circled:before { content: '\f310'; } /* '' */
+.icon-pinterest:before { content: '\f312'; } /* '' */
+.icon-pinterest-circled:before { content: '\f313'; } /* '' */
+.icon-tumblr:before { content: '\f315'; } /* '' */
+.icon-tumblr-circled:before { content: '\f316'; } /* '' */
+.icon-linkedin:before { content: '\f318'; } /* '' */
+.icon-linkedin-circled:before { content: '\f319'; } /* '' */
+.icon-dribbble:before { content: '\f31b'; } /* '' */
+.icon-dribbble-circled:before { content: '\f31c'; } /* '' */
+.icon-stumbleupon:before { content: '\f31e'; } /* '' */
+.icon-stumbleupon-circled:before { content: '\f31f'; } /* '' */
+.icon-lastfm:before { content: '\f321'; } /* '' */
+.icon-lastfm-circled:before { content: '\f322'; } /* '' */
+.icon-rdio:before { content: '\f324'; } /* '' */
+.icon-rdio-circled:before { content: '\f325'; } /* '' */
+.icon-spotify:before { content: '\f327'; } /* '' */
+.icon-spotify-circled:before { content: '\f328'; } /* '' */
+.icon-qq:before { content: '\f32a'; } /* '' */
+.icon-instagram:before { content: '\f32d'; } /* '' */
+.icon-dropbox:before { content: '\f330'; } /* '' */
+.icon-evernote:before { content: '\f333'; } /* '' */
+.icon-flattr:before { content: '\f336'; } /* '' */
+.icon-skype:before { content: '\f339'; } /* '' */
+.icon-skype-circled:before { content: '\f33a'; } /* '' */
+.icon-renren:before { content: '\f33c'; } /* '' */
+.icon-sina-weibo:before { content: '\f33f'; } /* '' */
+.icon-paypal:before { content: '\f342'; } /* '' */
+.icon-picasa:before { content: '\f345'; } /* '' */
+.icon-soundcloud:before { content: '\f348'; } /* '' */
+.icon-mixi:before { content: '\f34b'; } /* '' */
+.icon-behance:before { content: '\f34e'; } /* '' */
+.icon-google-circles:before { content: '\f351'; } /* '' */
+.icon-vkontakte:before { content: '\f354'; } /* '' */
+.icon-smashing:before { content: '\f357'; } /* '' */
+.icon-sweden:before { content: '\f601'; } /* '' */
+.icon-db-shape:before { content: '\f600'; } /* '' */
+.icon-logo-db:before { content: '\f603'; } /* '' */
+
diff -r 2790fb8f7e03 -r e83cbc116352 web/data/cubicweb.rhythm.js
--- a/web/data/cubicweb.rhythm.js Fri Jun 21 14:22:39 2013 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-$(document).ready(function() {
- $('a.rhythm').click(function (event){
- $('div#pageContent').toggleClass('rhythm_bg');
- $('div#page').toggleClass('rhythm_bg');
- event.preventDefault();
- });
-});
diff -r 2790fb8f7e03 -r e83cbc116352 web/data/cubicweb.widgets.js
--- a/web/data/cubicweb.widgets.js Fri Jun 21 14:22:39 2013 +0200
+++ b/web/data/cubicweb.widgets.js Fri Jan 10 17:12:20 2014 +0100
@@ -92,7 +92,8 @@
if (($(instanceData.userInput).attr('cubicweb:initialvalue') !== undefined) && !instanceData.hiddenInput){
hiHandlers.initializeHiddenInput(instanceData);
}
- $.ui.autocomplete.prototype._search = methods.search;
+ $.ui.autocomplete.prototype._value = methods._value;
+ $.data(this, 'settings', settings);
if (settings.multiple) {
$.ui.autocomplete.filter = methods.multiple.makeFilter(this);
$(this).bind({
@@ -125,6 +126,20 @@
});
},
+ _value: function() {
+ /* We extend the widget with the ability to lookup and
+ handle several terms at once ('multiple' option). E.g.:
+ toto, titi, tu.... The autocompletion must be
+ performed only on the last of such a list of terms.
+ */
+ var settings = $(this.element).data('settings');
+ var value = this.valueMethod.apply( this.element, arguments );
+ if (settings.multiple & arguments.length === 0) {
+ return extractLast(value);
+ }
+ return value
+ },
+
multiple: {
focus: function() {
// prevent value inserted on focus
@@ -140,7 +155,7 @@
return false;
},
keydown: function(evt) {
- if ($(this).data('autocomplete').menu.active && evt.keyCode == $.ui.keyCode.TAB) {
+ if (evt.keyCode == $.ui.keyCode.TAB) {
evt.preventDefault();
}
},
@@ -161,13 +176,7 @@
methods.resetValues(instanceData);
}
},
- search: function(value) {
- this.element.addClass("ui-autocomplete-loading");
- if (this.options.multiple) {
- value = extractLast(value);
- }
- this.source({term: value}, this.response);
- },
+
ensureExactMatch: function(evt) {
var instanceData = $(this).data('cwautocomplete');
if (evt.keyCode == $.ui.keyCode.ENTER || evt.keyCode == $.ui.keyCode.TAB) {
@@ -179,6 +188,7 @@
}
}
},
+
resetValues: function(instanceData){
$(instanceData.userInput).val('');
$(instanceData.hiddenInput).val('');
@@ -544,105 +554,77 @@
// IE things can not handle hide/show options on select, this cloned list solition (should propably have 2 widgets)
(function ($) {
- var defaultSettings = {
- bindDblClick: true
- };
+
var methods = {
- __init__: function(fromSelect, toSelect, options) {
- var settings = $.extend({}, defaultSettings, options);
- var bindDblClick = settings['bindDblClick'];
- var $fromNode = $(cw.jqNode(fromSelect));
- var clonedSelect = $fromNode.clone();
- var $toNode = $(cw.jqNode(toSelect));
- var $addButton = $(this.find('.cwinoutadd')[0]);
- var $removeButton = $(this.find('.cwinoutremove')[0]);
- // bind buttons
- var name = this.attr('id');
- var instanceData = {'fromNode':fromSelect,
- 'toNode':toSelect,
- 'cloned':clonedSelect,
- 'bindDblClick':bindDblClick,
- 'name': name};
- $addButton.bind('click', {'instanceData':instanceData}, methods.inOutWidgetAddValues);
- $removeButton.bind('click', {'instanceData':instanceData}, methods.inOutWidgetRemoveValues);
- if(bindDblClick){
- $toNode.bind('dblclick', {'instanceData': instanceData}, methods.inOutWidgetRemoveValues);
- }
- methods.inOutWidgetRemplaceSelect($fromNode, $toNode, clonedSelect, bindDblClick, name);
- },
+ __init__: function(fromSelect, toSelect) {
+ // closed over state
+ var state = {'$fromNode' : $(cw.escape('#' + fromSelect)),
+ '$toNode' : $(cw.escape('#' + toSelect)),
+ 'name' : this.attr('id')};
- inOutWidgetRemplaceSelect: function($fromNode, $toNode, clonedSelect, bindDblClick, name){
- var $newSelect = clonedSelect.clone();
- $toNode.find('option').each(function() {
- $newSelect.find('$(this)[value='+$(this).val()+']').remove();
- });
- var fromparent = $fromNode.parent();
- if (bindDblClick) {
- //XXX jQuery live binding does not seem to work here
- $newSelect.bind('dblclick', {'instanceData': {'fromNode':$fromNode.attr('id'),
- 'toNode': $toNode.attr('id'),
- 'cloned':clonedSelect,
- 'bindDblClick':bindDblClick,
- 'name': name}},
- methods.inOutWidgetAddValues);
- }
- $fromNode.remove();
- fromparent.append($newSelect);
- },
+ function sortoptions($optionlist) {
+ var $sorted = $optionlist.find('option').sort(function(opt1, opt2) {
+ return $(opt1).text() > $(opt2).text() ? 1 : -1;
+ });
+ // this somehow translates to an inplace sort
+ $optionlist.append($sorted);
+ };
+ sortoptions(state.$fromNode);
+ sortoptions(state.$toNode);
- inOutWidgetAddValues: function(event){
- var $fromNode = $(cw.jqNode(event.data.instanceData.fromNode));
- var $toNode = $(cw.jqNode(event.data.instanceData.toNode));
- $fromNode.find('option:selected').each(function() {
- var option = $(this);
- var newoption = OPTION({'value':option.val()},
- value=option.text());
- $toNode.append(newoption);
- var hiddenInput = INPUT({
- type: "hidden", name: event.data.instanceData.name,
- value:option.val()
+ // will move selected options from one list to the other
+ // and call an option handler on each option
+ function moveoptions ($fromlist, $tolist, opthandler) {
+ $fromlist.find('option:selected').each(function(index, option) {
+ var $option = $(option);
+ // add a new option to the target list
+ $tolist.append(OPTION({'value' : $option.val()},
+ $option.text()));
+ // process callback on the option
+ opthandler.call(null, $option);
+ // remove option from the source list
+ $option.remove();
});
- $toNode.parent().append(hiddenInput);
- });
- methods.inOutWidgetRemplaceSelect($fromNode, $toNode, event.data.instanceData.cloned,
- event.data.instanceData.bindDblClick,
- event.data.instanceData.name);
- // for ie 7 : ie does not resize correctly the select
- if($.browser.msie && $.browser.version.substr(0,1) < 8){
- var p = $toNode.parent();
- var newtoNode = $toNode.clone();
- if (event.data.instanceData.bindDblClick) {
- newtoNode.bind('dblclick', {'fromNode': $fromNode.attr('id'),
- 'toNode': $toNode.attr('id'),
- 'cloned': event.data.instanceData.cloned,
- 'bindDblClick': true,
- 'name': event.data.instanceData.name},
- methods.inOutWidgetRemoveValues);
- }
- $toNode.remove();
- p.append(newtoNode);
- }
- },
+ // re-sort both lists
+ sortoptions($fromlist);
+ sortoptions($tolist);
+ };
+
+ function addvalues () {
+ moveoptions(state.$fromNode, state.$toNode, function ($option) {
+ // add an hidden input for the edit controller
+ var hiddenInput = INPUT({
+ type: 'hidden', name: state.name,
+ value : $option.val()
+ });
+ state.$toNode.parent().append(hiddenInput);
+ });
+ };
- inOutWidgetRemoveValues: function(event){
- var $fromNode = $(cw.jqNode(event.data.instanceData.toNode));
- var $toNode = $(cw.jqNode(event.data.instanceData.fromNode));
- var name = event.data.instanceData.name.replace(':', '\\:');
- $fromNode.find('option:selected').each(function(){
- var option = $(this);
- var newoption = OPTION({'value':option.val()},
- value=option.text());
- option.remove();
- $fromNode.parent().find('input[name]='+ name).each(function() {
- $(this).val()==option.val()?$(this).remove():null;
- });
- });
- methods.inOutWidgetRemplaceSelect($toNode, $fromNode, event.data.instanceData.cloned,
- event.data.instanceData.bindDblClick,
- event.data.instanceData.name);
+ function removevalues () {
+ moveoptions(state.$toNode, state.$fromNode, function($option) {
+ // remove hidden inputs for the edit controller
+ var selector = 'input[name=' + cw.escape(state.name) + ']'
+ state.$toNode.parent().find(selector).each(function(index, input) {
+ if ($(input).val() == $option.val()) {
+ $(input).remove();
+ }
+ });
+ });
+ };
+
+ var $this = $(this);
+ $this.find('.cwinoutadd').bind( // 'add >>>' symbol
+ 'click', {'state' : state}, addvalues);
+ $this.find('.cwinoutremove').bind( // 'remove <<<' symbol
+ 'click', {'state' : state}, removevalues);
+
+ state.$fromNode.bind('dblclick', {'state': state}, addvalues);
+ state.$toNode.bind('dblclick', {'state': state}, removevalues);
+
}
};
- $.fn.cwinoutwidget = function(fromSelect, toSelect, options){
- return methods.__init__.apply(this, [fromSelect, toSelect, options]);
+ $.fn.cwinoutwidget = function(fromSelect, toSelect) {
+ return methods.__init__.apply(this, [fromSelect, toSelect]);
};
-})(jQuery);
\ No newline at end of file
+})(jQuery);
diff -r 2790fb8f7e03 -r e83cbc116352 web/data/entypo.eot
Binary file web/data/entypo.eot has changed
diff -r 2790fb8f7e03 -r e83cbc116352 web/data/entypo.svg
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/data/entypo.svg Fri Jan 10 17:12:20 2014 +0100
@@ -0,0 +1,295 @@
+
+
+
+Copyright (C) 2012 by Daniel Bruce
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff -r 2790fb8f7e03 -r e83cbc116352 web/data/entypo.ttf
Binary file web/data/entypo.ttf has changed
diff -r 2790fb8f7e03 -r e83cbc116352 web/data/entypo.woff
Binary file web/data/entypo.woff has changed
diff -r 2790fb8f7e03 -r e83cbc116352 web/data/jquery-migrate.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/data/jquery-migrate.js Fri Jan 10 17:12:20 2014 +0100
@@ -0,0 +1,521 @@
+/*!
+ * jQuery Migrate - v1.2.1 - 2013-05-08
+ * https://github.com/jquery/jquery-migrate
+ * Copyright 2005, 2013 jQuery Foundation, Inc. and other contributors; Licensed MIT
+ */
+(function( jQuery, window, undefined ) {
+// See http://bugs.jquery.com/ticket/13335
+// "use strict";
+
+
+var warnedAbout = {};
+
+// List of warnings already given; public read only
+jQuery.migrateWarnings = [];
+
+// Set to true to prevent console output; migrateWarnings still maintained
+// jQuery.migrateMute = false;
+
+// Show a message on the console so devs know we're active
+if ( !jQuery.migrateMute && window.console && window.console.log ) {
+ window.console.log("JQMIGRATE: Logging is active");
+}
+
+// Set to false to disable traces that appear with warnings
+if ( jQuery.migrateTrace === undefined ) {
+ jQuery.migrateTrace = true;
+}
+
+// Forget any warnings we've already given; public
+jQuery.migrateReset = function() {
+ warnedAbout = {};
+ jQuery.migrateWarnings.length = 0;
+};
+
+function migrateWarn( msg) {
+ var console = window.console;
+ if ( !warnedAbout[ msg ] ) {
+ warnedAbout[ msg ] = true;
+ jQuery.migrateWarnings.push( msg );
+ if ( console && console.warn && !jQuery.migrateMute ) {
+ console.warn( "JQMIGRATE: " + msg );
+ if ( jQuery.migrateTrace && console.trace ) {
+ console.trace();
+ }
+ }
+ }
+}
+
+function migrateWarnProp( obj, prop, value, msg ) {
+ if ( Object.defineProperty ) {
+ // On ES5 browsers (non-oldIE), warn if the code tries to get prop;
+ // allow property to be overwritten in case some other plugin wants it
+ try {
+ Object.defineProperty( obj, prop, {
+ configurable: true,
+ enumerable: true,
+ get: function() {
+ migrateWarn( msg );
+ return value;
+ },
+ set: function( newValue ) {
+ migrateWarn( msg );
+ value = newValue;
+ }
+ });
+ return;
+ } catch( err ) {
+ // IE8 is a dope about Object.defineProperty, can't warn there
+ }
+ }
+
+ // Non-ES5 (or broken) browser; just set the property
+ jQuery._definePropertyBroken = true;
+ obj[ prop ] = value;
+}
+
+if ( document.compatMode === "BackCompat" ) {
+ // jQuery has never supported or tested Quirks Mode
+ migrateWarn( "jQuery is not compatible with Quirks Mode" );
+}
+
+
+var attrFn = jQuery( " ", { size: 1 } ).attr("size") && jQuery.attrFn,
+ oldAttr = jQuery.attr,
+ valueAttrGet = jQuery.attrHooks.value && jQuery.attrHooks.value.get ||
+ function() { return null; },
+ valueAttrSet = jQuery.attrHooks.value && jQuery.attrHooks.value.set ||
+ function() { return undefined; },
+ rnoType = /^(?:input|button)$/i,
+ rnoAttrNodeType = /^[238]$/,
+ rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
+ ruseDefault = /^(?:checked|selected)$/i;
+
+// jQuery.attrFn
+migrateWarnProp( jQuery, "attrFn", attrFn || {}, "jQuery.attrFn is deprecated" );
+
+jQuery.attr = function( elem, name, value, pass ) {
+ var lowerName = name.toLowerCase(),
+ nType = elem && elem.nodeType;
+
+ if ( pass ) {
+ // Since pass is used internally, we only warn for new jQuery
+ // versions where there isn't a pass arg in the formal params
+ if ( oldAttr.length < 4 ) {
+ migrateWarn("jQuery.fn.attr( props, pass ) is deprecated");
+ }
+ if ( elem && !rnoAttrNodeType.test( nType ) &&
+ (attrFn ? name in attrFn : jQuery.isFunction(jQuery.fn[name])) ) {
+ return jQuery( elem )[ name ]( value );
+ }
+ }
+
+ // Warn if user tries to set `type`, since it breaks on IE 6/7/8; by checking
+ // for disconnected elements we don't warn on $( "", { type: "button" } ).
+ if ( name === "type" && value !== undefined && rnoType.test( elem.nodeName ) && elem.parentNode ) {
+ migrateWarn("Can't change the 'type' of an input or button in IE 6/7/8");
+ }
+
+ // Restore boolHook for boolean property/attribute synchronization
+ if ( !jQuery.attrHooks[ lowerName ] && rboolean.test( lowerName ) ) {
+ jQuery.attrHooks[ lowerName ] = {
+ get: function( elem, name ) {
+ // Align boolean attributes with corresponding properties
+ // Fall back to attribute presence where some booleans are not supported
+ var attrNode,
+ property = jQuery.prop( elem, name );
+ return property === true || typeof property !== "boolean" &&
+ ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ?
+
+ name.toLowerCase() :
+ undefined;
+ },
+ set: function( elem, value, name ) {
+ var propName;
+ if ( value === false ) {
+ // Remove boolean attributes when set to false
+ jQuery.removeAttr( elem, name );
+ } else {
+ // value is true since we know at this point it's type boolean and not false
+ // Set boolean attributes to the same name and set the DOM property
+ propName = jQuery.propFix[ name ] || name;
+ if ( propName in elem ) {
+ // Only set the IDL specifically if it already exists on the element
+ elem[ propName ] = true;
+ }
+
+ elem.setAttribute( name, name.toLowerCase() );
+ }
+ return name;
+ }
+ };
+
+ // Warn only for attributes that can remain distinct from their properties post-1.9
+ if ( ruseDefault.test( lowerName ) ) {
+ migrateWarn( "jQuery.fn.attr('" + lowerName + "') may use property instead of attribute" );
+ }
+ }
+
+ return oldAttr.call( jQuery, elem, name, value );
+};
+
+// attrHooks: value
+jQuery.attrHooks.value = {
+ get: function( elem, name ) {
+ var nodeName = ( elem.nodeName || "" ).toLowerCase();
+ if ( nodeName === "button" ) {
+ return valueAttrGet.apply( this, arguments );
+ }
+ if ( nodeName !== "input" && nodeName !== "option" ) {
+ migrateWarn("jQuery.fn.attr('value') no longer gets properties");
+ }
+ return name in elem ?
+ elem.value :
+ null;
+ },
+ set: function( elem, value ) {
+ var nodeName = ( elem.nodeName || "" ).toLowerCase();
+ if ( nodeName === "button" ) {
+ return valueAttrSet.apply( this, arguments );
+ }
+ if ( nodeName !== "input" && nodeName !== "option" ) {
+ migrateWarn("jQuery.fn.attr('value', val) no longer sets properties");
+ }
+ // Does not return so that setAttribute is also used
+ elem.value = value;
+ }
+};
+
+
+var matched, browser,
+ oldInit = jQuery.fn.init,
+ oldParseJSON = jQuery.parseJSON,
+ // Note: XSS check is done below after string is trimmed
+ rquickExpr = /^([^<]*)(<[\w\W]+>)([^>]*)$/;
+
+// $(html) "looks like html" rule change
+jQuery.fn.init = function( selector, context, rootjQuery ) {
+ var match;
+
+ if ( selector && typeof selector === "string" && !jQuery.isPlainObject( context ) &&
+ (match = rquickExpr.exec( jQuery.trim( selector ) )) && match[ 0 ] ) {
+ // This is an HTML string according to the "old" rules; is it still?
+ if ( selector.charAt( 0 ) !== "<" ) {
+ migrateWarn("$(html) HTML strings must start with '<' character");
+ }
+ if ( match[ 3 ] ) {
+ migrateWarn("$(html) HTML text after last tag is ignored");
+ }
+ // Consistently reject any HTML-like string starting with a hash (#9521)
+ // Note that this may break jQuery 1.6.x code that otherwise would work.
+ if ( match[ 0 ].charAt( 0 ) === "#" ) {
+ migrateWarn("HTML string cannot start with a '#' character");
+ jQuery.error("JQMIGRATE: Invalid selector string (XSS)");
+ }
+ // Now process using loose rules; let pre-1.8 play too
+ if ( context && context.context ) {
+ // jQuery object as context; parseHTML expects a DOM object
+ context = context.context;
+ }
+ if ( jQuery.parseHTML ) {
+ return oldInit.call( this, jQuery.parseHTML( match[ 2 ], context, true ),
+ context, rootjQuery );
+ }
+ }
+ return oldInit.apply( this, arguments );
+};
+jQuery.fn.init.prototype = jQuery.fn;
+
+// Let $.parseJSON(falsy_value) return null
+jQuery.parseJSON = function( json ) {
+ if ( !json && json !== null ) {
+ migrateWarn("jQuery.parseJSON requires a valid JSON string");
+ return null;
+ }
+ return oldParseJSON.apply( this, arguments );
+};
+
+jQuery.uaMatch = function( ua ) {
+ ua = ua.toLowerCase();
+
+ var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) ||
+ /(webkit)[ \/]([\w.]+)/.exec( ua ) ||
+ /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) ||
+ /(msie) ([\w.]+)/.exec( ua ) ||
+ ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) ||
+ [];
+
+ return {
+ browser: match[ 1 ] || "",
+ version: match[ 2 ] || "0"
+ };
+};
+
+// Don't clobber any existing jQuery.browser in case it's different
+if ( !jQuery.browser ) {
+ matched = jQuery.uaMatch( navigator.userAgent );
+ browser = {};
+
+ if ( matched.browser ) {
+ browser[ matched.browser ] = true;
+ browser.version = matched.version;
+ }
+
+ // Chrome is Webkit, but Webkit is also Safari.
+ if ( browser.chrome ) {
+ browser.webkit = true;
+ } else if ( browser.webkit ) {
+ browser.safari = true;
+ }
+
+ jQuery.browser = browser;
+}
+
+// Warn if the code tries to get jQuery.browser
+migrateWarnProp( jQuery, "browser", jQuery.browser, "jQuery.browser is deprecated" );
+
+jQuery.sub = function() {
+ function jQuerySub( selector, context ) {
+ return new jQuerySub.fn.init( selector, context );
+ }
+ jQuery.extend( true, jQuerySub, this );
+ jQuerySub.superclass = this;
+ jQuerySub.fn = jQuerySub.prototype = this();
+ jQuerySub.fn.constructor = jQuerySub;
+ jQuerySub.sub = this.sub;
+ jQuerySub.fn.init = function init( selector, context ) {
+ if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {
+ context = jQuerySub( context );
+ }
+
+ return jQuery.fn.init.call( this, selector, context, rootjQuerySub );
+ };
+ jQuerySub.fn.init.prototype = jQuerySub.fn;
+ var rootjQuerySub = jQuerySub(document);
+ migrateWarn( "jQuery.sub() is deprecated" );
+ return jQuerySub;
+};
+
+
+// Ensure that $.ajax gets the new parseJSON defined in core.js
+jQuery.ajaxSetup({
+ converters: {
+ "text json": jQuery.parseJSON
+ }
+});
+
+
+var oldFnData = jQuery.fn.data;
+
+jQuery.fn.data = function( name ) {
+ var ret, evt,
+ elem = this[0];
+
+ // Handles 1.7 which has this behavior and 1.8 which doesn't
+ if ( elem && name === "events" && arguments.length === 1 ) {
+ ret = jQuery.data( elem, name );
+ evt = jQuery._data( elem, name );
+ if ( ( ret === undefined || ret === evt ) && evt !== undefined ) {
+ migrateWarn("Use of jQuery.fn.data('events') is deprecated");
+ return evt;
+ }
+ }
+ return oldFnData.apply( this, arguments );
+};
+
+
+var rscriptType = /\/(java|ecma)script/i,
+ oldSelf = jQuery.fn.andSelf || jQuery.fn.addBack;
+
+jQuery.fn.andSelf = function() {
+ migrateWarn("jQuery.fn.andSelf() replaced by jQuery.fn.addBack()");
+ return oldSelf.apply( this, arguments );
+};
+
+// Since jQuery.clean is used internally on older versions, we only shim if it's missing
+if ( !jQuery.clean ) {
+ jQuery.clean = function( elems, context, fragment, scripts ) {
+ // Set context per 1.8 logic
+ context = context || document;
+ context = !context.nodeType && context[0] || context;
+ context = context.ownerDocument || context;
+
+ migrateWarn("jQuery.clean() is deprecated");
+
+ var i, elem, handleScript, jsTags,
+ ret = [];
+
+ jQuery.merge( ret, jQuery.buildFragment( elems, context ).childNodes );
+
+ // Complex logic lifted directly from jQuery 1.8
+ if ( fragment ) {
+ // Special handling of each script element
+ handleScript = function( elem ) {
+ // Check if we consider it executable
+ if ( !elem.type || rscriptType.test( elem.type ) ) {
+ // Detach the script and store it in the scripts array (if provided) or the fragment
+ // Return truthy to indicate that it has been handled
+ return scripts ?
+ scripts.push( elem.parentNode ? elem.parentNode.removeChild( elem ) : elem ) :
+ fragment.appendChild( elem );
+ }
+ };
+
+ for ( i = 0; (elem = ret[i]) != null; i++ ) {
+ // Check if we're done after handling an executable script
+ if ( !( jQuery.nodeName( elem, "script" ) && handleScript( elem ) ) ) {
+ // Append to fragment and handle embedded scripts
+ fragment.appendChild( elem );
+ if ( typeof elem.getElementsByTagName !== "undefined" ) {
+ // handleScript alters the DOM, so use jQuery.merge to ensure snapshot iteration
+ jsTags = jQuery.grep( jQuery.merge( [], elem.getElementsByTagName("script") ), handleScript );
+
+ // Splice the scripts into ret after their former ancestor and advance our index beyond them
+ ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) );
+ i += jsTags.length;
+ }
+ }
+ }
+ }
+
+ return ret;
+ };
+}
+
+var eventAdd = jQuery.event.add,
+ eventRemove = jQuery.event.remove,
+ eventTrigger = jQuery.event.trigger,
+ oldToggle = jQuery.fn.toggle,
+ oldLive = jQuery.fn.live,
+ oldDie = jQuery.fn.die,
+ ajaxEvents = "ajaxStart|ajaxStop|ajaxSend|ajaxComplete|ajaxError|ajaxSuccess",
+ rajaxEvent = new RegExp( "\\b(?:" + ajaxEvents + ")\\b" ),
+ rhoverHack = /(?:^|\s)hover(\.\S+|)\b/,
+ hoverHack = function( events ) {
+ if ( typeof( events ) !== "string" || jQuery.event.special.hover ) {
+ return events;
+ }
+ if ( rhoverHack.test( events ) ) {
+ migrateWarn("'hover' pseudo-event is deprecated, use 'mouseenter mouseleave'");
+ }
+ return events && events.replace( rhoverHack, "mouseenter$1 mouseleave$1" );
+ };
+
+// Event props removed in 1.9, put them back if needed; no practical way to warn them
+if ( jQuery.event.props && jQuery.event.props[ 0 ] !== "attrChange" ) {
+ jQuery.event.props.unshift( "attrChange", "attrName", "relatedNode", "srcElement" );
+}
+
+// Undocumented jQuery.event.handle was "deprecated" in jQuery 1.7
+if ( jQuery.event.dispatch ) {
+ migrateWarnProp( jQuery.event, "handle", jQuery.event.dispatch, "jQuery.event.handle is undocumented and deprecated" );
+}
+
+// Support for 'hover' pseudo-event and ajax event warnings
+jQuery.event.add = function( elem, types, handler, data, selector ){
+ if ( elem !== document && rajaxEvent.test( types ) ) {
+ migrateWarn( "AJAX events should be attached to document: " + types );
+ }
+ eventAdd.call( this, elem, hoverHack( types || "" ), handler, data, selector );
+};
+jQuery.event.remove = function( elem, types, handler, selector, mappedTypes ){
+ eventRemove.call( this, elem, hoverHack( types ) || "", handler, selector, mappedTypes );
+};
+
+jQuery.fn.error = function() {
+ var args = Array.prototype.slice.call( arguments, 0);
+ migrateWarn("jQuery.fn.error() is deprecated");
+ args.splice( 0, 0, "error" );
+ if ( arguments.length ) {
+ return this.bind.apply( this, args );
+ }
+ // error event should not bubble to window, although it does pre-1.7
+ this.triggerHandler.apply( this, args );
+ return this;
+};
+
+jQuery.fn.toggle = function( fn, fn2 ) {
+
+ // Don't mess with animation or css toggles
+ if ( !jQuery.isFunction( fn ) || !jQuery.isFunction( fn2 ) ) {
+ return oldToggle.apply( this, arguments );
+ }
+ migrateWarn("jQuery.fn.toggle(handler, handler...) is deprecated");
+
+ // Save reference to arguments for access in closure
+ var args = arguments,
+ guid = fn.guid || jQuery.guid++,
+ i = 0,
+ toggler = function( event ) {
+ // Figure out which function to execute
+ var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i;
+ jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 );
+
+ // Make sure that clicks stop
+ event.preventDefault();
+
+ // and execute the function
+ return args[ lastToggle ].apply( this, arguments ) || false;
+ };
+
+ // link all the functions, so any of them can unbind this click handler
+ toggler.guid = guid;
+ while ( i < args.length ) {
+ args[ i++ ].guid = guid;
+ }
+
+ return this.click( toggler );
+};
+
+jQuery.fn.live = function( types, data, fn ) {
+ migrateWarn("jQuery.fn.live() is deprecated");
+ if ( oldLive ) {
+ return oldLive.apply( this, arguments );
+ }
+ jQuery( this.context ).on( types, this.selector, data, fn );
+ return this;
+};
+
+jQuery.fn.die = function( types, fn ) {
+ migrateWarn("jQuery.fn.die() is deprecated");
+ if ( oldDie ) {
+ return oldDie.apply( this, arguments );
+ }
+ jQuery( this.context ).off( types, this.selector || "**", fn );
+ return this;
+};
+
+// Turn global events into document-triggered events
+jQuery.event.trigger = function( event, data, elem, onlyHandlers ){
+ if ( !elem && !rajaxEvent.test( event ) ) {
+ migrateWarn( "Global events are undocumented and deprecated" );
+ }
+ return eventTrigger.call( this, event, data, elem || document, onlyHandlers );
+};
+jQuery.each( ajaxEvents.split("|"),
+ function( _, name ) {
+ jQuery.event.special[ name ] = {
+ setup: function() {
+ var elem = this;
+
+ // The document needs no shimming; must be !== for oldIE
+ if ( elem !== document ) {
+ jQuery.event.add( document, name + "." + jQuery.guid, function() {
+ jQuery.event.trigger( name, null, elem, true );
+ });
+ jQuery._data( this, name, jQuery.guid++ );
+ }
+ return false;
+ },
+ teardown: function() {
+ if ( this !== document ) {
+ jQuery.event.remove( document, name + "." + jQuery._data( this, name ) );
+ }
+ return false;
+ }
+ };
+ }
+);
+
+
+})( jQuery, window );
diff -r 2790fb8f7e03 -r e83cbc116352 web/data/jquery.corner.js
--- a/web/data/jquery.corner.js Fri Jun 21 14:22:39 2013 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,247 +0,0 @@
-/*!
- * 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 = $('
').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 = $('
').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);
diff -r 2790fb8f7e03 -r e83cbc116352 web/data/jquery.js
--- a/web/data/jquery.js Fri Jun 21 14:22:39 2013 +0200
+++ b/web/data/jquery.js Fri Jan 10 17:12:20 2014 +0100
@@ -1,31 +1,38 @@
/*!
- * jQuery JavaScript Library v1.6.4
+ * jQuery JavaScript Library v1.10.2
* http://jquery.com/
*
- * Copyright 2011, John Resig
- * Dual licensed under the MIT or GPL Version 2 licenses.
- * http://jquery.org/license
- *
* Includes Sizzle.js
* http://sizzlejs.com/
- * Copyright 2011, The Dojo Foundation
- * Released under the MIT, BSD, and GPL Licenses.
*
- * Date: Mon Sep 12 18:54:48 2011 -0400
+ * Copyright 2005, 2013 jQuery Foundation, Inc. and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2013-07-03T13:48Z
*/
(function( window, undefined ) {
-// Use the correct document accordingly with window argument (sandbox)
-var document = window.document,
- navigator = window.navigator,
- location = window.location;
-var jQuery = (function() {
-
-// Define a local copy of jQuery
-var jQuery = function( selector, context ) {
- // The jQuery object is actually just the init constructor 'enhanced'
- return new jQuery.fn.init( selector, context, rootjQuery );
- },
+// Can't do this because several apps including ASP.NET trace
+// the stack via arguments.caller.callee and Firefox dies if
+// you try to trace through "use strict" call chains. (#13335)
+// Support: Firefox 18+
+//"use strict";
+var
+ // The deferred used on DOM ready
+ readyList,
+
+ // A central reference to the root jQuery(document)
+ rootjQuery,
+
+ // Support: IE<10
+ // For `typeof xmlNode.method` instead of `xmlNode.method !== undefined`
+ core_strundefined = typeof undefined,
+
+ // Use the correct document accordingly with window argument (sandbox)
+ location = window.location,
+ document = window.document,
+ docElem = document.documentElement,
// Map over jQuery in case of overwrite
_jQuery = window.jQuery,
@@ -33,136 +40,136 @@
// Map over the $ in case of overwrite
_$ = window.$,
- // A central reference to the root jQuery(document)
- rootjQuery,
-
- // A simple way to check for HTML strings or ID strings
+ // [[Class]] -> type pairs
+ class2type = {},
+
+ // List of deleted data cache ids, so we can reuse them
+ core_deletedIds = [],
+
+ core_version = "1.10.2",
+
+ // Save a reference to some core methods
+ core_concat = core_deletedIds.concat,
+ core_push = core_deletedIds.push,
+ core_slice = core_deletedIds.slice,
+ core_indexOf = core_deletedIds.indexOf,
+ core_toString = class2type.toString,
+ core_hasOwn = class2type.hasOwnProperty,
+ core_trim = core_version.trim,
+
+ // Define a local copy of jQuery
+ jQuery = function( selector, context ) {
+ // The jQuery object is actually just the init constructor 'enhanced'
+ return new jQuery.fn.init( selector, context, rootjQuery );
+ },
+
+ // Used for matching numbers
+ core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,
+
+ // Used for splitting on whitespace
+ core_rnotwhite = /\S+/g,
+
+ // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE)
+ rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
+
+ // A simple way to check for HTML strings
// Prioritize #id over to avoid XSS via location.hash (#9521)
- quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,
-
- // Check if a string has a non-whitespace character in it
- rnotwhite = /\S/,
-
- // Used for trimming whitespace
- trimLeft = /^\s+/,
- trimRight = /\s+$/,
-
- // Check for digits
- rdigit = /\d/,
+ // Strict HTML recognition (#11290: must start with <)
+ rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
// Match a standalone tag
- rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,
+ rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/,
// JSON RegExp
rvalidchars = /^[\],:{}\s]*$/,
- rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
- rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
-
- // Useragent RegExp
- rwebkit = /(webkit)[ \/]([\w.]+)/,
- ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/,
- rmsie = /(msie) ([\w.]+)/,
- rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/,
+ rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,
+ rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,
// Matches dashed string for camelizing
- rdashAlpha = /-([a-z]|[0-9])/ig,
rmsPrefix = /^-ms-/,
+ rdashAlpha = /-([\da-z])/gi,
// Used by jQuery.camelCase as callback to replace()
fcamelCase = function( all, letter ) {
- return ( letter + "" ).toUpperCase();
- },
-
- // Keep a UserAgent string for use with jQuery.browser
- userAgent = navigator.userAgent,
-
- // For matching the engine and version of the browser
- browserMatch,
-
- // The deferred used on DOM ready
- readyList,
+ return letter.toUpperCase();
+ },
// The ready event handler
- DOMContentLoaded,
-
- // Save a reference to some core methods
- toString = Object.prototype.toString,
- hasOwn = Object.prototype.hasOwnProperty,
- push = Array.prototype.push,
- slice = Array.prototype.slice,
- trim = String.prototype.trim,
- indexOf = Array.prototype.indexOf,
-
- // [[Class]] -> type pairs
- class2type = {};
+ completed = function( event ) {
+
+ // readyState === "complete" is good enough for us to call the dom ready in oldIE
+ if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) {
+ detach();
+ jQuery.ready();
+ }
+ },
+ // Clean-up method for dom ready events
+ detach = function() {
+ if ( document.addEventListener ) {
+ document.removeEventListener( "DOMContentLoaded", completed, false );
+ window.removeEventListener( "load", completed, false );
+
+ } else {
+ document.detachEvent( "onreadystatechange", completed );
+ window.detachEvent( "onload", completed );
+ }
+ };
jQuery.fn = jQuery.prototype = {
+ // The current version of jQuery being used
+ jquery: core_version,
+
constructor: jQuery,
init: function( selector, context, rootjQuery ) {
- var match, elem, ret, doc;
-
- // Handle $(""), $(null), or $(undefined)
+ var match, elem;
+
+ // HANDLE: $(""), $(null), $(undefined), $(false)
if ( !selector ) {
return this;
}
- // Handle $(DOMElement)
- if ( selector.nodeType ) {
- this.context = this[0] = selector;
- this.length = 1;
- return this;
- }
-
- // The body element only exists once, optimize finding it
- if ( selector === "body" && !context && document.body ) {
- this.context = document;
- this[0] = document.body;
- this.selector = selector;
- this.length = 1;
- return this;
- }
-
// Handle HTML strings
if ( typeof selector === "string" ) {
- // Are we dealing with HTML string or an ID?
if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
// Assume that strings that start and end with <> are HTML and skip the regex check
match = [ null, selector, null ];
} else {
- match = quickExpr.exec( selector );
- }
-
- // Verify a match, and that no context was specified for #id
+ match = rquickExpr.exec( selector );
+ }
+
+ // Match html or make sure no context is specified for #id
if ( match && (match[1] || !context) ) {
// HANDLE: $(html) -> $(array)
if ( match[1] ) {
context = context instanceof jQuery ? context[0] : context;
- doc = (context ? context.ownerDocument || context : document);
-
- // If a single string is passed in and it's a single tag
- // just do a createElement and skip the rest
- ret = rsingleTag.exec( selector );
-
- if ( ret ) {
- if ( jQuery.isPlainObject( context ) ) {
- selector = [ document.createElement( ret[1] ) ];
- jQuery.fn.attr.call( selector, context, true );
-
- } else {
- selector = [ doc.createElement( ret[1] ) ];
+
+ // scripts is true for back-compat
+ jQuery.merge( this, jQuery.parseHTML(
+ match[1],
+ context && context.nodeType ? context.ownerDocument || context : document,
+ true
+ ) );
+
+ // HANDLE: $(html, props)
+ if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
+ for ( match in context ) {
+ // Properties of context are called as methods if possible
+ if ( jQuery.isFunction( this[ match ] ) ) {
+ this[ match ]( context[ match ] );
+
+ // ...and otherwise set as attributes
+ } else {
+ this.attr( match, context[ match ] );
+ }
}
-
- } else {
- ret = jQuery.buildFragment( [ match[1] ], [ doc ] );
- selector = (ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment).childNodes;
}
- return jQuery.merge( this, selector );
-
- // HANDLE: $("#id")
+ return this;
+
+ // HANDLE: $(#id)
} else {
elem = document.getElementById( match[2] );
@@ -187,7 +194,7 @@
// HANDLE: $(expr, $(...))
} else if ( !context || context.jquery ) {
- return (context || rootjQuery).find( selector );
+ return ( context || rootjQuery ).find( selector );
// HANDLE: $(expr, context)
// (which is just equivalent to: $(context).find(expr)
@@ -195,13 +202,19 @@
return this.constructor( context ).find( selector );
}
+ // HANDLE: $(DOMElement)
+ } else if ( selector.nodeType ) {
+ this.context = this[0] = selector;
+ this.length = 1;
+ return this;
+
// HANDLE: $(function)
// Shortcut for document ready
} else if ( jQuery.isFunction( selector ) ) {
return rootjQuery.ready( selector );
}
- if (selector.selector !== undefined) {
+ if ( selector.selector !== undefined ) {
this.selector = selector.selector;
this.context = selector.context;
}
@@ -212,19 +225,11 @@
// Start with an empty selector
selector: "",
- // The current version of jQuery being used
- jquery: "1.6.4",
-
// The default length of a jQuery object is 0
length: 0,
- // The number of elements contained in the matched element set
- size: function() {
- return this.length;
- },
-
toArray: function() {
- return slice.call( this, 0 );
+ return core_slice.call( this );
},
// Get the Nth element in the matched element set OR
@@ -241,28 +246,15 @@
// Take an array of elements and push it onto the stack
// (returning the new matched element set)
- pushStack: function( elems, name, selector ) {
+ pushStack: function( elems ) {
+
// Build a new jQuery matched element set
- var ret = this.constructor();
-
- if ( jQuery.isArray( elems ) ) {
- push.apply( ret, elems );
-
- } else {
- jQuery.merge( ret, elems );
- }
+ var ret = jQuery.merge( this.constructor(), 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;
},
@@ -275,19 +267,14 @@
},
ready: function( fn ) {
- // Attach the listeners
- jQuery.bindReady();
-
// Add the callback
- readyList.done( fn );
+ jQuery.ready.promise().done( fn );
return this;
},
- eq: function( i ) {
- return i === -1 ?
- this.slice( i ) :
- this.slice( i, +i + 1 );
+ slice: function() {
+ return this.pushStack( core_slice.apply( this, arguments ) );
},
first: function() {
@@ -298,9 +285,10 @@
return this.eq( -1 );
},
- slice: function() {
- return this.pushStack( slice.apply( this, arguments ),
- "slice", slice.call(arguments).join(",") );
+ eq: function( i ) {
+ var len = this.length,
+ j = +i + ( i < 0 ? len : 0 );
+ return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
},
map: function( callback ) {
@@ -315,7 +303,7 @@
// For internal use only.
// Behaves like an Array's method, not like a jQuery method.
- push: push,
+ push: core_push,
sort: [].sort,
splice: [].splice
};
@@ -324,7 +312,7 @@
jQuery.fn.init.prototype = jQuery.fn;
jQuery.extend = jQuery.fn.extend = function() {
- var options, name, src, copy, copyIsArray, clone,
+ var src, copyIsArray, copy, name, options, clone,
target = arguments[0] || {},
i = 1,
length = arguments.length,
@@ -388,6 +376,10 @@
};
jQuery.extend({
+ // Unique for each copy of jQuery on the page
+ // Non-digits removed to match rinlinejQuery
+ expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ),
+
noConflict: function( deep ) {
if ( window.$ === jQuery ) {
window.$ = _$;
@@ -418,73 +410,31 @@
// Handle when the DOM is ready
ready: function( wait ) {
- // Either a released hold or an DOMready/load event and not yet ready
- if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) {
- // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
- if ( !document.body ) {
- return setTimeout( jQuery.ready, 1 );
- }
-
- // Remember that the DOM is ready
- jQuery.isReady = true;
-
- // If a normal DOM Ready event fired, decrement, and wait if need be
- if ( wait !== true && --jQuery.readyWait > 0 ) {
- return;
- }
-
- // If there are functions bound, to execute
- readyList.resolveWith( document, [ jQuery ] );
-
- // Trigger any bound ready events
- if ( jQuery.fn.trigger ) {
- jQuery( document ).trigger( "ready" ).unbind( "ready" );
- }
- }
- },
-
- bindReady: function() {
- if ( readyList ) {
+
+ // Abort if there are pending holds or we're already ready
+ if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
return;
}
- readyList = jQuery._Deferred();
-
- // Catch cases where $(document).ready() is called after the
- // browser event has already occurred.
- if ( document.readyState === "complete" ) {
- // Handle it asynchronously to allow scripts the opportunity to delay ready
- return setTimeout( jQuery.ready, 1 );
- }
-
- // Mozilla, Opera and webkit nightlies currently support this event
- if ( document.addEventListener ) {
- // Use the handy event callback
- document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
-
- // A fallback to window.onload, that will always work
- window.addEventListener( "load", jQuery.ready, false );
-
- // If IE event model is used
- } else if ( document.attachEvent ) {
- // ensure firing before onload,
- // maybe late but safe also for iframes
- document.attachEvent( "onreadystatechange", DOMContentLoaded );
-
- // A fallback to window.onload, that will always work
- window.attachEvent( "onload", jQuery.ready );
-
- // If IE and not a frame
- // continually check to see if the document is ready
- var toplevel = false;
-
- try {
- toplevel = window.frameElement == null;
- } catch(e) {}
-
- if ( document.documentElement.doScroll && toplevel ) {
- doScrollCheck();
- }
+ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+ if ( !document.body ) {
+ return setTimeout( jQuery.ready );
+ }
+
+ // Remember that the DOM is ready
+ jQuery.isReady = true;
+
+ // If a normal DOM Ready event fired, decrement, and wait if need be
+ if ( wait !== true && --jQuery.readyWait > 0 ) {
+ return;
+ }
+
+ // If there are functions bound, to execute
+ readyList.resolveWith( document, [ jQuery ] );
+
+ // Trigger any bound ready events
+ if ( jQuery.fn.trigger ) {
+ jQuery( document ).trigger("ready").off("ready");
}
},
@@ -499,22 +449,27 @@
return jQuery.type(obj) === "array";
},
- // A crude way of determining if an object is a window
isWindow: function( obj ) {
- return obj && typeof obj === "object" && "setInterval" in obj;
- },
-
- isNaN: function( obj ) {
- return obj == null || !rdigit.test( obj ) || isNaN( obj );
+ /* jshint eqeqeq: false */
+ return obj != null && obj == obj.window;
+ },
+
+ isNumeric: function( obj ) {
+ return !isNaN( parseFloat(obj) ) && isFinite( obj );
},
type: function( obj ) {
- return obj == null ?
- String( obj ) :
- class2type[ toString.call(obj) ] || "object";
+ if ( obj == null ) {
+ return String( obj );
+ }
+ return typeof obj === "object" || typeof obj === "function" ?
+ class2type[ core_toString.call(obj) ] || "object" :
+ typeof obj;
},
isPlainObject: function( obj ) {
+ var key;
+
// 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
@@ -525,8 +480,8 @@
try {
// Not own constructor property must be Object
if ( obj.constructor &&
- !hasOwn.call(obj, "constructor") &&
- !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
+ !core_hasOwn.call(obj, "constructor") &&
+ !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
return false;
}
} catch ( e ) {
@@ -534,54 +489,97 @@
return false;
}
+ // Support: IE<9
+ // Handle iteration over inherited properties before own properties.
+ if ( jQuery.support.ownLast ) {
+ for ( key in obj ) {
+ return core_hasOwn.call( obj, key );
+ }
+ }
+
// Own properties are enumerated firstly, so to speed up,
// if last one is own, then all properties are own.
-
- var key;
for ( key in obj ) {}
- return key === undefined || hasOwn.call( obj, key );
+ return key === undefined || core_hasOwn.call( obj, key );
},
isEmptyObject: function( obj ) {
- for ( var name in obj ) {
+ var name;
+ for ( name in obj ) {
return false;
}
return true;
},
error: function( msg ) {
- throw msg;
+ throw new Error( msg );
+ },
+
+ // data: string of html
+ // context (optional): If specified, the fragment will be created in this context, defaults to document
+ // keepScripts (optional): If true, will include scripts passed in the html string
+ parseHTML: function( data, context, keepScripts ) {
+ if ( !data || typeof data !== "string" ) {
+ return null;
+ }
+ if ( typeof context === "boolean" ) {
+ keepScripts = context;
+ context = false;
+ }
+ context = context || document;
+
+ var parsed = rsingleTag.exec( data ),
+ scripts = !keepScripts && [];
+
+ // Single tag
+ if ( parsed ) {
+ return [ context.createElement( parsed[1] ) ];
+ }
+
+ parsed = jQuery.buildFragment( [ data ], context, scripts );
+ if ( scripts ) {
+ jQuery( scripts ).remove();
+ }
+ return jQuery.merge( [], parsed.childNodes );
},
parseJSON: function( data ) {
- if ( typeof data !== "string" || !data ) {
- return null;
- }
-
- // Make sure leading/trailing whitespace is removed (IE can't handle it)
- data = jQuery.trim( data );
-
// Attempt to parse using the native JSON parser first
if ( window.JSON && window.JSON.parse ) {
return window.JSON.parse( data );
}
- // Make sure the incoming data is actual JSON
- // Logic borrowed from http://json.org/json2.js
- if ( rvalidchars.test( data.replace( rvalidescape, "@" )
- .replace( rvalidtokens, "]" )
- .replace( rvalidbraces, "")) ) {
-
- return (new Function( "return " + data ))();
-
- }
+ if ( data === null ) {
+ return data;
+ }
+
+ if ( typeof data === "string" ) {
+
+ // Make sure leading/trailing whitespace is removed (IE can't handle it)
+ data = jQuery.trim( data );
+
+ if ( data ) {
+ // Make sure the incoming data is actual JSON
+ // Logic borrowed from http://json.org/json2.js
+ if ( rvalidchars.test( data.replace( rvalidescape, "@" )
+ .replace( rvalidtokens, "]" )
+ .replace( rvalidbraces, "")) ) {
+
+ return ( new Function( "return " + data ) )();
+ }
+ }
+ }
+
jQuery.error( "Invalid JSON: " + data );
},
// Cross-browser xml parsing
parseXML: function( data ) {
var xml, tmp;
+ if ( !data || typeof data !== "string" ) {
+ return null;
+ }
try {
if ( window.DOMParser ) { // Standard
tmp = new DOMParser();
@@ -606,7 +604,7 @@
// Workarounds based on findings by Jim Driscoll
// http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
globalEval: function( data ) {
- if ( data && rnotwhite.test( data ) ) {
+ if ( data && jQuery.trim( data ) ) {
// We use execScript on Internet Explorer
// We use an anonymous function so that context is window
// rather than jQuery in Firefox
@@ -623,25 +621,30 @@
},
nodeName: function( elem, name ) {
- return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
+ return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
},
// args is for internal usage only
- each: function( object, callback, args ) {
- var name, i = 0,
- length = object.length,
- isObj = length === undefined || jQuery.isFunction( object );
+ each: function( obj, callback, args ) {
+ var value,
+ i = 0,
+ length = obj.length,
+ isArray = isArraylike( obj );
if ( args ) {
- if ( isObj ) {
- for ( name in object ) {
- if ( callback.apply( object[ name ], args ) === false ) {
+ if ( isArray ) {
+ for ( ; i < length; i++ ) {
+ value = callback.apply( obj[ i ], args );
+
+ if ( value === false ) {
break;
}
}
} else {
- for ( ; i < length; ) {
- if ( callback.apply( object[ i++ ], args ) === false ) {
+ for ( i in obj ) {
+ value = callback.apply( obj[ i ], args );
+
+ if ( value === false ) {
break;
}
}
@@ -649,72 +652,77 @@
// 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 ) {
+ if ( isArray ) {
+ for ( ; i < length; i++ ) {
+ value = callback.call( obj[ i ], i, obj[ i ] );
+
+ if ( value === false ) {
break;
}
}
} else {
- for ( ; i < length; ) {
- if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) {
+ for ( i in obj ) {
+ value = callback.call( obj[ i ], i, obj[ i ] );
+
+ if ( value === false ) {
break;
}
}
}
}
- return object;
+ return obj;
},
// Use native String.trim function wherever possible
- trim: trim ?
+ trim: core_trim && !core_trim.call("\uFEFF\xA0") ?
function( text ) {
return text == null ?
"" :
- trim.call( text );
+ core_trim.call( text );
} :
// Otherwise use our own trimming functionality
function( text ) {
return text == null ?
"" :
- text.toString().replace( trimLeft, "" ).replace( trimRight, "" );
+ ( text + "" ).replace( rtrim, "" );
},
// results is for internal usage only
- makeArray: function( array, results ) {
+ makeArray: function( arr, results ) {
var ret = results || [];
- if ( array != null ) {
- // The window, strings (and functions) also have 'length'
- // The extra typeof function check is to prevent crashes
- // in Safari 2 (See: #3039)
- // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930
- var type = jQuery.type( array );
-
- if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) {
- push.call( ret, array );
+ if ( arr != null ) {
+ if ( isArraylike( Object(arr) ) ) {
+ jQuery.merge( ret,
+ typeof arr === "string" ?
+ [ arr ] : arr
+ );
} else {
- jQuery.merge( ret, array );
+ core_push.call( ret, arr );
}
}
return ret;
},
- inArray: function( elem, array ) {
- if ( !array ) {
- return -1;
- }
-
- if ( indexOf ) {
- return indexOf.call( array, elem );
- }
-
- for ( var i = 0, length = array.length; i < length; i++ ) {
- if ( array[ i ] === elem ) {
- return i;
+ inArray: function( elem, arr, i ) {
+ var len;
+
+ if ( arr ) {
+ if ( core_indexOf ) {
+ return core_indexOf.call( arr, elem, i );
+ }
+
+ len = arr.length;
+ i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
+
+ for ( ; i < len; i++ ) {
+ // Skip accessing in sparse arrays
+ if ( i in arr && arr[ i ] === elem ) {
+ return i;
+ }
}
}
@@ -722,14 +730,14 @@
},
merge: function( first, second ) {
- var i = first.length,
+ var l = second.length,
+ i = first.length,
j = 0;
- if ( typeof second.length === "number" ) {
- for ( var l = second.length; j < l; j++ ) {
+ if ( typeof l === "number" ) {
+ for ( ; j < l; j++ ) {
first[ i++ ] = second[ j ];
}
-
} else {
while ( second[j] !== undefined ) {
first[ i++ ] = second[ j++ ];
@@ -742,12 +750,15 @@
},
grep: function( elems, callback, inv ) {
- var ret = [], retVal;
+ var retVal,
+ ret = [],
+ i = 0,
+ length = elems.length;
inv = !!inv;
// Go through the array, only saving the items
// that pass the validator function
- for ( var i = 0, length = elems.length; i < length; i++ ) {
+ for ( ; i < length; i++ ) {
retVal = !!callback( elems[ i ], i );
if ( inv !== retVal ) {
ret.push( elems[ i ] );
@@ -759,11 +770,11 @@
// arg is for internal usage only
map: function( elems, callback, arg ) {
- var value, key, ret = [],
+ var value,
i = 0,
length = elems.length,
- // jquery objects are treated as arrays
- isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ;
+ isArray = isArraylike( elems ),
+ ret = [];
// Go through the array, translating each of the items to their
if ( isArray ) {
@@ -777,8 +788,8 @@
// Go through every key on the object,
} else {
- for ( key in elems ) {
- value = callback( elems[ key ], key, arg );
+ for ( i in elems ) {
+ value = callback( elems[ i ], i, arg );
if ( value != null ) {
ret[ ret.length ] = value;
@@ -787,7 +798,7 @@
}
// Flatten any nested arrays
- return ret.concat.apply( [], ret );
+ return core_concat.apply( [], ret );
},
// A global GUID counter for objects
@@ -796,8 +807,10 @@
// Bind a function to a context, optionally partially applying any
// arguments.
proxy: function( fn, context ) {
+ var args, proxy, tmp;
+
if ( typeof context === "string" ) {
- var tmp = fn[ context ];
+ tmp = fn[ context ];
context = fn;
fn = tmp;
}
@@ -809,443 +822,2576 @@
}
// Simulated bind
- var args = slice.call( arguments, 2 ),
- proxy = function() {
- return fn.apply( context, args.concat( slice.call( arguments ) ) );
- };
+ args = core_slice.call( arguments, 2 );
+ proxy = function() {
+ return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) );
+ };
// Set the guid of unique handler to the same of original handler, so it can be removed
- proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
+ proxy.guid = fn.guid = fn.guid || jQuery.guid++;
return proxy;
},
- // Mutifunctional method to get and set values to a collection
+ // Multifunctional method to get and set values of a collection
// The value/s can optionally be executed if it's a function
- access: function( elems, key, value, exec, fn, pass ) {
- var length = elems.length;
-
- // Setting many attributes
- if ( typeof key === "object" ) {
- for ( var k in key ) {
- jQuery.access( elems, k, key[k], exec, fn, value );
- }
- return elems;
- }
-
- // Setting one attribute
- if ( value !== undefined ) {
- // Optionally, function values get executed if exec is true
- exec = !pass && exec && jQuery.isFunction(value);
-
- for ( var i = 0; i < length; i++ ) {
- fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
- }
-
- return elems;
- }
-
- // Getting an attribute
- return length ? fn( elems[0], key ) : undefined;
+ access: function( elems, fn, key, value, chainable, emptyGet, raw ) {
+ var i = 0,
+ length = elems.length,
+ bulk = key == null;
+
+ // Sets many values
+ if ( jQuery.type( key ) === "object" ) {
+ chainable = true;
+ for ( i in key ) {
+ jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
+ }
+
+ // Sets one value
+ } else if ( value !== undefined ) {
+ chainable = true;
+
+ if ( !jQuery.isFunction( value ) ) {
+ raw = true;
+ }
+
+ if ( bulk ) {
+ // Bulk operations run against the entire set
+ if ( raw ) {
+ fn.call( elems, value );
+ fn = null;
+
+ // ...except when executing function values
+ } else {
+ bulk = fn;
+ fn = function( elem, key, value ) {
+ return bulk.call( jQuery( elem ), value );
+ };
+ }
+ }
+
+ if ( fn ) {
+ for ( ; i < length; i++ ) {
+ fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
+ }
+ }
+ }
+
+ return chainable ?
+ elems :
+
+ // Gets
+ bulk ?
+ fn.call( elems ) :
+ length ? fn( elems[0], key ) : emptyGet;
},
now: function() {
- return (new Date()).getTime();
- },
-
- // Use of jQuery.browser is frowned upon.
- // More details: http://docs.jquery.com/Utilities/jQuery.browser
- uaMatch: function( ua ) {
- ua = ua.toLowerCase();
-
- var match = rwebkit.exec( ua ) ||
- ropera.exec( ua ) ||
- rmsie.exec( ua ) ||
- ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) ||
- [];
-
- return { browser: match[1] || "", version: match[2] || "0" };
- },
-
- sub: function() {
- function jQuerySub( selector, context ) {
- return new jQuerySub.fn.init( selector, context );
- }
- jQuery.extend( true, jQuerySub, this );
- jQuerySub.superclass = this;
- jQuerySub.fn = jQuerySub.prototype = this();
- jQuerySub.fn.constructor = jQuerySub;
- jQuerySub.sub = this.sub;
- jQuerySub.fn.init = function init( selector, context ) {
- if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {
- context = jQuerySub( context );
- }
-
- return jQuery.fn.init.call( this, selector, context, rootjQuerySub );
- };
- jQuerySub.fn.init.prototype = jQuerySub.fn;
- var rootjQuerySub = jQuerySub(document);
- return jQuerySub;
- },
-
- browser: {}
+ return ( new Date() ).getTime();
+ },
+
+ // A method for quickly swapping in/out CSS properties to get correct calculations.
+ // Note: this method belongs to the css module but it's needed here for the support module.
+ // If support gets modularized, this method should be moved back to the css module.
+ swap: function( elem, options, callback, args ) {
+ var ret, name,
+ old = {};
+
+ // Remember the old values, and insert the new ones
+ for ( name in options ) {
+ old[ name ] = elem.style[ name ];
+ elem.style[ name ] = options[ name ];
+ }
+
+ ret = callback.apply( elem, args || [] );
+
+ // Revert the old values
+ for ( name in options ) {
+ elem.style[ name ] = old[ name ];
+ }
+
+ return ret;
+ }
});
+jQuery.ready.promise = function( obj ) {
+ if ( !readyList ) {
+
+ readyList = jQuery.Deferred();
+
+ // Catch cases where $(document).ready() is called after the browser event has already occurred.
+ // we once tried to use readyState "interactive" here, but it caused issues like the one
+ // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
+ if ( document.readyState === "complete" ) {
+ // Handle it asynchronously to allow scripts the opportunity to delay ready
+ setTimeout( jQuery.ready );
+
+ // Standards-based browsers support DOMContentLoaded
+ } else if ( document.addEventListener ) {
+ // Use the handy event callback
+ document.addEventListener( "DOMContentLoaded", completed, false );
+
+ // A fallback to window.onload, that will always work
+ window.addEventListener( "load", completed, false );
+
+ // If IE event model is used
+ } else {
+ // Ensure firing before onload, maybe late but safe also for iframes
+ document.attachEvent( "onreadystatechange", completed );
+
+ // A fallback to window.onload, that will always work
+ window.attachEvent( "onload", completed );
+
+ // If IE and not a frame
+ // continually check to see if the document is ready
+ var top = false;
+
+ try {
+ top = window.frameElement == null && document.documentElement;
+ } catch(e) {}
+
+ if ( top && top.doScroll ) {
+ (function doScrollCheck() {
+ if ( !jQuery.isReady ) {
+
+ try {
+ // Use the trick by Diego Perini
+ // http://javascript.nwbox.com/IEContentLoaded/
+ top.doScroll("left");
+ } catch(e) {
+ return setTimeout( doScrollCheck, 50 );
+ }
+
+ // detach all dom ready events
+ detach();
+
+ // and execute any waiting functions
+ jQuery.ready();
+ }
+ })();
+ }
+ }
+ }
+ return readyList.promise( obj );
+};
+
// Populate the class2type map
-jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
+jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
class2type[ "[object " + name + "]" ] = name.toLowerCase();
});
-browserMatch = jQuery.uaMatch( userAgent );
-if ( browserMatch.browser ) {
- jQuery.browser[ browserMatch.browser ] = true;
- jQuery.browser.version = browserMatch.version;
-}
-
-// Deprecated, use jQuery.browser.webkit instead
-if ( jQuery.browser.webkit ) {
- jQuery.browser.safari = true;
-}
-
-// IE doesn't match non-breaking spaces with \s
-if ( rnotwhite.test( "\xA0" ) ) {
- trimLeft = /^[\s\xA0]+/;
- trimRight = /[\s\xA0]+$/;
+function isArraylike( obj ) {
+ var length = obj.length,
+ type = jQuery.type( obj );
+
+ if ( jQuery.isWindow( obj ) ) {
+ return false;
+ }
+
+ if ( obj.nodeType === 1 && length ) {
+ return true;
+ }
+
+ return type === "array" || type !== "function" &&
+ ( length === 0 ||
+ typeof length === "number" && length > 0 && ( length - 1 ) in obj );
}
// 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();
+/*!
+ * Sizzle CSS Selector Engine v1.10.2
+ * http://sizzlejs.com/
+ *
+ * Copyright 2013 jQuery Foundation, Inc. and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2013-07-03
+ */
+(function( window, undefined ) {
+
+var i,
+ support,
+ cachedruns,
+ Expr,
+ getText,
+ isXML,
+ compile,
+ outermostContext,
+ sortInput,
+
+ // Local document vars
+ setDocument,
+ document,
+ docElem,
+ documentIsHTML,
+ rbuggyQSA,
+ rbuggyMatches,
+ matches,
+ contains,
+
+ // Instance-specific data
+ expando = "sizzle" + -(new Date()),
+ preferredDoc = window.document,
+ dirruns = 0,
+ done = 0,
+ classCache = createCache(),
+ tokenCache = createCache(),
+ compilerCache = createCache(),
+ hasDuplicate = false,
+ sortOrder = function( a, b ) {
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+ }
+ return 0;
+ },
+
+ // General-purpose constants
+ strundefined = typeof undefined,
+ MAX_NEGATIVE = 1 << 31,
+
+ // Instance methods
+ hasOwn = ({}).hasOwnProperty,
+ arr = [],
+ pop = arr.pop,
+ push_native = arr.push,
+ push = arr.push,
+ slice = arr.slice,
+ // Use a stripped-down indexOf if we can't use a native one
+ indexOf = arr.indexOf || function( elem ) {
+ var i = 0,
+ len = this.length;
+ for ( ; i < len; i++ ) {
+ if ( this[i] === elem ) {
+ return i;
+ }
+ }
+ return -1;
+ },
+
+ booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
+
+ // Regular expressions
+
+ // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
+ whitespace = "[\\x20\\t\\r\\n\\f]",
+ // http://www.w3.org/TR/css3-syntax/#characters
+ characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
+
+ // Loosely modeled on CSS identifier characters
+ // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors
+ // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
+ identifier = characterEncoding.replace( "w", "w#" ),
+
+ // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors
+ attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +
+ "*(?:([*^$|!~]?=)" + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]",
+
+ // Prefer arguments quoted,
+ // then not containing pseudos/brackets,
+ // then attribute selectors/non-parenthetical expressions,
+ // then anything else
+ // These preferences are here to reduce the number of selectors
+ // needing tokenize in the PSEUDO preFilter
+ pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)",
+
+ // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
+ rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
+
+ rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
+ rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),
+
+ rsibling = new RegExp( whitespace + "*[+~]" ),
+ rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*)" + whitespace + "*\\]", "g" ),
+
+ rpseudo = new RegExp( pseudos ),
+ ridentifier = new RegExp( "^" + identifier + "$" ),
+
+ matchExpr = {
+ "ID": new RegExp( "^#(" + characterEncoding + ")" ),
+ "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
+ "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
+ "ATTR": new RegExp( "^" + attributes ),
+ "PSEUDO": new RegExp( "^" + pseudos ),
+ "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
+ "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
+ "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
+ "bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
+ // For use in libraries implementing .is()
+ // We use this for POS matching in `select`
+ "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
+ whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
+ },
+
+ rnative = /^[^{]+\{\s*\[native \w/,
+
+ // Easily-parseable/retrievable ID or TAG or CLASS selectors
+ rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
+
+ rinputs = /^(?:input|select|textarea|button)$/i,
+ rheader = /^h\d$/i,
+
+ rescape = /'|\\/g,
+
+ // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
+ runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
+ funescape = function( _, escaped, escapedWhitespace ) {
+ var high = "0x" + escaped - 0x10000;
+ // NaN means non-codepoint
+ // Support: Firefox
+ // Workaround erroneous numeric interpretation of +"0x"
+ return high !== high || escapedWhitespace ?
+ escaped :
+ // BMP codepoint
+ high < 0 ?
+ String.fromCharCode( high + 0x10000 ) :
+ // Supplemental Plane codepoint (surrogate pair)
+ String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
};
-} 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();
+// Optimize for push.apply( _, NodeList )
+try {
+ push.apply(
+ (arr = slice.call( preferredDoc.childNodes )),
+ preferredDoc.childNodes
+ );
+ // Support: Android<4.0
+ // Detect silently failing push.apply
+ arr[ preferredDoc.childNodes.length ].nodeType;
+} catch ( e ) {
+ push = { apply: arr.length ?
+
+ // Leverage slice if possible
+ function( target, els ) {
+ push_native.apply( target, slice.call(els) );
+ } :
+
+ // Support: IE<9
+ // Otherwise append directly
+ function( target, els ) {
+ var j = target.length,
+ i = 0;
+ // Can't trust NodeList.length
+ while ( (target[j++] = els[i++]) ) {}
+ target.length = j - 1;
}
};
}
-// The DOM ready check for Internet Explorer
-function doScrollCheck() {
- if ( jQuery.isReady ) {
- return;
- }
+function Sizzle( selector, context, results, seed ) {
+ var match, elem, m, nodeType,
+ // QSA vars
+ i, groups, old, nid, newContext, newSelector;
+
+ if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
+ setDocument( context );
+ }
+
+ context = context || document;
+ results = results || [];
+
+ if ( !selector || typeof selector !== "string" ) {
+ return results;
+ }
+
+ if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) {
+ return [];
+ }
+
+ if ( documentIsHTML && !seed ) {
+
+ // Shortcuts
+ if ( (match = rquickExpr.exec( selector )) ) {
+ // Speed-up: Sizzle("#ID")
+ if ( (m = match[1]) ) {
+ if ( nodeType === 9 ) {
+ elem = context.getElementById( m );
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ if ( elem && elem.parentNode ) {
+ // Handle the case where IE, Opera, and Webkit return items
+ // by name instead of ID
+ if ( elem.id === m ) {
+ results.push( elem );
+ return results;
+ }
+ } else {
+ return results;
+ }
+ } else {
+ // Context is not a document
+ if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
+ contains( context, elem ) && elem.id === m ) {
+ results.push( elem );
+ return results;
+ }
+ }
+
+ // Speed-up: Sizzle("TAG")
+ } else if ( match[2] ) {
+ push.apply( results, context.getElementsByTagName( selector ) );
+ return results;
+
+ // Speed-up: Sizzle(".CLASS")
+ } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) {
+ push.apply( results, context.getElementsByClassName( m ) );
+ return results;
+ }
+ }
+
+ // QSA path
+ if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
+ nid = old = expando;
+ newContext = context;
+ newSelector = nodeType === 9 && selector;
+
+ // qSA works strangely on Element-rooted queries
+ // We can work around this by specifying an extra ID on the root
+ // and working up from there (Thanks to Andrew Dupont for the technique)
+ // IE 8 doesn't work on object elements
+ if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
+ groups = tokenize( selector );
+
+ if ( (old = context.getAttribute("id")) ) {
+ nid = old.replace( rescape, "\\$&" );
+ } else {
+ context.setAttribute( "id", nid );
+ }
+ nid = "[id='" + nid + "'] ";
+
+ i = groups.length;
+ while ( i-- ) {
+ groups[i] = nid + toSelector( groups[i] );
+ }
+ newContext = rsibling.test( selector ) && context.parentNode || context;
+ newSelector = groups.join(",");
+ }
+
+ if ( newSelector ) {
+ try {
+ push.apply( results,
+ newContext.querySelectorAll( newSelector )
+ );
+ return results;
+ } catch(qsaError) {
+ } finally {
+ if ( !old ) {
+ context.removeAttribute("id");
+ }
+ }
+ }
+ }
+ }
+
+ // All others
+ return select( selector.replace( rtrim, "$1" ), context, results, seed );
+}
+
+/**
+ * Create key-value caches of limited size
+ * @returns {Function(string, Object)} Returns the Object data after storing it on itself with
+ * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
+ * deleting the oldest entry
+ */
+function createCache() {
+ var keys = [];
+
+ function cache( key, value ) {
+ // Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
+ if ( keys.push( key += " " ) > Expr.cacheLength ) {
+ // Only keep the most recent entries
+ delete cache[ keys.shift() ];
+ }
+ return (cache[ key ] = value);
+ }
+ return cache;
+}
+
+/**
+ * Mark a function for special use by Sizzle
+ * @param {Function} fn The function to mark
+ */
+function markFunction( fn ) {
+ fn[ expando ] = true;
+ return fn;
+}
+
+/**
+ * Support testing using an element
+ * @param {Function} fn Passed the created div and expects a boolean result
+ */
+function assert( fn ) {
+ var div = document.createElement("div");
try {
- // If IE is used, use the trick by Diego Perini
- // http://javascript.nwbox.com/IEContentLoaded/
- document.documentElement.doScroll("left");
- } catch(e) {
- setTimeout( doScrollCheck, 1 );
- return;
- }
-
- // and execute any waiting functions
- jQuery.ready();
+ return !!fn( div );
+ } catch (e) {
+ return false;
+ } finally {
+ // Remove from its parent by default
+ if ( div.parentNode ) {
+ div.parentNode.removeChild( div );
+ }
+ // release memory in IE
+ div = null;
+ }
+}
+
+/**
+ * Adds the same handler for all of the specified attrs
+ * @param {String} attrs Pipe-separated list of attributes
+ * @param {Function} handler The method that will be applied
+ */
+function addHandle( attrs, handler ) {
+ var arr = attrs.split("|"),
+ i = attrs.length;
+
+ while ( i-- ) {
+ Expr.attrHandle[ arr[i] ] = handler;
+ }
+}
+
+/**
+ * Checks document order of two siblings
+ * @param {Element} a
+ * @param {Element} b
+ * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
+ */
+function siblingCheck( a, b ) {
+ var cur = b && a,
+ diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
+ ( ~b.sourceIndex || MAX_NEGATIVE ) -
+ ( ~a.sourceIndex || MAX_NEGATIVE );
+
+ // Use IE sourceIndex if available on both nodes
+ if ( diff ) {
+ return diff;
+ }
+
+ // Check if b follows a
+ if ( cur ) {
+ while ( (cur = cur.nextSibling) ) {
+ if ( cur === b ) {
+ return -1;
+ }
+ }
+ }
+
+ return a ? 1 : -1;
+}
+
+/**
+ * Returns a function to use in pseudos for input types
+ * @param {String} type
+ */
+function createInputPseudo( type ) {
+ return function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return name === "input" && elem.type === type;
+ };
+}
+
+/**
+ * Returns a function to use in pseudos for buttons
+ * @param {String} type
+ */
+function createButtonPseudo( type ) {
+ return function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return (name === "input" || name === "button") && elem.type === type;
+ };
+}
+
+/**
+ * Returns a function to use in pseudos for positionals
+ * @param {Function} fn
+ */
+function createPositionalPseudo( fn ) {
+ return markFunction(function( argument ) {
+ argument = +argument;
+ return markFunction(function( seed, matches ) {
+ var j,
+ matchIndexes = fn( [], seed.length, argument ),
+ i = matchIndexes.length;
+
+ // Match elements found at the specified indexes
+ while ( i-- ) {
+ if ( seed[ (j = matchIndexes[i]) ] ) {
+ seed[j] = !(matches[j] = seed[j]);
+ }
+ }
+ });
+ });
}
-return jQuery;
-
-})();
-
-
-var // Promise methods
- promiseMethods = "done fail isResolved isRejected promise then always pipe".split( " " ),
- // Static reference to slice
- sliceDeferred = [].slice;
-
-jQuery.extend({
- // Create a simple deferred (one callbacks list)
- _Deferred: function() {
- var // callbacks list
- callbacks = [],
- // stored [ context , args ]
- fired,
- // to avoid firing when already doing so
- firing,
- // flag to know if the deferred has been cancelled
- cancelled,
- // the deferred itself
- deferred = {
-
- // done( f1, f2, ...)
- done: function() {
- if ( !cancelled ) {
- var args = arguments,
- i,
- length,
- elem,
- type,
- _fired;
- if ( fired ) {
- _fired = fired;
- fired = 0;
+/**
+ * Detect xml
+ * @param {Element|Object} elem An element or a document
+ */
+isXML = Sizzle.isXML = function( elem ) {
+ // documentElement is verified for cases where it doesn't yet exist
+ // (such as loading iframes in IE - #4833)
+ var documentElement = elem && (elem.ownerDocument || elem).documentElement;
+ return documentElement ? documentElement.nodeName !== "HTML" : false;
+};
+
+// Expose support vars for convenience
+support = Sizzle.support = {};
+
+/**
+ * Sets document-related variables once based on the current document
+ * @param {Element|Object} [doc] An element or document object to use to set the document
+ * @returns {Object} Returns the current document
+ */
+setDocument = Sizzle.setDocument = function( node ) {
+ var doc = node ? node.ownerDocument || node : preferredDoc,
+ parent = doc.defaultView;
+
+ // If no document and documentElement is available, return
+ if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
+ return document;
+ }
+
+ // Set our document
+ document = doc;
+ docElem = doc.documentElement;
+
+ // Support tests
+ documentIsHTML = !isXML( doc );
+
+ // Support: IE>8
+ // If iframe document is assigned to "document" variable and if iframe has been reloaded,
+ // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936
+ // IE6-8 do not support the defaultView property so parent will be undefined
+ if ( parent && parent.attachEvent && parent !== parent.top ) {
+ parent.attachEvent( "onbeforeunload", function() {
+ setDocument();
+ });
+ }
+
+ /* Attributes
+ ---------------------------------------------------------------------- */
+
+ // Support: IE<8
+ // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans)
+ support.attributes = assert(function( div ) {
+ div.className = "i";
+ return !div.getAttribute("className");
+ });
+
+ /* getElement(s)By*
+ ---------------------------------------------------------------------- */
+
+ // Check if getElementsByTagName("*") returns only elements
+ support.getElementsByTagName = assert(function( div ) {
+ div.appendChild( doc.createComment("") );
+ return !div.getElementsByTagName("*").length;
+ });
+
+ // Check if getElementsByClassName can be trusted
+ support.getElementsByClassName = assert(function( div ) {
+ div.innerHTML = "
";
+
+ // Support: Safari<4
+ // Catch class over-caching
+ div.firstChild.className = "i";
+ // Support: Opera<10
+ // Catch gEBCN failure to find non-leading classes
+ return div.getElementsByClassName("i").length === 2;
+ });
+
+ // Support: IE<10
+ // Check if getElementById returns elements by name
+ // The broken getElementById methods don't pick up programatically-set names,
+ // so use a roundabout getElementsByName test
+ support.getById = assert(function( div ) {
+ docElem.appendChild( div ).id = expando;
+ return !doc.getElementsByName || !doc.getElementsByName( expando ).length;
+ });
+
+ // ID find and filter
+ if ( support.getById ) {
+ Expr.find["ID"] = function( id, context ) {
+ if ( typeof context.getElementById !== strundefined && documentIsHTML ) {
+ var m = context.getElementById( id );
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ return m && m.parentNode ? [m] : [];
+ }
+ };
+ Expr.filter["ID"] = function( id ) {
+ var attrId = id.replace( runescape, funescape );
+ return function( elem ) {
+ return elem.getAttribute("id") === attrId;
+ };
+ };
+ } else {
+ // Support: IE6/7
+ // getElementById is not reliable as a find shortcut
+ delete Expr.find["ID"];
+
+ Expr.filter["ID"] = function( id ) {
+ var attrId = id.replace( runescape, funescape );
+ return function( elem ) {
+ var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
+ return node && node.value === attrId;
+ };
+ };
+ }
+
+ // Tag
+ Expr.find["TAG"] = support.getElementsByTagName ?
+ function( tag, context ) {
+ if ( typeof context.getElementsByTagName !== strundefined ) {
+ return context.getElementsByTagName( tag );
+ }
+ } :
+ function( tag, context ) {
+ var elem,
+ tmp = [],
+ i = 0,
+ results = context.getElementsByTagName( tag );
+
+ // Filter out possible comments
+ if ( tag === "*" ) {
+ while ( (elem = results[i++]) ) {
+ if ( elem.nodeType === 1 ) {
+ tmp.push( elem );
+ }
+ }
+
+ return tmp;
+ }
+ return results;
+ };
+
+ // Class
+ Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
+ if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) {
+ return context.getElementsByClassName( className );
+ }
+ };
+
+ /* QSA/matchesSelector
+ ---------------------------------------------------------------------- */
+
+ // QSA and matchesSelector support
+
+ // matchesSelector(:active) reports false when true (IE9/Opera 11.5)
+ rbuggyMatches = [];
+
+ // qSa(:focus) reports false when true (Chrome 21)
+ // We allow this because of a bug in IE8/9 that throws an error
+ // whenever `document.activeElement` is accessed on an iframe
+ // So, we allow :focus to pass through QSA all the time to avoid the IE error
+ // See http://bugs.jquery.com/ticket/13378
+ rbuggyQSA = [];
+
+ if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) {
+ // Build QSA regex
+ // Regex strategy adopted from Diego Perini
+ assert(function( div ) {
+ // Select is set to empty string on purpose
+ // This is to test IE's treatment of not explicitly
+ // setting a boolean content attribute,
+ // since its presence should be enough
+ // http://bugs.jquery.com/ticket/12359
+ div.innerHTML = " ";
+
+ // Support: IE8
+ // Boolean attributes and "value" are not treated correctly
+ if ( !div.querySelectorAll("[selected]").length ) {
+ rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
+ }
+
+ // Webkit/Opera - :checked should return selected option elements
+ // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
+ // IE8 throws error here and will not see later tests
+ if ( !div.querySelectorAll(":checked").length ) {
+ rbuggyQSA.push(":checked");
+ }
+ });
+
+ assert(function( div ) {
+
+ // Support: Opera 10-12/IE8
+ // ^= $= *= and empty values
+ // Should not select anything
+ // Support: Windows 8 Native Apps
+ // The type attribute is restricted during .innerHTML assignment
+ var input = doc.createElement("input");
+ input.setAttribute( "type", "hidden" );
+ div.appendChild( input ).setAttribute( "t", "" );
+
+ if ( div.querySelectorAll("[t^='']").length ) {
+ rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
+ }
+
+ // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
+ // IE8 throws error here and will not see later tests
+ if ( !div.querySelectorAll(":enabled").length ) {
+ rbuggyQSA.push( ":enabled", ":disabled" );
+ }
+
+ // Opera 10-11 does not throw on post-comma invalid pseudos
+ div.querySelectorAll("*,:x");
+ rbuggyQSA.push(",.*:");
+ });
+ }
+
+ if ( (support.matchesSelector = rnative.test( (matches = docElem.webkitMatchesSelector ||
+ docElem.mozMatchesSelector ||
+ docElem.oMatchesSelector ||
+ docElem.msMatchesSelector) )) ) {
+
+ assert(function( div ) {
+ // Check to see if it's possible to do matchesSelector
+ // on a disconnected node (IE 9)
+ support.disconnectedMatch = matches.call( div, "div" );
+
+ // This should fail with an exception
+ // Gecko does not error, returns false instead
+ matches.call( div, "[s!='']:x" );
+ rbuggyMatches.push( "!=", pseudos );
+ });
+ }
+
+ rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
+ rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );
+
+ /* Contains
+ ---------------------------------------------------------------------- */
+
+ // Element contains another
+ // Purposefully does not implement inclusive descendent
+ // As in, an element does not contain itself
+ contains = rnative.test( docElem.contains ) || docElem.compareDocumentPosition ?
+ function( a, b ) {
+ var adown = a.nodeType === 9 ? a.documentElement : a,
+ bup = b && b.parentNode;
+ return a === bup || !!( bup && bup.nodeType === 1 && (
+ adown.contains ?
+ adown.contains( bup ) :
+ a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
+ ));
+ } :
+ function( a, b ) {
+ if ( b ) {
+ while ( (b = b.parentNode) ) {
+ if ( b === a ) {
+ return true;
+ }
+ }
+ }
+ return false;
+ };
+
+ /* Sorting
+ ---------------------------------------------------------------------- */
+
+ // Document order sorting
+ sortOrder = docElem.compareDocumentPosition ?
+ function( a, b ) {
+
+ // Flag for duplicate removal
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+ }
+
+ var compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b );
+
+ if ( compare ) {
+ // Disconnected nodes
+ if ( compare & 1 ||
+ (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {
+
+ // Choose the first element that is related to our preferred document
+ if ( a === doc || contains(preferredDoc, a) ) {
+ return -1;
+ }
+ if ( b === doc || contains(preferredDoc, b) ) {
+ return 1;
+ }
+
+ // Maintain original order
+ return sortInput ?
+ ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
+ 0;
+ }
+
+ return compare & 4 ? -1 : 1;
+ }
+
+ // Not directly comparable, sort on existence of method
+ return a.compareDocumentPosition ? -1 : 1;
+ } :
+ function( a, b ) {
+ var cur,
+ i = 0,
+ aup = a.parentNode,
+ bup = b.parentNode,
+ ap = [ a ],
+ bp = [ b ];
+
+ // Exit early if the nodes are identical
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+
+ // Parentless nodes are either documents or disconnected
+ } else if ( !aup || !bup ) {
+ return a === doc ? -1 :
+ b === doc ? 1 :
+ aup ? -1 :
+ bup ? 1 :
+ sortInput ?
+ ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
+ 0;
+
+ // If the nodes are siblings, we can do a quick check
+ } else if ( aup === bup ) {
+ return siblingCheck( a, b );
+ }
+
+ // Otherwise we need full lists of their ancestors for comparison
+ cur = a;
+ while ( (cur = cur.parentNode) ) {
+ ap.unshift( cur );
+ }
+ cur = b;
+ while ( (cur = cur.parentNode) ) {
+ bp.unshift( cur );
+ }
+
+ // Walk down the tree looking for a discrepancy
+ while ( ap[i] === bp[i] ) {
+ i++;
+ }
+
+ return i ?
+ // Do a sibling check if the nodes have a common ancestor
+ siblingCheck( ap[i], bp[i] ) :
+
+ // Otherwise nodes in our document sort first
+ ap[i] === preferredDoc ? -1 :
+ bp[i] === preferredDoc ? 1 :
+ 0;
+ };
+
+ return doc;
+};
+
+Sizzle.matches = function( expr, elements ) {
+ return Sizzle( expr, null, null, elements );
+};
+
+Sizzle.matchesSelector = function( elem, expr ) {
+ // Set document vars if needed
+ if ( ( elem.ownerDocument || elem ) !== document ) {
+ setDocument( elem );
+ }
+
+ // Make sure that attribute selectors are quoted
+ expr = expr.replace( rattributeQuotes, "='$1']" );
+
+ if ( support.matchesSelector && documentIsHTML &&
+ ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
+ ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) {
+
+ try {
+ var ret = matches.call( elem, expr );
+
+ // IE 9's matchesSelector returns false on disconnected nodes
+ if ( ret || support.disconnectedMatch ||
+ // As well, disconnected nodes are said to be in a document
+ // fragment in IE 9
+ elem.document && elem.document.nodeType !== 11 ) {
+ return ret;
+ }
+ } catch(e) {}
+ }
+
+ return Sizzle( expr, document, null, [elem] ).length > 0;
+};
+
+Sizzle.contains = function( context, elem ) {
+ // Set document vars if needed
+ if ( ( context.ownerDocument || context ) !== document ) {
+ setDocument( context );
+ }
+ return contains( context, elem );
+};
+
+Sizzle.attr = function( elem, name ) {
+ // Set document vars if needed
+ if ( ( elem.ownerDocument || elem ) !== document ) {
+ setDocument( elem );
+ }
+
+ var fn = Expr.attrHandle[ name.toLowerCase() ],
+ // Don't get fooled by Object.prototype properties (jQuery #13807)
+ val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
+ fn( elem, name, !documentIsHTML ) :
+ undefined;
+
+ return val === undefined ?
+ support.attributes || !documentIsHTML ?
+ elem.getAttribute( name ) :
+ (val = elem.getAttributeNode(name)) && val.specified ?
+ val.value :
+ null :
+ val;
+};
+
+Sizzle.error = function( msg ) {
+ throw new Error( "Syntax error, unrecognized expression: " + msg );
+};
+
+/**
+ * Document sorting and removing duplicates
+ * @param {ArrayLike} results
+ */
+Sizzle.uniqueSort = function( results ) {
+ var elem,
+ duplicates = [],
+ j = 0,
+ i = 0;
+
+ // Unless we *know* we can detect duplicates, assume their presence
+ hasDuplicate = !support.detectDuplicates;
+ sortInput = !support.sortStable && results.slice( 0 );
+ results.sort( sortOrder );
+
+ if ( hasDuplicate ) {
+ while ( (elem = results[i++]) ) {
+ if ( elem === results[ i ] ) {
+ j = duplicates.push( i );
+ }
+ }
+ while ( j-- ) {
+ results.splice( duplicates[ j ], 1 );
+ }
+ }
+
+ return results;
+};
+
+/**
+ * Utility function for retrieving the text value of an array of DOM nodes
+ * @param {Array|Element} elem
+ */
+getText = Sizzle.getText = function( elem ) {
+ var node,
+ ret = "",
+ i = 0,
+ nodeType = elem.nodeType;
+
+ if ( !nodeType ) {
+ // If no nodeType, this is expected to be an array
+ for ( ; (node = elem[i]); i++ ) {
+ // Do not traverse comment nodes
+ ret += getText( node );
+ }
+ } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
+ // Use textContent for elements
+ // innerText usage removed for consistency of new lines (see #11153)
+ if ( typeof elem.textContent === "string" ) {
+ return elem.textContent;
+ } else {
+ // Traverse its children
+ for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
+ ret += getText( elem );
+ }
+ }
+ } else if ( nodeType === 3 || nodeType === 4 ) {
+ return elem.nodeValue;
+ }
+ // Do not include comment or processing instruction nodes
+
+ return ret;
+};
+
+Expr = Sizzle.selectors = {
+
+ // Can be adjusted by the user
+ cacheLength: 50,
+
+ createPseudo: markFunction,
+
+ match: matchExpr,
+
+ attrHandle: {},
+
+ find: {},
+
+ relative: {
+ ">": { dir: "parentNode", first: true },
+ " ": { dir: "parentNode" },
+ "+": { dir: "previousSibling", first: true },
+ "~": { dir: "previousSibling" }
+ },
+
+ preFilter: {
+ "ATTR": function( match ) {
+ match[1] = match[1].replace( runescape, funescape );
+
+ // Move the given value to match[3] whether quoted or unquoted
+ match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape );
+
+ if ( match[2] === "~=" ) {
+ match[3] = " " + match[3] + " ";
+ }
+
+ return match.slice( 0, 4 );
+ },
+
+ "CHILD": function( match ) {
+ /* matches from matchExpr["CHILD"]
+ 1 type (only|nth|...)
+ 2 what (child|of-type)
+ 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
+ 4 xn-component of xn+y argument ([+-]?\d*n|)
+ 5 sign of xn-component
+ 6 x of xn-component
+ 7 sign of y-component
+ 8 y of y-component
+ */
+ match[1] = match[1].toLowerCase();
+
+ if ( match[1].slice( 0, 3 ) === "nth" ) {
+ // nth-* requires argument
+ if ( !match[3] ) {
+ Sizzle.error( match[0] );
+ }
+
+ // numeric x and y parameters for Expr.filter.CHILD
+ // remember that false/true cast respectively to 0/1
+ match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
+ match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
+
+ // other types prohibit arguments
+ } else if ( match[3] ) {
+ Sizzle.error( match[0] );
+ }
+
+ return match;
+ },
+
+ "PSEUDO": function( match ) {
+ var excess,
+ unquoted = !match[5] && match[2];
+
+ if ( matchExpr["CHILD"].test( match[0] ) ) {
+ return null;
+ }
+
+ // Accept quoted arguments as-is
+ if ( match[3] && match[4] !== undefined ) {
+ match[2] = match[4];
+
+ // Strip excess characters from unquoted arguments
+ } else if ( unquoted && rpseudo.test( unquoted ) &&
+ // Get excess from tokenize (recursively)
+ (excess = tokenize( unquoted, true )) &&
+ // advance to the next closing parenthesis
+ (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
+
+ // excess is a negative index
+ match[0] = match[0].slice( 0, excess );
+ match[2] = unquoted.slice( 0, excess );
+ }
+
+ // Return only captures needed by the pseudo filter method (type and argument)
+ return match.slice( 0, 3 );
+ }
+ },
+
+ filter: {
+
+ "TAG": function( nodeNameSelector ) {
+ var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
+ return nodeNameSelector === "*" ?
+ function() { return true; } :
+ function( elem ) {
+ return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
+ };
+ },
+
+ "CLASS": function( className ) {
+ var pattern = classCache[ className + " " ];
+
+ return pattern ||
+ (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
+ classCache( className, function( elem ) {
+ return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" );
+ });
+ },
+
+ "ATTR": function( name, operator, check ) {
+ return function( elem ) {
+ var result = Sizzle.attr( elem, name );
+
+ if ( result == null ) {
+ return operator === "!=";
+ }
+ if ( !operator ) {
+ return true;
+ }
+
+ result += "";
+
+ return operator === "=" ? result === check :
+ operator === "!=" ? result !== check :
+ operator === "^=" ? check && result.indexOf( check ) === 0 :
+ operator === "*=" ? check && result.indexOf( check ) > -1 :
+ operator === "$=" ? check && result.slice( -check.length ) === check :
+ operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
+ operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
+ false;
+ };
+ },
+
+ "CHILD": function( type, what, argument, first, last ) {
+ var simple = type.slice( 0, 3 ) !== "nth",
+ forward = type.slice( -4 ) !== "last",
+ ofType = what === "of-type";
+
+ return first === 1 && last === 0 ?
+
+ // Shortcut for :nth-*(n)
+ function( elem ) {
+ return !!elem.parentNode;
+ } :
+
+ function( elem, context, xml ) {
+ var cache, outerCache, node, diff, nodeIndex, start,
+ dir = simple !== forward ? "nextSibling" : "previousSibling",
+ parent = elem.parentNode,
+ name = ofType && elem.nodeName.toLowerCase(),
+ useCache = !xml && !ofType;
+
+ if ( parent ) {
+
+ // :(first|last|only)-(child|of-type)
+ if ( simple ) {
+ while ( dir ) {
+ node = elem;
+ while ( (node = node[ dir ]) ) {
+ if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) {
+ return false;
+ }
+ }
+ // Reverse direction for :only-* (if we haven't yet done so)
+ start = dir = type === "only" && !start && "nextSibling";
+ }
+ return true;
}
- for ( i = 0, length = args.length; i < length; i++ ) {
- elem = args[ i ];
- type = jQuery.type( elem );
- if ( type === "array" ) {
- deferred.done.apply( deferred, elem );
- } else if ( type === "function" ) {
- callbacks.push( elem );
+
+ start = [ forward ? parent.firstChild : parent.lastChild ];
+
+ // non-xml :nth-child(...) stores cache data on `parent`
+ if ( forward && useCache ) {
+ // Seek `elem` from a previously-cached index
+ outerCache = parent[ expando ] || (parent[ expando ] = {});
+ cache = outerCache[ type ] || [];
+ nodeIndex = cache[0] === dirruns && cache[1];
+ diff = cache[0] === dirruns && cache[2];
+ node = nodeIndex && parent.childNodes[ nodeIndex ];
+
+ while ( (node = ++nodeIndex && node && node[ dir ] ||
+
+ // Fallback to seeking `elem` from the start
+ (diff = nodeIndex = 0) || start.pop()) ) {
+
+ // When found, cache indexes on `parent` and break
+ if ( node.nodeType === 1 && ++diff && node === elem ) {
+ outerCache[ type ] = [ dirruns, nodeIndex, diff ];
+ break;
+ }
+ }
+
+ // Use previously-cached element index if available
+ } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) {
+ diff = cache[1];
+
+ // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...)
+ } else {
+ // Use the same loop as above to seek `elem` from the start
+ while ( (node = ++nodeIndex && node && node[ dir ] ||
+ (diff = nodeIndex = 0) || start.pop()) ) {
+
+ if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) {
+ // Cache the index of each encountered element
+ if ( useCache ) {
+ (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ];
+ }
+
+ if ( node === elem ) {
+ break;
+ }
+ }
}
}
- if ( _fired ) {
- deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] );
- }
+
+ // Incorporate the offset, then check against cycle size
+ diff -= last;
+ return diff === first || ( diff % first === 0 && diff / first >= 0 );
}
- return this;
- },
-
- // resolve with given context and args
- resolveWith: function( context, args ) {
- if ( !cancelled && !fired && !firing ) {
- // make sure args are available (#8421)
- args = args || [];
- firing = 1;
- try {
- while( callbacks[ 0 ] ) {
- callbacks.shift().apply( context, args );
- }
+ };
+ },
+
+ "PSEUDO": function( pseudo, argument ) {
+ // pseudo-class names are case-insensitive
+ // http://www.w3.org/TR/selectors/#pseudo-classes
+ // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
+ // Remember that setFilters inherits from pseudos
+ var args,
+ fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
+ Sizzle.error( "unsupported pseudo: " + pseudo );
+
+ // The user may use createPseudo to indicate that
+ // arguments are needed to create the filter function
+ // just as Sizzle does
+ if ( fn[ expando ] ) {
+ return fn( argument );
+ }
+
+ // But maintain support for old signatures
+ if ( fn.length > 1 ) {
+ args = [ pseudo, pseudo, "", argument ];
+ return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
+ markFunction(function( seed, matches ) {
+ var idx,
+ matched = fn( seed, argument ),
+ i = matched.length;
+ while ( i-- ) {
+ idx = indexOf.call( seed, matched[i] );
+ seed[ idx ] = !( matches[ idx ] = matched[i] );
}
- finally {
- fired = [ context, args ];
- firing = 0;
+ }) :
+ function( elem ) {
+ return fn( elem, 0, args );
+ };
+ }
+
+ return fn;
+ }
+ },
+
+ pseudos: {
+ // Potentially complex pseudos
+ "not": markFunction(function( selector ) {
+ // Trim the selector passed to compile
+ // to avoid treating leading and trailing
+ // spaces as combinators
+ var input = [],
+ results = [],
+ matcher = compile( selector.replace( rtrim, "$1" ) );
+
+ return matcher[ expando ] ?
+ markFunction(function( seed, matches, context, xml ) {
+ var elem,
+ unmatched = matcher( seed, null, xml, [] ),
+ i = seed.length;
+
+ // Match elements unmatched by `matcher`
+ while ( i-- ) {
+ if ( (elem = unmatched[i]) ) {
+ seed[i] = !(matches[i] = elem);
}
}
- return this;
- },
-
- // resolve with this as context and given arguments
- resolve: function() {
- deferred.resolveWith( this, arguments );
- return this;
- },
-
- // Has this deferred been resolved?
- isResolved: function() {
- return !!( firing || fired );
- },
-
- // Cancel
- cancel: function() {
- cancelled = 1;
- callbacks = [];
- return this;
- }
+ }) :
+ function( elem, context, xml ) {
+ input[0] = elem;
+ matcher( input, null, xml, results );
+ return !results.pop();
+ };
+ }),
+
+ "has": markFunction(function( selector ) {
+ return function( elem ) {
+ return Sizzle( selector, elem ).length > 0;
+ };
+ }),
+
+ "contains": markFunction(function( text ) {
+ return function( elem ) {
+ return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
+ };
+ }),
+
+ // "Whether an element is represented by a :lang() selector
+ // is based solely on the element's language value
+ // being equal to the identifier C,
+ // or beginning with the identifier C immediately followed by "-".
+ // The matching of C against the element's language value is performed case-insensitively.
+ // The identifier C does not have to be a valid language name."
+ // http://www.w3.org/TR/selectors/#lang-pseudo
+ "lang": markFunction( function( lang ) {
+ // lang value must be a valid identifier
+ if ( !ridentifier.test(lang || "") ) {
+ Sizzle.error( "unsupported lang: " + lang );
+ }
+ lang = lang.replace( runescape, funescape ).toLowerCase();
+ return function( elem ) {
+ var elemLang;
+ do {
+ if ( (elemLang = documentIsHTML ?
+ elem.lang :
+ elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {
+
+ elemLang = elemLang.toLowerCase();
+ return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
+ }
+ } while ( (elem = elem.parentNode) && elem.nodeType === 1 );
+ return false;
};
-
- return deferred;
- },
-
- // Full fledged deferred (two callbacks list)
- Deferred: function( func ) {
- var deferred = jQuery._Deferred(),
- failDeferred = jQuery._Deferred(),
- promise;
- // Add errorDeferred methods, then and promise
- jQuery.extend( deferred, {
- then: function( doneCallbacks, failCallbacks ) {
- deferred.done( doneCallbacks ).fail( failCallbacks );
+ }),
+
+ // Miscellaneous
+ "target": function( elem ) {
+ var hash = window.location && window.location.hash;
+ return hash && hash.slice( 1 ) === elem.id;
+ },
+
+ "root": function( elem ) {
+ return elem === docElem;
+ },
+
+ "focus": function( elem ) {
+ return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
+ },
+
+ // Boolean properties
+ "enabled": function( elem ) {
+ return elem.disabled === false;
+ },
+
+ "disabled": function( elem ) {
+ return elem.disabled === true;
+ },
+
+ "checked": function( elem ) {
+ // In CSS3, :checked should return both checked and selected elements
+ // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
+ var nodeName = elem.nodeName.toLowerCase();
+ return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
+ },
+
+ "selected": function( elem ) {
+ // Accessing this property makes selected-by-default
+ // options in Safari work properly
+ if ( elem.parentNode ) {
+ elem.parentNode.selectedIndex;
+ }
+
+ return elem.selected === true;
+ },
+
+ // Contents
+ "empty": function( elem ) {
+ // http://www.w3.org/TR/selectors/#empty-pseudo
+ // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)),
+ // not comment, processing instructions, or others
+ // Thanks to Diego Perini for the nodeName shortcut
+ // Greater than "@" means alpha characters (specifically not starting with "#" or "?")
+ for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
+ if ( elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4 ) {
+ return false;
+ }
+ }
+ return true;
+ },
+
+ "parent": function( elem ) {
+ return !Expr.pseudos["empty"]( elem );
+ },
+
+ // Element/input types
+ "header": function( elem ) {
+ return rheader.test( elem.nodeName );
+ },
+
+ "input": function( elem ) {
+ return rinputs.test( elem.nodeName );
+ },
+
+ "button": function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return name === "input" && elem.type === "button" || name === "button";
+ },
+
+ "text": function( elem ) {
+ var attr;
+ // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
+ // use getAttribute instead to test this case
+ return elem.nodeName.toLowerCase() === "input" &&
+ elem.type === "text" &&
+ ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === elem.type );
+ },
+
+ // Position-in-collection
+ "first": createPositionalPseudo(function() {
+ return [ 0 ];
+ }),
+
+ "last": createPositionalPseudo(function( matchIndexes, length ) {
+ return [ length - 1 ];
+ }),
+
+ "eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
+ return [ argument < 0 ? argument + length : argument ];
+ }),
+
+ "even": createPositionalPseudo(function( matchIndexes, length ) {
+ var i = 0;
+ for ( ; i < length; i += 2 ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ }),
+
+ "odd": createPositionalPseudo(function( matchIndexes, length ) {
+ var i = 1;
+ for ( ; i < length; i += 2 ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ }),
+
+ "lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
+ var i = argument < 0 ? argument + length : argument;
+ for ( ; --i >= 0; ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ }),
+
+ "gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
+ var i = argument < 0 ? argument + length : argument;
+ for ( ; ++i < length; ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ })
+ }
+};
+
+Expr.pseudos["nth"] = Expr.pseudos["eq"];
+
+// Add button/input type pseudos
+for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
+ Expr.pseudos[ i ] = createInputPseudo( i );
+}
+for ( i in { submit: true, reset: true } ) {
+ Expr.pseudos[ i ] = createButtonPseudo( i );
+}
+
+// Easy API for creating new setFilters
+function setFilters() {}
+setFilters.prototype = Expr.filters = Expr.pseudos;
+Expr.setFilters = new setFilters();
+
+function tokenize( selector, parseOnly ) {
+ var matched, match, tokens, type,
+ soFar, groups, preFilters,
+ cached = tokenCache[ selector + " " ];
+
+ if ( cached ) {
+ return parseOnly ? 0 : cached.slice( 0 );
+ }
+
+ soFar = selector;
+ groups = [];
+ preFilters = Expr.preFilter;
+
+ while ( soFar ) {
+
+ // Comma and first run
+ if ( !matched || (match = rcomma.exec( soFar )) ) {
+ if ( match ) {
+ // Don't consume trailing commas as valid
+ soFar = soFar.slice( match[0].length ) || soFar;
+ }
+ groups.push( tokens = [] );
+ }
+
+ matched = false;
+
+ // Combinators
+ if ( (match = rcombinators.exec( soFar )) ) {
+ matched = match.shift();
+ tokens.push({
+ value: matched,
+ // Cast descendant combinators to space
+ type: match[0].replace( rtrim, " " )
+ });
+ soFar = soFar.slice( matched.length );
+ }
+
+ // Filters
+ for ( type in Expr.filter ) {
+ if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
+ (match = preFilters[ type ]( match ))) ) {
+ matched = match.shift();
+ tokens.push({
+ value: matched,
+ type: type,
+ matches: match
+ });
+ soFar = soFar.slice( matched.length );
+ }
+ }
+
+ if ( !matched ) {
+ break;
+ }
+ }
+
+ // Return the length of the invalid excess
+ // if we're just parsing
+ // Otherwise, throw an error or return tokens
+ return parseOnly ?
+ soFar.length :
+ soFar ?
+ Sizzle.error( selector ) :
+ // Cache the tokens
+ tokenCache( selector, groups ).slice( 0 );
+}
+
+function toSelector( tokens ) {
+ var i = 0,
+ len = tokens.length,
+ selector = "";
+ for ( ; i < len; i++ ) {
+ selector += tokens[i].value;
+ }
+ return selector;
+}
+
+function addCombinator( matcher, combinator, base ) {
+ var dir = combinator.dir,
+ checkNonElements = base && dir === "parentNode",
+ doneName = done++;
+
+ return combinator.first ?
+ // Check against closest ancestor/preceding element
+ function( elem, context, xml ) {
+ while ( (elem = elem[ dir ]) ) {
+ if ( elem.nodeType === 1 || checkNonElements ) {
+ return matcher( elem, context, xml );
+ }
+ }
+ } :
+
+ // Check against all ancestor/preceding elements
+ function( elem, context, xml ) {
+ var data, cache, outerCache,
+ dirkey = dirruns + " " + doneName;
+
+ // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
+ if ( xml ) {
+ while ( (elem = elem[ dir ]) ) {
+ if ( elem.nodeType === 1 || checkNonElements ) {
+ if ( matcher( elem, context, xml ) ) {
+ return true;
+ }
+ }
+ }
+ } else {
+ while ( (elem = elem[ dir ]) ) {
+ if ( elem.nodeType === 1 || checkNonElements ) {
+ outerCache = elem[ expando ] || (elem[ expando ] = {});
+ if ( (cache = outerCache[ dir ]) && cache[0] === dirkey ) {
+ if ( (data = cache[1]) === true || data === cachedruns ) {
+ return data === true;
+ }
+ } else {
+ cache = outerCache[ dir ] = [ dirkey ];
+ cache[1] = matcher( elem, context, xml ) || cachedruns;
+ if ( cache[1] === true ) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ };
+}
+
+function elementMatcher( matchers ) {
+ return matchers.length > 1 ?
+ function( elem, context, xml ) {
+ var i = matchers.length;
+ while ( i-- ) {
+ if ( !matchers[i]( elem, context, xml ) ) {
+ return false;
+ }
+ }
+ return true;
+ } :
+ matchers[0];
+}
+
+function condense( unmatched, map, filter, context, xml ) {
+ var elem,
+ newUnmatched = [],
+ i = 0,
+ len = unmatched.length,
+ mapped = map != null;
+
+ for ( ; i < len; i++ ) {
+ if ( (elem = unmatched[i]) ) {
+ if ( !filter || filter( elem, context, xml ) ) {
+ newUnmatched.push( elem );
+ if ( mapped ) {
+ map.push( i );
+ }
+ }
+ }
+ }
+
+ return newUnmatched;
+}
+
+function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
+ if ( postFilter && !postFilter[ expando ] ) {
+ postFilter = setMatcher( postFilter );
+ }
+ if ( postFinder && !postFinder[ expando ] ) {
+ postFinder = setMatcher( postFinder, postSelector );
+ }
+ return markFunction(function( seed, results, context, xml ) {
+ var temp, i, elem,
+ preMap = [],
+ postMap = [],
+ preexisting = results.length,
+
+ // Get initial elements from seed or context
+ elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
+
+ // Prefilter to get matcher input, preserving a map for seed-results synchronization
+ matcherIn = preFilter && ( seed || !selector ) ?
+ condense( elems, preMap, preFilter, context, xml ) :
+ elems,
+
+ matcherOut = matcher ?
+ // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
+ postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
+
+ // ...intermediate processing is necessary
+ [] :
+
+ // ...otherwise use results directly
+ results :
+ matcherIn;
+
+ // Find primary matches
+ if ( matcher ) {
+ matcher( matcherIn, matcherOut, context, xml );
+ }
+
+ // Apply postFilter
+ if ( postFilter ) {
+ temp = condense( matcherOut, postMap );
+ postFilter( temp, [], context, xml );
+
+ // Un-match failing elements by moving them back to matcherIn
+ i = temp.length;
+ while ( i-- ) {
+ if ( (elem = temp[i]) ) {
+ matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
+ }
+ }
+ }
+
+ if ( seed ) {
+ if ( postFinder || preFilter ) {
+ if ( postFinder ) {
+ // Get the final matcherOut by condensing this intermediate into postFinder contexts
+ temp = [];
+ i = matcherOut.length;
+ while ( i-- ) {
+ if ( (elem = matcherOut[i]) ) {
+ // Restore matcherIn since elem is not yet a final match
+ temp.push( (matcherIn[i] = elem) );
+ }
+ }
+ postFinder( null, (matcherOut = []), temp, xml );
+ }
+
+ // Move matched elements from seed to results to keep them synchronized
+ i = matcherOut.length;
+ while ( i-- ) {
+ if ( (elem = matcherOut[i]) &&
+ (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
+
+ seed[temp] = !(results[temp] = elem);
+ }
+ }
+ }
+
+ // Add elements to results, through postFinder if defined
+ } else {
+ matcherOut = condense(
+ matcherOut === results ?
+ matcherOut.splice( preexisting, matcherOut.length ) :
+ matcherOut
+ );
+ if ( postFinder ) {
+ postFinder( null, results, matcherOut, xml );
+ } else {
+ push.apply( results, matcherOut );
+ }
+ }
+ });
+}
+
+function matcherFromTokens( tokens ) {
+ var checkContext, matcher, j,
+ len = tokens.length,
+ leadingRelative = Expr.relative[ tokens[0].type ],
+ implicitRelative = leadingRelative || Expr.relative[" "],
+ i = leadingRelative ? 1 : 0,
+
+ // The foundational matcher ensures that elements are reachable from top-level context(s)
+ matchContext = addCombinator( function( elem ) {
+ return elem === checkContext;
+ }, implicitRelative, true ),
+ matchAnyContext = addCombinator( function( elem ) {
+ return indexOf.call( checkContext, elem ) > -1;
+ }, implicitRelative, true ),
+ matchers = [ function( elem, context, xml ) {
+ return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
+ (checkContext = context).nodeType ?
+ matchContext( elem, context, xml ) :
+ matchAnyContext( elem, context, xml ) );
+ } ];
+
+ for ( ; i < len; i++ ) {
+ if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
+ matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
+ } else {
+ matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
+
+ // Return special upon seeing a positional matcher
+ if ( matcher[ expando ] ) {
+ // Find the next relative operator (if any) for proper handling
+ j = ++i;
+ for ( ; j < len; j++ ) {
+ if ( Expr.relative[ tokens[j].type ] ) {
+ break;
+ }
+ }
+ return setMatcher(
+ i > 1 && elementMatcher( matchers ),
+ i > 1 && toSelector(
+ // If the preceding token was a descendant combinator, insert an implicit any-element `*`
+ tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
+ ).replace( rtrim, "$1" ),
+ matcher,
+ i < j && matcherFromTokens( tokens.slice( i, j ) ),
+ j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
+ j < len && toSelector( tokens )
+ );
+ }
+ matchers.push( matcher );
+ }
+ }
+
+ return elementMatcher( matchers );
+}
+
+function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
+ // A counter to specify which element is currently being matched
+ var matcherCachedRuns = 0,
+ bySet = setMatchers.length > 0,
+ byElement = elementMatchers.length > 0,
+ superMatcher = function( seed, context, xml, results, expandContext ) {
+ var elem, j, matcher,
+ setMatched = [],
+ matchedCount = 0,
+ i = "0",
+ unmatched = seed && [],
+ outermost = expandContext != null,
+ contextBackup = outermostContext,
+ // We must always have either seed elements or context
+ elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ),
+ // Use integer dirruns iff this is the outermost matcher
+ dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1);
+
+ if ( outermost ) {
+ outermostContext = context !== document && context;
+ cachedruns = matcherCachedRuns;
+ }
+
+ // Add elements passing elementMatchers directly to results
+ // Keep `i` a string if there are no elements so `matchedCount` will be "00" below
+ for ( ; (elem = elems[i]) != null; i++ ) {
+ if ( byElement && elem ) {
+ j = 0;
+ while ( (matcher = elementMatchers[j++]) ) {
+ if ( matcher( elem, context, xml ) ) {
+ results.push( elem );
+ break;
+ }
+ }
+ if ( outermost ) {
+ dirruns = dirrunsUnique;
+ cachedruns = ++matcherCachedRuns;
+ }
+ }
+
+ // Track unmatched elements for set filters
+ if ( bySet ) {
+ // They will have gone through all possible matchers
+ if ( (elem = !matcher && elem) ) {
+ matchedCount--;
+ }
+
+ // Lengthen the array for every element, matched or not
+ if ( seed ) {
+ unmatched.push( elem );
+ }
+ }
+ }
+
+ // Apply set filters to unmatched elements
+ matchedCount += i;
+ if ( bySet && i !== matchedCount ) {
+ j = 0;
+ while ( (matcher = setMatchers[j++]) ) {
+ matcher( unmatched, setMatched, context, xml );
+ }
+
+ if ( seed ) {
+ // Reintegrate element matches to eliminate the need for sorting
+ if ( matchedCount > 0 ) {
+ while ( i-- ) {
+ if ( !(unmatched[i] || setMatched[i]) ) {
+ setMatched[i] = pop.call( results );
+ }
+ }
+ }
+
+ // Discard index placeholder values to get only actual matches
+ setMatched = condense( setMatched );
+ }
+
+ // Add matches to results
+ push.apply( results, setMatched );
+
+ // Seedless set matches succeeding multiple successful matchers stipulate sorting
+ if ( outermost && !seed && setMatched.length > 0 &&
+ ( matchedCount + setMatchers.length ) > 1 ) {
+
+ Sizzle.uniqueSort( results );
+ }
+ }
+
+ // Override manipulation of globals by nested matchers
+ if ( outermost ) {
+ dirruns = dirrunsUnique;
+ outermostContext = contextBackup;
+ }
+
+ return unmatched;
+ };
+
+ return bySet ?
+ markFunction( superMatcher ) :
+ superMatcher;
+}
+
+compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) {
+ var i,
+ setMatchers = [],
+ elementMatchers = [],
+ cached = compilerCache[ selector + " " ];
+
+ if ( !cached ) {
+ // Generate a function of recursive functions that can be used to check each element
+ if ( !group ) {
+ group = tokenize( selector );
+ }
+ i = group.length;
+ while ( i-- ) {
+ cached = matcherFromTokens( group[i] );
+ if ( cached[ expando ] ) {
+ setMatchers.push( cached );
+ } else {
+ elementMatchers.push( cached );
+ }
+ }
+
+ // Cache the compiled function
+ cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
+ }
+ return cached;
+};
+
+function multipleContexts( selector, contexts, results ) {
+ var i = 0,
+ len = contexts.length;
+ for ( ; i < len; i++ ) {
+ Sizzle( selector, contexts[i], results );
+ }
+ return results;
+}
+
+function select( selector, context, results, seed ) {
+ var i, tokens, token, type, find,
+ match = tokenize( selector );
+
+ if ( !seed ) {
+ // Try to minimize operations if there is only one group
+ if ( match.length === 1 ) {
+
+ // Take a shortcut and set the context if the root selector is an ID
+ tokens = match[0] = match[0].slice( 0 );
+ if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
+ support.getById && context.nodeType === 9 && documentIsHTML &&
+ Expr.relative[ tokens[1].type ] ) {
+
+ context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
+ if ( !context ) {
+ return results;
+ }
+ selector = selector.slice( tokens.shift().value.length );
+ }
+
+ // Fetch a seed set for right-to-left matching
+ i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
+ while ( i-- ) {
+ token = tokens[i];
+
+ // Abort if we hit a combinator
+ if ( Expr.relative[ (type = token.type) ] ) {
+ break;
+ }
+ if ( (find = Expr.find[ type ]) ) {
+ // Search, expanding context for leading sibling combinators
+ if ( (seed = find(
+ token.matches[0].replace( runescape, funescape ),
+ rsibling.test( tokens[0].type ) && context.parentNode || context
+ )) ) {
+
+ // If seed is empty or no tokens remain, we can return early
+ tokens.splice( i, 1 );
+ selector = seed.length && toSelector( tokens );
+ if ( !selector ) {
+ push.apply( results, seed );
+ return results;
+ }
+
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Compile and execute a filtering function
+ // Provide `match` to avoid retokenization if we modified the selector above
+ compile( selector, match )(
+ seed,
+ context,
+ !documentIsHTML,
+ results,
+ rsibling.test( selector )
+ );
+ return results;
+}
+
+// One-time assignments
+
+// Sort stability
+support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;
+
+// Support: Chrome<14
+// Always assume duplicates if they aren't passed to the comparison function
+support.detectDuplicates = hasDuplicate;
+
+// Initialize against the default document
+setDocument();
+
+// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
+// Detached nodes confoundingly follow *each other*
+support.sortDetached = assert(function( div1 ) {
+ // Should return 1, but returns 4 (following)
+ return div1.compareDocumentPosition( document.createElement("div") ) & 1;
+});
+
+// Support: IE<8
+// Prevent attribute/property "interpolation"
+// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
+if ( !assert(function( div ) {
+ div.innerHTML = " ";
+ return div.firstChild.getAttribute("href") === "#" ;
+}) ) {
+ addHandle( "type|href|height|width", function( elem, name, isXML ) {
+ if ( !isXML ) {
+ return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
+ }
+ });
+}
+
+// Support: IE<9
+// Use defaultValue in place of getAttribute("value")
+if ( !support.attributes || !assert(function( div ) {
+ div.innerHTML = " ";
+ div.firstChild.setAttribute( "value", "" );
+ return div.firstChild.getAttribute( "value" ) === "";
+}) ) {
+ addHandle( "value", function( elem, name, isXML ) {
+ if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
+ return elem.defaultValue;
+ }
+ });
+}
+
+// Support: IE<9
+// Use getAttributeNode to fetch booleans when getAttribute lies
+if ( !assert(function( div ) {
+ return div.getAttribute("disabled") == null;
+}) ) {
+ addHandle( booleans, function( elem, name, isXML ) {
+ var val;
+ if ( !isXML ) {
+ return (val = elem.getAttributeNode( name )) && val.specified ?
+ val.value :
+ elem[ name ] === true ? name.toLowerCase() : null;
+ }
+ });
+}
+
+jQuery.find = Sizzle;
+jQuery.expr = Sizzle.selectors;
+jQuery.expr[":"] = jQuery.expr.pseudos;
+jQuery.unique = Sizzle.uniqueSort;
+jQuery.text = Sizzle.getText;
+jQuery.isXMLDoc = Sizzle.isXML;
+jQuery.contains = Sizzle.contains;
+
+
+})( window );
+// String to Object options format cache
+var optionsCache = {};
+
+// Convert String-formatted options into Object-formatted ones and store in cache
+function createOptions( options ) {
+ var object = optionsCache[ options ] = {};
+ jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) {
+ object[ flag ] = true;
+ });
+ return object;
+}
+
+/*
+ * Create a callback list using the following parameters:
+ *
+ * options: an optional list of space-separated options that will change how
+ * the callback list behaves or a more traditional option object
+ *
+ * By default a callback list will act like an event callback list and can be
+ * "fired" multiple times.
+ *
+ * Possible options:
+ *
+ * once: will ensure the callback list can only be fired once (like a Deferred)
+ *
+ * memory: will keep track of previous values and will call any callback added
+ * after the list has been fired right away with the latest "memorized"
+ * values (like a Deferred)
+ *
+ * unique: will ensure a callback can only be added once (no duplicate in the list)
+ *
+ * stopOnFalse: interrupt callings when a callback returns false
+ *
+ */
+jQuery.Callbacks = function( options ) {
+
+ // Convert options from String-formatted to Object-formatted if needed
+ // (we check in cache first)
+ options = typeof options === "string" ?
+ ( optionsCache[ options ] || createOptions( options ) ) :
+ jQuery.extend( {}, options );
+
+ var // Flag to know if list is currently firing
+ firing,
+ // Last fire value (for non-forgettable lists)
+ memory,
+ // Flag to know if list was already fired
+ fired,
+ // End of the loop when firing
+ firingLength,
+ // Index of currently firing callback (modified by remove if needed)
+ firingIndex,
+ // First callback to fire (used internally by add and fireWith)
+ firingStart,
+ // Actual callback list
+ list = [],
+ // Stack of fire calls for repeatable lists
+ stack = !options.once && [],
+ // Fire callbacks
+ fire = function( data ) {
+ memory = options.memory && data;
+ fired = true;
+ firingIndex = firingStart || 0;
+ firingStart = 0;
+ firingLength = list.length;
+ firing = true;
+ for ( ; list && firingIndex < firingLength; firingIndex++ ) {
+ if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
+ memory = false; // To prevent further calls using add
+ break;
+ }
+ }
+ firing = false;
+ if ( list ) {
+ if ( stack ) {
+ if ( stack.length ) {
+ fire( stack.shift() );
+ }
+ } else if ( memory ) {
+ list = [];
+ } else {
+ self.disable();
+ }
+ }
+ },
+ // Actual Callbacks object
+ self = {
+ // Add a callback or a collection of callbacks to the list
+ add: function() {
+ if ( list ) {
+ // First, we save the current length
+ var start = list.length;
+ (function add( args ) {
+ jQuery.each( args, function( _, arg ) {
+ var type = jQuery.type( arg );
+ if ( type === "function" ) {
+ if ( !options.unique || !self.has( arg ) ) {
+ list.push( arg );
+ }
+ } else if ( arg && arg.length && type !== "string" ) {
+ // Inspect recursively
+ add( arg );
+ }
+ });
+ })( arguments );
+ // Do we need to add the callbacks to the
+ // current firing batch?
+ if ( firing ) {
+ firingLength = list.length;
+ // With memory, if we're not firing then
+ // we should call right away
+ } else if ( memory ) {
+ firingStart = start;
+ fire( memory );
+ }
+ }
return this;
},
- always: function() {
- return deferred.done.apply( deferred, arguments ).fail.apply( this, arguments );
+ // Remove a callback from the list
+ remove: function() {
+ if ( list ) {
+ jQuery.each( arguments, function( _, arg ) {
+ var index;
+ while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
+ list.splice( index, 1 );
+ // Handle firing indexes
+ if ( firing ) {
+ if ( index <= firingLength ) {
+ firingLength--;
+ }
+ if ( index <= firingIndex ) {
+ firingIndex--;
+ }
+ }
+ }
+ });
+ }
+ return this;
+ },
+ // Check if a given callback is in the list.
+ // If no argument is given, return whether or not list has callbacks attached.
+ has: function( fn ) {
+ return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
+ },
+ // Remove all callbacks from the list
+ empty: function() {
+ list = [];
+ firingLength = 0;
+ return this;
+ },
+ // Have the list do nothing anymore
+ disable: function() {
+ list = stack = memory = undefined;
+ return this;
+ },
+ // Is it disabled?
+ disabled: function() {
+ return !list;
+ },
+ // Lock the list in its current state
+ lock: function() {
+ stack = undefined;
+ if ( !memory ) {
+ self.disable();
+ }
+ return this;
+ },
+ // Is it locked?
+ locked: function() {
+ return !stack;
},
- fail: failDeferred.done,
- rejectWith: failDeferred.resolveWith,
- reject: failDeferred.resolve,
- isRejected: failDeferred.isResolved,
- pipe: function( fnDone, fnFail ) {
- return jQuery.Deferred(function( newDefer ) {
- jQuery.each( {
- done: [ fnDone, "resolve" ],
- fail: [ fnFail, "reject" ]
- }, function( handler, data ) {
- var fn = data[ 0 ],
- action = data[ 1 ],
- returned;
- if ( jQuery.isFunction( fn ) ) {
- deferred[ handler ](function() {
- returned = fn.apply( this, arguments );
+ // Call all callbacks with the given context and arguments
+ fireWith: function( context, args ) {
+ if ( list && ( !fired || stack ) ) {
+ args = args || [];
+ args = [ context, args.slice ? args.slice() : args ];
+ if ( firing ) {
+ stack.push( args );
+ } else {
+ fire( args );
+ }
+ }
+ return this;
+ },
+ // Call all the callbacks with the given arguments
+ fire: function() {
+ self.fireWith( this, arguments );
+ return this;
+ },
+ // To know if the callbacks have already been called at least once
+ fired: function() {
+ return !!fired;
+ }
+ };
+
+ return self;
+};
+jQuery.extend({
+
+ Deferred: function( func ) {
+ var tuples = [
+ // action, add listener, listener list, final state
+ [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
+ [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
+ [ "notify", "progress", jQuery.Callbacks("memory") ]
+ ],
+ state = "pending",
+ promise = {
+ state: function() {
+ return state;
+ },
+ always: function() {
+ deferred.done( arguments ).fail( arguments );
+ return this;
+ },
+ then: function( /* fnDone, fnFail, fnProgress */ ) {
+ var fns = arguments;
+ return jQuery.Deferred(function( newDefer ) {
+ jQuery.each( tuples, function( i, tuple ) {
+ var action = tuple[ 0 ],
+ fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
+ // deferred[ done | fail | progress ] for forwarding actions to newDefer
+ deferred[ tuple[1] ](function() {
+ var returned = fn && fn.apply( this, arguments );
if ( returned && jQuery.isFunction( returned.promise ) ) {
- returned.promise().then( newDefer.resolve, newDefer.reject );
+ returned.promise()
+ .done( newDefer.resolve )
+ .fail( newDefer.reject )
+ .progress( newDefer.notify );
} else {
- newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] );
+ newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
}
});
- } else {
- deferred[ handler ]( newDefer[ action ] );
- }
- });
- }).promise();
+ });
+ fns = null;
+ }).promise();
+ },
+ // Get a promise for this deferred
+ // If obj is provided, the promise aspect is added to the object
+ promise: function( obj ) {
+ return obj != null ? jQuery.extend( obj, promise ) : promise;
+ }
},
- // Get a promise for this deferred
- // If obj is provided, the promise aspect is added to the object
- promise: function( obj ) {
- if ( obj == null ) {
- if ( promise ) {
- return promise;
- }
- promise = obj = {};
- }
- var i = promiseMethods.length;
- while( i-- ) {
- obj[ promiseMethods[i] ] = deferred[ promiseMethods[i] ];
- }
- return obj;
- }
+ deferred = {};
+
+ // Keep pipe for back-compat
+ promise.pipe = promise.then;
+
+ // Add list-specific methods
+ jQuery.each( tuples, function( i, tuple ) {
+ var list = tuple[ 2 ],
+ stateString = tuple[ 3 ];
+
+ // promise[ done | fail | progress ] = list.add
+ promise[ tuple[1] ] = list.add;
+
+ // Handle state
+ if ( stateString ) {
+ list.add(function() {
+ // state = [ resolved | rejected ]
+ state = stateString;
+
+ // [ reject_list | resolve_list ].disable; progress_list.lock
+ }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
+ }
+
+ // deferred[ resolve | reject | notify ]
+ deferred[ tuple[0] ] = function() {
+ deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
+ return this;
+ };
+ deferred[ tuple[0] + "With" ] = list.fireWith;
});
- // Make sure only one callback list will be used
- deferred.done( failDeferred.cancel ).fail( deferred.cancel );
- // Unexpose cancel
- delete deferred.cancel;
+
+ // Make the deferred a promise
+ promise.promise( deferred );
+
// Call given func if any
if ( func ) {
func.call( deferred, deferred );
}
+
+ // All done!
return deferred;
},
// Deferred helper
- when: function( firstParam ) {
- var args = arguments,
- i = 0,
- length = args.length,
- count = length,
- deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ?
- firstParam :
- jQuery.Deferred();
- function resolveFunc( i ) {
- return function( value ) {
- args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
- if ( !( --count ) ) {
- // Strange bug in FF4:
- // Values changed onto the arguments object sometimes end up as undefined values
- // outside the $.when method. Cloning the object into a fresh array solves the issue
- deferred.resolveWith( deferred, sliceDeferred.call( args, 0 ) );
- }
- };
- }
+ when: function( subordinate /* , ..., subordinateN */ ) {
+ var i = 0,
+ resolveValues = core_slice.call( arguments ),
+ length = resolveValues.length,
+
+ // the count of uncompleted subordinates
+ remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
+
+ // the master Deferred. If resolveValues consist of only a single Deferred, just use that.
+ deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
+
+ // Update function for both resolve and progress values
+ updateFunc = function( i, contexts, values ) {
+ return function( value ) {
+ contexts[ i ] = this;
+ values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value;
+ if( values === progressValues ) {
+ deferred.notifyWith( contexts, values );
+ } else if ( !( --remaining ) ) {
+ deferred.resolveWith( contexts, values );
+ }
+ };
+ },
+
+ progressValues, progressContexts, resolveContexts;
+
+ // add listeners to Deferred subordinates; treat others as resolved
if ( length > 1 ) {
- for( ; i < length; i++ ) {
- if ( args[ i ] && jQuery.isFunction( args[ i ].promise ) ) {
- args[ i ].promise().then( resolveFunc(i), deferred.reject );
+ progressValues = new Array( length );
+ progressContexts = new Array( length );
+ resolveContexts = new Array( length );
+ for ( ; i < length; i++ ) {
+ if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
+ resolveValues[ i ].promise()
+ .done( updateFunc( i, resolveContexts, resolveValues ) )
+ .fail( deferred.reject )
+ .progress( updateFunc( i, progressContexts, progressValues ) );
} else {
- --count;
- }
- }
- if ( !count ) {
- deferred.resolveWith( deferred, args );
- }
- } else if ( deferred !== firstParam ) {
- deferred.resolveWith( deferred, length ? [ firstParam ] : [] );
- }
+ --remaining;
+ }
+ }
+ }
+
+ // if we're not waiting on anything, resolve the master
+ if ( !remaining ) {
+ deferred.resolveWith( resolveContexts, resolveValues );
+ }
+
return deferred.promise();
}
});
-
-
-
-jQuery.support = (function() {
-
- var div = document.createElement( "div" ),
- documentElement = document.documentElement,
- all,
- a,
- select,
- opt,
- input,
- marginDiv,
- support,
- fragment,
- body,
- testElementParent,
- testElement,
- testElementStyle,
- tds,
- events,
- eventName,
- i,
- isSupported;
-
- // Preliminary tests
- div.setAttribute("className", "t");
- div.innerHTML = " a ";
-
-
- all = div.getElementsByTagName( "*" );
- a = div.getElementsByTagName( "a" )[ 0 ];
-
- // Can't get basic test support
- if ( !all || !all.length || !a ) {
- return {};
- }
-
- // First batch of supports tests
- select = document.createElement( "select" );
+jQuery.support = (function( support ) {
+
+ var all, a, input, select, fragment, opt, eventName, isSupported, i,
+ div = document.createElement("div");
+
+ // Setup
+ div.setAttribute( "className", "t" );
+ div.innerHTML = " a ";
+
+ // Finish early in limited (non-browser) environments
+ all = div.getElementsByTagName("*") || [];
+ a = div.getElementsByTagName("a")[ 0 ];
+ if ( !a || !a.style || !all.length ) {
+ return support;
+ }
+
+ // First batch of tests
+ select = document.createElement("select");
opt = select.appendChild( document.createElement("option") );
- input = div.getElementsByTagName( "input" )[ 0 ];
-
- support = {
- // IE strips leading whitespace when .innerHTML is used
- leadingWhitespace: ( div.firstChild.nodeType === 3 ),
-
- // Make sure that tbody elements aren't automatically inserted
- // IE will insert them into empty tables
- tbody: !div.getElementsByTagName( "tbody" ).length,
-
- // Make sure that link elements get serialized correctly by innerHTML
- // This requires a wrapper element in IE
- htmlSerialize: !!div.getElementsByTagName( "link" ).length,
-
- // Get the style information from getAttribute
- // (IE uses .cssText instead)
- style: /top/.test( a.getAttribute("style") ),
-
- // Make sure that URLs aren't manipulated
- // (IE normalizes it by default)
- hrefNormalized: ( a.getAttribute( "href" ) === "/a" ),
-
- // Make sure that element opacity exists
- // (IE uses filter instead)
- // Use a regex to work around a WebKit issue. See #5145
- opacity: /^0.55$/.test( a.style.opacity ),
-
- // Verify style float existence
- // (IE uses styleFloat instead of cssFloat)
- cssFloat: !!a.style.cssFloat,
-
- // Make sure that if no value is specified for a checkbox
- // that it defaults to "on".
- // (WebKit defaults to "" instead)
- checkOn: ( input.value === "on" ),
-
- // Make sure that a selected-by-default option has a working selected property.
- // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
- optSelected: opt.selected,
-
- // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
- getSetAttribute: div.className !== "t",
-
- // Will be defined later
- submitBubbles: true,
- changeBubbles: true,
- focusinBubbles: false,
- deleteExpando: true,
- noCloneEvent: true,
- inlineBlockNeedsLayout: false,
- shrinkWrapBlocks: false,
- reliableMarginRight: true
- };
+ input = div.getElementsByTagName("input")[ 0 ];
+
+ a.style.cssText = "top:1px;float:left;opacity:.5";
+
+ // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
+ support.getSetAttribute = div.className !== "t";
+
+ // IE strips leading whitespace when .innerHTML is used
+ support.leadingWhitespace = div.firstChild.nodeType === 3;
+
+ // Make sure that tbody elements aren't automatically inserted
+ // IE will insert them into empty tables
+ support.tbody = !div.getElementsByTagName("tbody").length;
+
+ // Make sure that link elements get serialized correctly by innerHTML
+ // This requires a wrapper element in IE
+ support.htmlSerialize = !!div.getElementsByTagName("link").length;
+
+ // Get the style information from getAttribute
+ // (IE uses .cssText instead)
+ support.style = /top/.test( a.getAttribute("style") );
+
+ // Make sure that URLs aren't manipulated
+ // (IE normalizes it by default)
+ support.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
+ support.opacity = /^0.5/.test( a.style.opacity );
+
+ // Verify style float existence
+ // (IE uses styleFloat instead of cssFloat)
+ support.cssFloat = !!a.style.cssFloat;
+
+ // Check the default checkbox/radio value ("" on WebKit; "on" elsewhere)
+ support.checkOn = !!input.value;
+
+ // 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)
+ support.optSelected = opt.selected;
+
+ // Tests for enctype support on a form (#6743)
+ support.enctype = !!document.createElement("form").enctype;
+
+ // Makes sure cloning an html5 element does not cause problems
+ // Where outerHTML is undefined, this still works
+ support.html5Clone = document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav>";
+
+ // Will be defined later
+ support.inlineBlockNeedsLayout = false;
+ support.shrinkWrapBlocks = false;
+ support.pixelPosition = false;
+ support.deleteExpando = true;
+ support.noCloneEvent = true;
+ support.reliableMarginRight = true;
+ support.boxSizingReliable = true;
// Make sure checked status is properly cloned
input.checked = true;
@@ -1256,466 +3402,446 @@
select.disabled = true;
support.optDisabled = !opt.disabled;
- // Test to see if it's possible to delete an expando from an element
- // Fails in Internet Explorer
+ // Support: IE<9
try {
delete div.test;
} catch( e ) {
support.deleteExpando = false;
}
- if ( !div.addEventListener && div.attachEvent && div.fireEvent ) {
- div.attachEvent( "onclick", function() {
- // Cloning a node shouldn't copy over any
- // bound event handlers (IE does this)
- support.noCloneEvent = false;
- });
- div.cloneNode( true ).fireEvent( "onclick" );
- }
-
- // Check if a radio maintains it's value
- // after being appended to the DOM
+ // Check if we can trust getAttribute("value")
input = document.createElement("input");
+ input.setAttribute( "value", "" );
+ support.input = input.getAttribute( "value" ) === "";
+
+ // Check if an input maintains its value after becoming a radio
input.value = "t";
- input.setAttribute("type", "radio");
+ input.setAttribute( "type", "radio" );
support.radioValue = input.value === "t";
- input.setAttribute("checked", "checked");
- div.appendChild( input );
+ // #11217 - WebKit loses check when the name is after the checked attribute
+ input.setAttribute( "checked", "t" );
+ input.setAttribute( "name", "t" );
+
fragment = document.createDocumentFragment();
- fragment.appendChild( div.firstChild );
-
- // WebKit doesn't clone checked state correctly in fragments
- support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
-
- div.innerHTML = "";
-
- // Figure out if the W3C box model works as expected
- div.style.width = div.style.paddingLeft = "1px";
-
- body = document.getElementsByTagName( "body" )[ 0 ];
- // We use our own, invisible, body unless the body is already present
- // in which case we use a div (#9239)
- testElement = document.createElement( body ? "div" : "body" );
- testElementStyle = {
- visibility: "hidden",
- width: 0,
- height: 0,
- border: 0,
- margin: 0,
- background: "none"
- };
- if ( body ) {
- jQuery.extend( testElementStyle, {
- position: "absolute",
- left: "-1000px",
- top: "-1000px"
- });
- }
- for ( i in testElementStyle ) {
- testElement.style[ i ] = testElementStyle[ i ];
- }
- testElement.appendChild( div );
- testElementParent = body || documentElement;
- testElementParent.insertBefore( testElement, testElementParent.firstChild );
+ fragment.appendChild( input );
// Check if a disconnected checkbox will retain its checked
// value of true after appended to the DOM (IE6/7)
support.appendChecked = input.checked;
- support.boxModel = div.offsetWidth === 2;
-
- if ( "zoom" in div.style ) {
- // Check if natively block-level elements act like inline-block
- // elements when setting their display to 'inline' and giving
- // them layout
- // (IE < 8 does this)
- div.style.display = "inline";
- div.style.zoom = 1;
- support.inlineBlockNeedsLayout = ( div.offsetWidth === 2 );
-
- // Check if elements with layout shrink-wrap their children
- // (IE 6 does this)
- div.style.display = "";
- div.innerHTML = "
";
- support.shrinkWrapBlocks = ( div.offsetWidth !== 2 );
- }
-
- div.innerHTML = "";
- tds = div.getElementsByTagName( "td" );
-
- // Check if table cells still have offsetWidth/Height when they are set
- // to display:none and there are still other visible table cells in a
- // table row; if so, offsetWidth/Height are not reliable for use when
- // determining if an element has been hidden directly using
- // display:none (it is still safe to use offsets if a parent element is
- // hidden; don safety goggles and see bug #4512 for more information).
- // (only IE 8 fails this test)
- isSupported = ( tds[ 0 ].offsetHeight === 0 );
-
- tds[ 0 ].style.display = "";
- tds[ 1 ].style.display = "none";
-
- // Check if empty table cells still have offsetWidth/Height
- // (IE < 8 fail this test)
- support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
- div.innerHTML = "";
-
- // Check if div with explicit width and no margin-right incorrectly
- // gets computed margin-right based on width of container. For more
- // info see bug #3333
- // Fails in WebKit before Feb 2011 nightlies
- // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
- if ( document.defaultView && document.defaultView.getComputedStyle ) {
- marginDiv = document.createElement( "div" );
- marginDiv.style.width = "0";
- marginDiv.style.marginRight = "0";
- div.appendChild( marginDiv );
- support.reliableMarginRight =
- ( parseInt( ( document.defaultView.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0;
- }
-
- // Remove the body element we added
- testElement.innerHTML = "";
- testElementParent.removeChild( testElement );
-
- // Technique from Juriy Zaytsev
- // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
- // We only care about the case where non-standard event systems
- // are used, namely in IE. Short-circuiting here helps us to
- // avoid an eval call (in setAttribute) which can cause CSP
- // to go haywire. See: https://developer.mozilla.org/en/Security/CSP
+ // WebKit doesn't clone checked state correctly in fragments
+ support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
+
+ // Support: IE<9
+ // Opera does not clone events (and typeof div.attachEvent === undefined).
+ // IE9-10 clones events bound via attachEvent, but they don't trigger with .click()
if ( div.attachEvent ) {
- for( i in {
- submit: 1,
- change: 1,
- focusin: 1
- } ) {
- eventName = "on" + i;
- isSupported = ( eventName in div );
- if ( !isSupported ) {
- div.setAttribute( eventName, "return;" );
- isSupported = ( typeof div[ eventName ] === "function" );
- }
- support[ i + "Bubbles" ] = isSupported;
- }
- }
-
- // Null connected elements to avoid leaks in IE
- testElement = fragment = select = opt = body = marginDiv = div = input = null;
+ div.attachEvent( "onclick", function() {
+ support.noCloneEvent = false;
+ });
+
+ div.cloneNode( true ).click();
+ }
+
+ // Support: IE<9 (lack submit/change bubble), Firefox 17+ (lack focusin event)
+ // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP)
+ for ( i in { submit: true, change: true, focusin: true }) {
+ div.setAttribute( eventName = "on" + i, "t" );
+
+ support[ i + "Bubbles" ] = eventName in window || div.attributes[ eventName ].expando === false;
+ }
+
+ div.style.backgroundClip = "content-box";
+ div.cloneNode( true ).style.backgroundClip = "";
+ support.clearCloneStyle = div.style.backgroundClip === "content-box";
+
+ // Support: IE<9
+ // Iteration over object's inherited properties before its own.
+ for ( i in jQuery( support ) ) {
+ break;
+ }
+ support.ownLast = i !== "0";
+
+ // Run tests that need a body at doc ready
+ jQuery(function() {
+ var container, marginDiv, tds,
+ divReset = "padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",
+ body = document.getElementsByTagName("body")[0];
+
+ if ( !body ) {
+ // Return for frameset docs that don't have a body
+ return;
+ }
+
+ container = document.createElement("div");
+ container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px";
+
+ body.appendChild( container ).appendChild( div );
+
+ // Support: IE8
+ // Check if table cells still have offsetWidth/Height when they are set
+ // to display:none and there are still other visible table cells in a
+ // table row; if so, offsetWidth/Height are not reliable for use when
+ // determining if an element has been hidden directly using
+ // display:none (it is still safe to use offsets if a parent element is
+ // hidden; don safety goggles and see bug #4512 for more information).
+ div.innerHTML = "";
+ tds = div.getElementsByTagName("td");
+ tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none";
+ isSupported = ( tds[ 0 ].offsetHeight === 0 );
+
+ tds[ 0 ].style.display = "";
+ tds[ 1 ].style.display = "none";
+
+ // Support: IE8
+ // Check if empty table cells still have offsetWidth/Height
+ support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
+
+ // Check box-sizing and margin behavior.
+ div.innerHTML = "";
+ div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;";
+
+ // Workaround failing boxSizing test due to offsetWidth returning wrong value
+ // with some non-1 values of body zoom, ticket #13543
+ jQuery.swap( body, body.style.zoom != null ? { zoom: 1 } : {}, function() {
+ support.boxSizing = div.offsetWidth === 4;
+ });
+
+ // Use window.getComputedStyle because jsdom on node.js will break without it.
+ if ( window.getComputedStyle ) {
+ support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%";
+ support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px";
+
+ // Check if div with explicit width and no margin-right incorrectly
+ // gets computed margin-right based on width of container. (#3333)
+ // Fails in WebKit before Feb 2011 nightlies
+ // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+ marginDiv = div.appendChild( document.createElement("div") );
+ marginDiv.style.cssText = div.style.cssText = divReset;
+ marginDiv.style.marginRight = marginDiv.style.width = "0";
+ div.style.width = "1px";
+
+ support.reliableMarginRight =
+ !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight );
+ }
+
+ if ( typeof div.style.zoom !== core_strundefined ) {
+ // Support: IE<8
+ // Check if natively block-level elements act like inline-block
+ // elements when setting their display to 'inline' and giving
+ // them layout
+ div.innerHTML = "";
+ div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1";
+ support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 );
+
+ // Support: IE6
+ // Check if elements with layout shrink-wrap their children
+ div.style.display = "block";
+ div.innerHTML = "
";
+ div.firstChild.style.width = "5px";
+ support.shrinkWrapBlocks = ( div.offsetWidth !== 3 );
+
+ if ( support.inlineBlockNeedsLayout ) {
+ // Prevent IE 6 from affecting layout for positioned elements #11048
+ // Prevent IE from shrinking the body in IE 7 mode #12869
+ // Support: IE<8
+ body.style.zoom = 1;
+ }
+ }
+
+ body.removeChild( container );
+
+ // Null elements to avoid leaks in IE
+ container = div = tds = marginDiv = null;
+ });
+
+ // Null elements to avoid leaks in IE
+ all = select = fragment = opt = a = input = null;
return support;
-})();
-
-// Keep track of boxModel
-jQuery.boxModel = jQuery.support.boxModel;
-
-
-
-
-var rbrace = /^(?:\{.*\}|\[.*\])$/,
+})({});
+
+var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/,
rmultiDash = /([A-Z])/g;
+function internalData( elem, name, data, pvt /* Internal Use Only */ ){
+ if ( !jQuery.acceptData( elem ) ) {
+ return;
+ }
+
+ var ret, thisCache,
+ internalKey = jQuery.expando,
+
+ // We have to handle DOM nodes and JS objects differently because IE6-7
+ // can't GC object references properly across the DOM-JS boundary
+ isNode = elem.nodeType,
+
+ // Only DOM nodes need the global jQuery cache; JS object data is
+ // attached directly to the object so GC can occur automatically
+ cache = isNode ? jQuery.cache : elem,
+
+ // Only defining an ID for JS objects if its cache already exists allows
+ // the code to shortcut on the same path as a DOM node with no cache
+ id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey;
+
+ // Avoid doing any more work than we need to when trying to get data on an
+ // object that has no data at all
+ if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && data === undefined && typeof name === "string" ) {
+ return;
+ }
+
+ if ( !id ) {
+ // Only DOM nodes need a new unique ID for each element since their data
+ // ends up in the global cache
+ if ( isNode ) {
+ id = elem[ internalKey ] = core_deletedIds.pop() || jQuery.guid++;
+ } else {
+ id = internalKey;
+ }
+ }
+
+ if ( !cache[ id ] ) {
+ // Avoid exposing jQuery metadata on plain JS objects when the object
+ // is serialized using JSON.stringify
+ cache[ id ] = isNode ? {} : { toJSON: jQuery.noop };
+ }
+
+ // An object can be passed to jQuery.data instead of a key/value pair; this gets
+ // shallow copied over onto the existing cache
+ if ( typeof name === "object" || typeof name === "function" ) {
+ if ( pvt ) {
+ cache[ id ] = jQuery.extend( cache[ id ], name );
+ } else {
+ cache[ id ].data = jQuery.extend( cache[ id ].data, name );
+ }
+ }
+
+ thisCache = cache[ id ];
+
+ // jQuery data() is stored in a separate object inside the object's internal data
+ // cache in order to avoid key collisions between internal data and user-defined
+ // data.
+ if ( !pvt ) {
+ if ( !thisCache.data ) {
+ thisCache.data = {};
+ }
+
+ thisCache = thisCache.data;
+ }
+
+ if ( data !== undefined ) {
+ thisCache[ jQuery.camelCase( name ) ] = data;
+ }
+
+ // Check for both converted-to-camel and non-converted data property names
+ // If a data property was specified
+ if ( typeof name === "string" ) {
+
+ // First Try to find as-is property data
+ ret = thisCache[ name ];
+
+ // Test for null|undefined property data
+ if ( ret == null ) {
+
+ // Try to find the camelCased property
+ ret = thisCache[ jQuery.camelCase( name ) ];
+ }
+ } else {
+ ret = thisCache;
+ }
+
+ return ret;
+}
+
+function internalRemoveData( elem, name, pvt ) {
+ if ( !jQuery.acceptData( elem ) ) {
+ return;
+ }
+
+ var thisCache, i,
+ isNode = elem.nodeType,
+
+ // See jQuery.data for more information
+ cache = isNode ? jQuery.cache : elem,
+ id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
+
+ // If there is already no cache entry for this object, there is no
+ // purpose in continuing
+ if ( !cache[ id ] ) {
+ return;
+ }
+
+ if ( name ) {
+
+ thisCache = pvt ? cache[ id ] : cache[ id ].data;
+
+ if ( thisCache ) {
+
+ // Support array or space separated string names for data keys
+ if ( !jQuery.isArray( name ) ) {
+
+ // try the string as a key before any manipulation
+ if ( name in thisCache ) {
+ name = [ name ];
+ } else {
+
+ // split the camel cased version by spaces unless a key with the spaces exists
+ name = jQuery.camelCase( name );
+ if ( name in thisCache ) {
+ name = [ name ];
+ } else {
+ name = name.split(" ");
+ }
+ }
+ } else {
+ // If "name" is an array of keys...
+ // When data is initially created, via ("key", "val") signature,
+ // keys will be converted to camelCase.
+ // Since there is no way to tell _how_ a key was added, remove
+ // both plain key and camelCase key. #12786
+ // This will only penalize the array argument path.
+ name = name.concat( jQuery.map( name, jQuery.camelCase ) );
+ }
+
+ i = name.length;
+ while ( i-- ) {
+ delete thisCache[ name[i] ];
+ }
+
+ // If there is no data left in the cache, we want to continue
+ // and let the cache object itself get destroyed
+ if ( pvt ? !isEmptyDataObject(thisCache) : !jQuery.isEmptyObject(thisCache) ) {
+ return;
+ }
+ }
+ }
+
+ // See jQuery.data for more information
+ if ( !pvt ) {
+ delete cache[ id ].data;
+
+ // Don't destroy the parent cache unless the internal data object
+ // had been the only thing left in it
+ if ( !isEmptyDataObject( cache[ id ] ) ) {
+ return;
+ }
+ }
+
+ // Destroy the cache
+ if ( isNode ) {
+ jQuery.cleanData( [ elem ], true );
+
+ // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080)
+ /* jshint eqeqeq: false */
+ } else if ( jQuery.support.deleteExpando || cache != cache.window ) {
+ /* jshint eqeqeq: true */
+ delete cache[ id ];
+
+ // When all else fails, null
+ } else {
+ cache[ id ] = null;
+ }
+}
+
jQuery.extend({
cache: {},
- // Please use with caution
- uuid: 0,
-
- // Unique for each copy of jQuery on the page
- // Non-digits removed to match rinlinejQuery
- expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),
-
// The following elements throw uncatchable exceptions if you
// attempt to add expando properties to them.
noData: {
+ "applet": true,
"embed": true,
// Ban all objects except for Flash (which handle expandos)
- "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
- "applet": true
+ "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
},
hasData: function( elem ) {
elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
-
return !!elem && !isEmptyDataObject( elem );
},
- data: function( elem, name, data, pvt /* Internal Use Only */ ) {
- if ( !jQuery.acceptData( elem ) ) {
- return;
- }
-
- var thisCache, ret,
- internalKey = jQuery.expando,
- getByName = typeof name === "string",
-
- // We have to handle DOM nodes and JS objects differently because IE6-7
- // can't GC object references properly across the DOM-JS boundary
- isNode = elem.nodeType,
-
- // Only DOM nodes need the global jQuery cache; JS object data is
- // attached directly to the object so GC can occur automatically
- cache = isNode ? jQuery.cache : elem,
-
- // Only defining an ID for JS objects if its cache already exists allows
- // the code to shortcut on the same path as a DOM node with no cache
- id = isNode ? elem[ jQuery.expando ] : elem[ jQuery.expando ] && jQuery.expando;
-
- // Avoid doing any more work than we need to when trying to get data on an
- // object that has no data at all
- if ( (!id || (pvt && id && (cache[ id ] && !cache[ id ][ internalKey ]))) && getByName && data === undefined ) {
- return;
- }
-
- if ( !id ) {
- // Only DOM nodes need a new unique ID for each element since their data
- // ends up in the global cache
- if ( isNode ) {
- elem[ jQuery.expando ] = id = ++jQuery.uuid;
- } else {
- id = jQuery.expando;
- }
- }
-
- if ( !cache[ id ] ) {
- cache[ id ] = {};
-
- // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery
- // metadata on plain JS objects when the object is serialized using
- // JSON.stringify
- if ( !isNode ) {
- cache[ id ].toJSON = jQuery.noop;
- }
- }
-
- // An object can be passed to jQuery.data instead of a key/value pair; this gets
- // shallow copied over onto the existing cache
- if ( typeof name === "object" || typeof name === "function" ) {
- if ( pvt ) {
- cache[ id ][ internalKey ] = jQuery.extend(cache[ id ][ internalKey ], name);
- } else {
- cache[ id ] = jQuery.extend(cache[ id ], name);
- }
- }
-
- thisCache = cache[ id ];
-
- // Internal jQuery data is stored in a separate object inside the object's data
- // cache in order to avoid key collisions between internal data and user-defined
- // data
- if ( pvt ) {
- if ( !thisCache[ internalKey ] ) {
- thisCache[ internalKey ] = {};
- }
-
- thisCache = thisCache[ internalKey ];
- }
-
- if ( data !== undefined ) {
- thisCache[ jQuery.camelCase( name ) ] = data;
- }
-
- // TODO: This is a hack for 1.5 ONLY. It will be removed in 1.6. Users should
- // not attempt to inspect the internal events object using jQuery.data, as this
- // internal data object is undocumented and subject to change.
- if ( name === "events" && !thisCache[name] ) {
- return thisCache[ internalKey ] && thisCache[ internalKey ].events;
- }
-
- // Check for both converted-to-camel and non-converted data property names
- // If a data property was specified
- if ( getByName ) {
-
- // First Try to find as-is property data
- ret = thisCache[ name ];
-
- // Test for null|undefined property data
- if ( ret == null ) {
-
- // Try to find the camelCased property
- ret = thisCache[ jQuery.camelCase( name ) ];
- }
- } else {
- ret = thisCache;
- }
-
- return ret;
- },
-
- removeData: function( elem, name, pvt /* Internal Use Only */ ) {
- if ( !jQuery.acceptData( elem ) ) {
- return;
- }
-
- var thisCache,
-
- // Reference to internal data cache key
- internalKey = jQuery.expando,
-
- isNode = elem.nodeType,
-
- // See jQuery.data for more information
- cache = isNode ? jQuery.cache : elem,
-
- // See jQuery.data for more information
- id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
-
- // If there is already no cache entry for this object, there is no
- // purpose in continuing
- if ( !cache[ id ] ) {
- return;
- }
-
- if ( name ) {
-
- thisCache = pvt ? cache[ id ][ internalKey ] : cache[ id ];
-
- if ( thisCache ) {
-
- // Support interoperable removal of hyphenated or camelcased keys
- if ( !thisCache[ name ] ) {
- name = jQuery.camelCase( name );
- }
-
- delete thisCache[ name ];
-
- // If there is no data left in the cache, we want to continue
- // and let the cache object itself get destroyed
- if ( !isEmptyDataObject(thisCache) ) {
- return;
- }
- }
- }
-
- // See jQuery.data for more information
- if ( pvt ) {
- delete cache[ id ][ internalKey ];
-
- // Don't destroy the parent cache unless the internal data object
- // had been the only thing left in it
- if ( !isEmptyDataObject(cache[ id ]) ) {
- return;
- }
- }
-
- var internalCache = cache[ id ][ internalKey ];
-
- // Browsers that fail expando deletion also refuse to delete expandos on
- // the window, but it will allow it on all other JS objects; other browsers
- // don't care
- // Ensure that `cache` is not a window object #10080
- if ( jQuery.support.deleteExpando || !cache.setInterval ) {
- delete cache[ id ];
- } else {
- cache[ id ] = null;
- }
-
- // We destroyed the entire user cache at once because it's faster than
- // iterating through each key, but we need to continue to persist internal
- // data if it existed
- if ( internalCache ) {
- cache[ id ] = {};
- // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery
- // metadata on plain JS objects when the object is serialized using
- // JSON.stringify
- if ( !isNode ) {
- cache[ id ].toJSON = jQuery.noop;
- }
-
- cache[ id ][ internalKey ] = internalCache;
-
- // Otherwise, we need to eliminate the expando on the node to avoid
- // false lookups in the cache for entries that no longer exist
- } else if ( isNode ) {
- // IE does not allow us to delete expando properties from nodes,
- // nor does it have a removeAttribute function on Document nodes;
- // we must handle all of these cases
- if ( jQuery.support.deleteExpando ) {
- delete elem[ jQuery.expando ];
- } else if ( elem.removeAttribute ) {
- elem.removeAttribute( jQuery.expando );
- } else {
- elem[ jQuery.expando ] = null;
- }
- }
+ data: function( elem, name, data ) {
+ return internalData( elem, name, data );
+ },
+
+ removeData: function( elem, name ) {
+ return internalRemoveData( elem, name );
},
// For internal use only.
_data: function( elem, name, data ) {
- return jQuery.data( elem, name, data, true );
+ return internalData( elem, name, data, true );
+ },
+
+ _removeData: function( elem, name ) {
+ return internalRemoveData( elem, name, true );
},
// A method for determining if a DOM node can handle the data expando
acceptData: function( elem ) {
- if ( elem.nodeName ) {
- var match = jQuery.noData[ elem.nodeName.toLowerCase() ];
-
- if ( match ) {
- return !(match === true || elem.getAttribute("classid") !== match);
- }
- }
-
- return true;
+ // Do not set data on non-element because it will not be cleared (#8335).
+ if ( elem.nodeType && elem.nodeType !== 1 && elem.nodeType !== 9 ) {
+ return false;
+ }
+
+ var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ];
+
+ // nodes accept data unless otherwise specified; rejection can be conditional
+ return !noData || noData !== true && elem.getAttribute("classid") === noData;
}
});
jQuery.fn.extend({
data: function( key, value ) {
- var data = null;
-
- if ( typeof key === "undefined" ) {
+ var attrs, name,
+ data = null,
+ i = 0,
+ elem = this[0];
+
+ // Special expections of .data basically thwart jQuery.access,
+ // so implement the relevant behavior ourselves
+
+ // Gets all values
+ if ( key === undefined ) {
if ( this.length ) {
- data = jQuery.data( this[0] );
-
- if ( this[0].nodeType === 1 ) {
- var attr = this[0].attributes, name;
- for ( var i = 0, l = attr.length; i < l; i++ ) {
- name = attr[i].name;
-
- if ( name.indexOf( "data-" ) === 0 ) {
- name = jQuery.camelCase( name.substring(5) );
-
- dataAttr( this[0], name, data[ name ] );
+ data = jQuery.data( elem );
+
+ if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
+ attrs = elem.attributes;
+ for ( ; i < attrs.length; i++ ) {
+ name = attrs[i].name;
+
+ if ( name.indexOf("data-") === 0 ) {
+ name = jQuery.camelCase( name.slice(5) );
+
+ dataAttr( elem, name, data[ name ] );
}
}
+ jQuery._data( elem, "parsedAttrs", true );
}
}
return data;
-
- } else if ( typeof key === "object" ) {
+ }
+
+ // Sets multiple values
+ if ( typeof key === "object" ) {
return this.each(function() {
jQuery.data( this, key );
});
}
- var parts = key.split(".");
- parts[1] = parts[1] ? "." + parts[1] : "";
-
- if ( value === undefined ) {
- data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
-
+ return arguments.length > 1 ?
+
+ // Sets one value
+ this.each(function() {
+ jQuery.data( this, key, value );
+ }) :
+
+ // Gets one value
// Try to fetch any internally stored data first
- if ( data === undefined && this.length ) {
- data = jQuery.data( this[0], key );
- data = dataAttr( this[0], key, data );
- }
-
- return data === undefined && parts[1] ?
- this.data( parts[0] ) :
- data;
-
- } else {
- return this.each(function() {
- var $this = jQuery( this ),
- args = [ parts[0], value ];
-
- $this.triggerHandler( "setData" + parts[1] + "!", args );
- jQuery.data( this, key, value );
- $this.triggerHandler( "changeData" + parts[1] + "!", args );
- });
- }
+ elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : null;
},
removeData: function( key ) {
@@ -1737,11 +3863,12 @@
if ( typeof data === "string" ) {
try {
data = data === "true" ? true :
- data === "false" ? false :
- data === "null" ? null :
- !jQuery.isNaN( data ) ? parseFloat( data ) :
+ data === "false" ? false :
+ data === "null" ? null :
+ // Only convert to a number if it doesn't change the string
+ +data + "" === data ? +data :
rbrace.test( data ) ? jQuery.parseJSON( data ) :
- data;
+ data;
} catch( e ) {}
// Make sure we set the data so it isn't changed later
@@ -1755,11 +3882,15 @@
return data;
}
-// TODO: This is a hack for 1.5 ONLY to allow objects with a single toJSON
-// property to be considered empty objects; this property always exists in
-// order to make sure JSON.stringify does not expose internal metadata
+// checks a cache object for emptiness
function isEmptyDataObject( obj ) {
- for ( var name in obj ) {
+ var name;
+ for ( name in obj ) {
+
+ // if the public data object is empty, the private is still empty
+ if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
+ continue;
+ }
if ( name !== "toJSON" ) {
return false;
}
@@ -1767,71 +3898,23 @@
return true;
}
-
-
-
-
-function handleQueueMarkDefer( elem, type, src ) {
- var deferDataKey = type + "defer",
- queueDataKey = type + "queue",
- markDataKey = type + "mark",
- defer = jQuery.data( elem, deferDataKey, undefined, true );
- if ( defer &&
- ( src === "queue" || !jQuery.data( elem, queueDataKey, undefined, true ) ) &&
- ( src === "mark" || !jQuery.data( elem, markDataKey, undefined, true ) ) ) {
- // Give room for hard-coded callbacks to fire first
- // and eventually mark/queue something else on the element
- setTimeout( function() {
- if ( !jQuery.data( elem, queueDataKey, undefined, true ) &&
- !jQuery.data( elem, markDataKey, undefined, true ) ) {
- jQuery.removeData( elem, deferDataKey, true );
- defer.resolve();
- }
- }, 0 );
- }
-}
-
jQuery.extend({
-
- _mark: function( elem, type ) {
+ queue: function( elem, type, data ) {
+ var queue;
+
if ( elem ) {
- type = (type || "fx") + "mark";
- jQuery.data( elem, type, (jQuery.data(elem,type,undefined,true) || 0) + 1, true );
- }
- },
-
- _unmark: function( force, elem, type ) {
- if ( force !== true ) {
- type = elem;
- elem = force;
- force = false;
- }
- if ( elem ) {
- type = type || "fx";
- var key = type + "mark",
- count = force ? 0 : ( (jQuery.data( elem, key, undefined, true) || 1 ) - 1 );
- if ( count ) {
- jQuery.data( elem, key, count, true );
- } else {
- jQuery.removeData( elem, key, true );
- handleQueueMarkDefer( elem, type, "mark" );
- }
- }
- },
-
- queue: function( elem, type, data ) {
- if ( elem ) {
- type = (type || "fx") + "queue";
- var q = jQuery.data( elem, type, undefined, true );
+ type = ( type || "fx" ) + "queue";
+ queue = jQuery._data( elem, type );
+
// Speed up dequeue by getting out quickly if this is just a lookup
if ( data ) {
- if ( !q || jQuery.isArray(data) ) {
- q = jQuery.data( elem, type, jQuery.makeArray(data), true );
+ if ( !queue || jQuery.isArray(data) ) {
+ queue = jQuery._data( elem, type, jQuery.makeArray(data) );
} else {
- q.push( data );
- }
- }
- return q || [];
+ queue.push( data );
+ }
+ }
+ return queue || [];
}
},
@@ -1839,50 +3922,75 @@
type = type || "fx";
var queue = jQuery.queue( elem, type ),
+ startLength = queue.length,
fn = queue.shift(),
- defer;
+ hooks = jQuery._queueHooks( elem, type ),
+ next = function() {
+ jQuery.dequeue( elem, type );
+ };
// If the fx queue is dequeued, always remove the progress sentinel
if ( fn === "inprogress" ) {
fn = queue.shift();
+ startLength--;
}
if ( fn ) {
+
// Add a progress sentinel to prevent the fx queue from being
// automatically dequeued
if ( type === "fx" ) {
- queue.unshift("inprogress");
- }
-
- fn.call(elem, function() {
- jQuery.dequeue(elem, type);
- });
- }
-
- if ( !queue.length ) {
- jQuery.removeData( elem, type + "queue", true );
- handleQueueMarkDefer( elem, type, "queue" );
- }
+ queue.unshift( "inprogress" );
+ }
+
+ // clear up the last queue stop function
+ delete hooks.stop;
+ fn.call( elem, next, hooks );
+ }
+
+ if ( !startLength && hooks ) {
+ hooks.empty.fire();
+ }
+ },
+
+ // not intended for public consumption - generates a queueHooks object, or returns the current one
+ _queueHooks: function( elem, type ) {
+ var key = type + "queueHooks";
+ return jQuery._data( elem, key ) || jQuery._data( elem, key, {
+ empty: jQuery.Callbacks("once memory").add(function() {
+ jQuery._removeData( elem, type + "queue" );
+ jQuery._removeData( elem, key );
+ })
+ });
}
});
jQuery.fn.extend({
queue: function( type, data ) {
+ var setter = 2;
+
if ( typeof type !== "string" ) {
data = type;
type = "fx";
- }
-
- if ( data === undefined ) {
+ setter--;
+ }
+
+ if ( arguments.length < setter ) {
return jQuery.queue( this[0], type );
}
- return this.each(function() {
- var queue = jQuery.queue( this, type, data );
-
- if ( type === "fx" && queue[0] !== "inprogress" ) {
- jQuery.dequeue( this, type );
- }
- });
+
+ return data === undefined ?
+ this :
+ this.each(function() {
+ var queue = jQuery.queue( this, type, data );
+
+ // ensure a hooks for this queue
+ jQuery._queueHooks( this, type );
+
+ if ( type === "fx" && queue[0] !== "inprogress" ) {
+ jQuery.dequeue( this, type );
+ }
+ });
},
dequeue: function( type ) {
return this.each(function() {
@@ -1892,14 +4000,14 @@
// 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;
+ 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 );
+ return this.queue( type, function( next, hooks ) {
+ var timeout = setTimeout( next, time );
+ hooks.stop = function() {
+ clearTimeout( timeout );
+ };
});
},
clearQueue: function( type ) {
@@ -1907,54 +4015,47 @@
},
// Get a promise resolved when queues of a certain type
// are emptied (fx is the type by default)
- promise: function( type, object ) {
+ promise: function( type, obj ) {
+ var tmp,
+ count = 1,
+ defer = jQuery.Deferred(),
+ elements = this,
+ i = this.length,
+ resolve = function() {
+ if ( !( --count ) ) {
+ defer.resolveWith( elements, [ elements ] );
+ }
+ };
+
if ( typeof type !== "string" ) {
- object = type;
+ obj = type;
type = undefined;
}
type = type || "fx";
- var defer = jQuery.Deferred(),
- elements = this,
- i = elements.length,
- count = 1,
- deferDataKey = type + "defer",
- queueDataKey = type + "queue",
- markDataKey = type + "mark",
- tmp;
- function resolve() {
- if ( !( --count ) ) {
- defer.resolveWith( elements, [ elements ] );
- }
- }
+
while( i-- ) {
- if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) ||
- ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) ||
- jQuery.data( elements[ i ], markDataKey, undefined, true ) ) &&
- jQuery.data( elements[ i ], deferDataKey, jQuery._Deferred(), true ) )) {
+ tmp = jQuery._data( elements[ i ], type + "queueHooks" );
+ if ( tmp && tmp.empty ) {
count++;
- tmp.done( resolve );
+ tmp.empty.add( resolve );
}
}
resolve();
- return defer.promise();
+ return defer.promise( obj );
}
});
-
-
-
-
-var rclass = /[\n\t\r]/g,
- rspace = /\s+/,
+var nodeHook, boolHook,
+ rclass = /[\t\r\n\f]/g,
rreturn = /\r/g,
- rtype = /^(?:button|input)$/i,
- rfocusable = /^(?:button|input|object|select|textarea)$/i,
- rclickable = /^a(?:rea)?$/i,
- rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
- nodeHook, boolHook;
+ rfocusable = /^(?:input|select|textarea|button|object)$/i,
+ rclickable = /^(?:a|area)$/i,
+ ruseDefault = /^(?:checked|selected)$/i,
+ getSetAttribute = jQuery.support.getSetAttribute,
+ getSetInput = jQuery.support.input;
jQuery.fn.extend({
attr: function( name, value ) {
- return jQuery.access( this, name, value, true, jQuery.attr );
+ return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );
},
removeAttr: function( name ) {
@@ -1962,11 +4063,11 @@
jQuery.removeAttr( this, name );
});
},
-
+
prop: function( name, value ) {
- return jQuery.access( this, name, value, true, jQuery.prop );
- },
-
+ return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
+ },
+
removeProp: function( name ) {
name = jQuery.propFix[ name ] || name;
return this.each(function() {
@@ -1979,35 +4080,37 @@
},
addClass: function( value ) {
- var classNames, i, l, elem,
- setClass, c, cl;
+ var classes, elem, cur, clazz, j,
+ i = 0,
+ len = this.length,
+ proceed = typeof value === "string" && value;
if ( jQuery.isFunction( value ) ) {
return this.each(function( j ) {
- jQuery( this ).addClass( value.call(this, j, this.className) );
+ jQuery( this ).addClass( value.call( this, j, this.className ) );
});
}
- if ( value && typeof value === "string" ) {
- classNames = value.split( rspace );
-
- for ( i = 0, l = this.length; i < l; i++ ) {
+ if ( proceed ) {
+ // The disjunction here is for better compressibility (see removeClass)
+ classes = ( value || "" ).match( core_rnotwhite ) || [];
+
+ for ( ; i < len; i++ ) {
elem = this[ i ];
-
- if ( elem.nodeType === 1 ) {
- if ( !elem.className && classNames.length === 1 ) {
- elem.className = value;
-
- } else {
- setClass = " " + elem.className + " ";
-
- for ( c = 0, cl = classNames.length; c < cl; c++ ) {
- if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) {
- setClass += classNames[ c ] + " ";
- }
+ cur = elem.nodeType === 1 && ( elem.className ?
+ ( " " + elem.className + " " ).replace( rclass, " " ) :
+ " "
+ );
+
+ if ( cur ) {
+ j = 0;
+ while ( (clazz = classes[j++]) ) {
+ if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
+ cur += clazz + " ";
}
- elem.className = jQuery.trim( setClass );
}
+ elem.className = jQuery.trim( cur );
+
}
}
}
@@ -2016,31 +4119,36 @@
},
removeClass: function( value ) {
- var classNames, i, l, elem, className, c, cl;
+ var classes, elem, cur, clazz, j,
+ i = 0,
+ len = this.length,
+ proceed = arguments.length === 0 || typeof value === "string" && value;
if ( jQuery.isFunction( value ) ) {
return this.each(function( j ) {
- jQuery( this ).removeClass( value.call(this, j, this.className) );
+ jQuery( this ).removeClass( value.call( this, j, this.className ) );
});
}
-
- if ( (value && typeof value === "string") || value === undefined ) {
- classNames = (value || "").split( rspace );
-
- for ( i = 0, l = this.length; i < l; i++ ) {
+ if ( proceed ) {
+ classes = ( value || "" ).match( core_rnotwhite ) || [];
+
+ for ( ; i < len; i++ ) {
elem = this[ i ];
-
- if ( elem.nodeType === 1 && elem.className ) {
- if ( value ) {
- className = (" " + elem.className + " ").replace( rclass, " " );
- for ( c = 0, cl = classNames.length; c < cl; c++ ) {
- className = className.replace(" " + classNames[ c ] + " ", " ");
+ // This expression is here for better compressibility (see addClass)
+ cur = elem.nodeType === 1 && ( elem.className ?
+ ( " " + elem.className + " " ).replace( rclass, " " ) :
+ ""
+ );
+
+ if ( cur ) {
+ j = 0;
+ while ( (clazz = classes[j++]) ) {
+ // Remove *all* instances
+ while ( cur.indexOf( " " + clazz + " " ) >= 0 ) {
+ cur = cur.replace( " " + clazz + " ", " " );
}
- elem.className = jQuery.trim( className );
-
- } else {
- elem.className = "";
}
+ elem.className = value ? jQuery.trim( cur ) : "";
}
}
}
@@ -2049,8 +4157,11 @@
},
toggleClass: function( value, stateVal ) {
- var type = typeof value,
- isBool = typeof stateVal === "boolean";
+ var type = typeof value;
+
+ if ( typeof stateVal === "boolean" && type === "string" ) {
+ return stateVal ? this.addClass( value ) : this.removeClass( value );
+ }
if ( jQuery.isFunction( value ) ) {
return this.each(function( i ) {
@@ -2064,31 +4175,39 @@
var className,
i = 0,
self = jQuery( this ),
- state = stateVal,
- classNames = value.split( rspace );
+ classNames = value.match( core_rnotwhite ) || [];
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" ) {
+ // check each className given, space separated list
+ if ( self.hasClass( className ) ) {
+ self.removeClass( className );
+ } else {
+ self.addClass( className );
+ }
+ }
+
+ // Toggle whole class name
+ } else if ( type === core_strundefined || type === "boolean" ) {
if ( this.className ) {
// store className if set
jQuery._data( this, "__className__", this.className );
}
- // toggle whole className
+ // If the element has a class name or if we're passed "false",
+ // then remove the whole classname (if there was one, the above saved it).
+ // Otherwise bring back whatever was previously saved (if anything),
+ // falling back to the empty string if nothing was stored.
this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
}
});
},
hasClass: function( selector ) {
- var className = " " + selector + " ";
- for ( var i = 0, l = this.length; i < l; i++ ) {
- if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
+ var className = " " + selector + " ",
+ i = 0,
+ l = this.length;
+ for ( ; i < l; i++ ) {
+ if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) {
return true;
}
}
@@ -2097,12 +4216,12 @@
},
val: function( value ) {
- var hooks, ret,
+ var ret, hooks, isFunction,
elem = this[0];
-
+
if ( !arguments.length ) {
if ( elem ) {
- hooks = jQuery.valHooks[ elem.nodeName.toLowerCase() ] || jQuery.valHooks[ elem.type ];
+ hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
return ret;
@@ -2110,27 +4229,27 @@
ret = elem.value;
- return typeof ret === "string" ?
+ return typeof ret === "string" ?
// handle most common string cases
- ret.replace(rreturn, "") :
+ ret.replace(rreturn, "") :
// handle cases where value is null/undef or number
ret == null ? "" : ret;
}
- return undefined;
- }
-
- var isFunction = jQuery.isFunction( value );
+ return;
+ }
+
+ isFunction = jQuery.isFunction( value );
return this.each(function( i ) {
- var self = jQuery(this), val;
+ var val;
if ( this.nodeType !== 1 ) {
return;
}
if ( isFunction ) {
- val = value.call( this, i, self.val() );
+ val = value.call( this, i, jQuery( this ).val() );
} else {
val = value;
}
@@ -2146,7 +4265,7 @@
});
}
- hooks = jQuery.valHooks[ this.nodeName.toLowerCase() ] || jQuery.valHooks[ this.type ];
+ hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
// If set returns undefined, fall back to normal setting
if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
@@ -2160,32 +4279,34 @@
valHooks: {
option: {
get: function( elem ) {
- // attributes.value is undefined in Blackberry 4.7 but
- // uses .value. See #6932
- var val = elem.attributes.value;
- return !val || val.specified ? elem.value : elem.text;
+ // Use proper attribute retrieval(#6932, #12072)
+ var val = jQuery.find.attr( elem, "value" );
+ return val != null ?
+ val :
+ elem.text;
}
},
select: {
get: function( elem ) {
- var value,
- index = elem.selectedIndex,
- values = [],
+ var value, option,
options = elem.options,
- one = elem.type === "select-one";
-
- // Nothing was selected
- if ( index < 0 ) {
- return null;
- }
+ index = elem.selectedIndex,
+ one = elem.type === "select-one" || index < 0,
+ values = one ? null : [],
+ max = one ? index + 1 : options.length,
+ i = index < 0 ?
+ max :
+ one ? index : 0;
// Loop through all the selected options
- for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
- var option = options[ i ];
-
- // Don't return options that are disabled or in a disabled optgroup
- if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) &&
- (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) {
+ for ( ; i < max; i++ ) {
+ option = options[ i ];
+
+ // oldIE doesn't update selected after form reset (#2551)
+ if ( ( option.selected || i === index ) &&
+ // Don't return options that are disabled or in a disabled optgroup
+ ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) &&
+ ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
// Get the specific value for the option
value = jQuery( option ).val();
@@ -2200,22 +4321,24 @@
}
}
- // Fixes Bug #2551 -- select.val() broken in IE after form.reset()
- if ( one && !values.length && options.length ) {
- return jQuery( options[ index ] ).val();
- }
-
return values;
},
set: function( elem, value ) {
- var values = jQuery.makeArray( value );
-
- jQuery(elem).find("option").each(function() {
- this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
- });
-
- if ( !values.length ) {
+ var optionSet, option,
+ options = elem.options,
+ values = jQuery.makeArray( value ),
+ i = options.length;
+
+ while ( i-- ) {
+ option = options[ i ];
+ if ( (option.selected = jQuery.inArray( jQuery(option).val(), values ) >= 0) ) {
+ optionSet = true;
+ }
+ }
+
+ // force browsers to behave consistently when non-matching value is set
+ if ( !optionSet ) {
elem.selectedIndex = -1;
}
return values;
@@ -2223,99 +4346,81 @@
}
},
- attrFn: {
- val: true,
- css: true,
- html: true,
- text: true,
- data: true,
- width: true,
- height: true,
- offset: true
- },
-
- attrFix: {
- // Always normalize to ensure hook usage
- tabindex: "tabIndex"
- },
-
- attr: function( elem, name, value, pass ) {
- var nType = elem.nodeType;
-
+ attr: function( elem, name, value ) {
+ var hooks, ret,
+ nType = elem.nodeType;
+
// don't get/set attributes on text, comment and attribute nodes
if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
- return undefined;
- }
-
- if ( pass && name in jQuery.attrFn ) {
- return jQuery( elem )[ name ]( value );
+ return;
}
// Fallback to prop when attributes are not supported
- if ( !("getAttribute" in elem) ) {
+ if ( typeof elem.getAttribute === core_strundefined ) {
return jQuery.prop( elem, name, value );
}
- var ret, hooks,
- notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
-
- // Normalize the name if needed
- if ( notxml ) {
- name = jQuery.attrFix[ name ] || name;
-
- hooks = jQuery.attrHooks[ name ];
-
- if ( !hooks ) {
- // Use boolHook for boolean attributes
- if ( rboolean.test( name ) ) {
- hooks = boolHook;
-
- // Use nodeHook if available( IE6/7 )
- } else if ( nodeHook ) {
- hooks = nodeHook;
- }
- }
+ // All attributes are lowercase
+ // Grab necessary hook if one is defined
+ if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
+ name = name.toLowerCase();
+ hooks = jQuery.attrHooks[ name ] ||
+ ( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook );
}
if ( value !== undefined ) {
if ( value === null ) {
jQuery.removeAttr( elem, name );
- return undefined;
-
- } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) {
+
+ } else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
return ret;
} else {
- elem.setAttribute( name, "" + value );
+ elem.setAttribute( name, value + "" );
return value;
}
- } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) {
+ } else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
return ret;
} else {
-
- ret = elem.getAttribute( name );
+ ret = jQuery.find.attr( elem, name );
// Non-existent attributes return null, we normalize to undefined
- return ret === null ?
+ return ret == null ?
undefined :
ret;
}
},
- removeAttr: function( elem, name ) {
- var propName;
- if ( elem.nodeType === 1 ) {
- name = jQuery.attrFix[ name ] || name;
-
- jQuery.attr( elem, name, "" );
- elem.removeAttribute( name );
-
- // Set corresponding property to false for boolean attributes
- if ( rboolean.test( name ) && (propName = jQuery.propFix[ name ] || name) in elem ) {
- elem[ propName ] = false;
+ removeAttr: function( elem, value ) {
+ var name, propName,
+ i = 0,
+ attrNames = value && value.match( core_rnotwhite );
+
+ if ( attrNames && elem.nodeType === 1 ) {
+ while ( (name = attrNames[i++]) ) {
+ propName = jQuery.propFix[ name ] || name;
+
+ // Boolean attributes get special treatment (#10870)
+ if ( jQuery.expr.match.bool.test( name ) ) {
+ // Set corresponding property to false
+ if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) {
+ elem[ propName ] = false;
+ // Support: IE<9
+ // Also clear defaultChecked/defaultSelected (if appropriate)
+ } else {
+ elem[ jQuery.camelCase( "default-" + name ) ] =
+ elem[ propName ] = false;
+ }
+
+ // See #9699 for explanation of this approach (setting first, then removal)
+ } else {
+ jQuery.attr( elem, name, "" );
+ }
+
+ elem.removeAttribute( getSetAttribute ? name : propName );
}
}
},
@@ -2323,13 +4428,9 @@
attrHooks: {
type: {
set: function( elem, value ) {
- // We can't allow the type property to be changed (since it causes problems in IE)
- if ( rtype.test( elem.nodeName ) && elem.parentNode ) {
- jQuery.error( "type property can't be changed" );
- } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
+ if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
// Setting the type on a radio button after the value resets the value in IE6-9
- // Reset value to it's default in case type is set after value
- // This is for element creation
+ // Reset value to default in case type is set after value during creation
var val = elem.value;
elem.setAttribute( "type", value );
if ( val ) {
@@ -2338,53 +4439,24 @@
return value;
}
}
- },
- // Use the value property for back compat
- // Use the nodeHook for button elements in IE6/7 (#1954)
- value: {
- get: function( elem, name ) {
- if ( nodeHook && jQuery.nodeName( elem, "button" ) ) {
- return nodeHook.get( elem, name );
- }
- return name in elem ?
- elem.value :
- null;
- },
- set: function( elem, value, name ) {
- if ( nodeHook && jQuery.nodeName( elem, "button" ) ) {
- return nodeHook.set( elem, value, name );
- }
- // Does not return so that setAttribute is also used
- elem.value = value;
- }
}
},
propFix: {
- tabindex: "tabIndex",
- readonly: "readOnly",
"for": "htmlFor",
- "class": "className",
- maxlength: "maxLength",
- cellspacing: "cellSpacing",
- cellpadding: "cellPadding",
- rowspan: "rowSpan",
- colspan: "colSpan",
- usemap: "useMap",
- frameborder: "frameBorder",
- contenteditable: "contentEditable"
- },
-
+ "class": "className"
+ },
+
prop: function( elem, name, value ) {
- var nType = elem.nodeType;
+ var ret, hooks, notxml,
+ nType = elem.nodeType;
// don't get/set properties on text, comment and attribute nodes
if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
- return undefined;
- }
-
- var ret, hooks,
- notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+ return;
+ }
+
+ notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
if ( notxml ) {
// Fix name and attach hooks
@@ -2393,122 +4465,170 @@
}
if ( value !== undefined ) {
- if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
- return ret;
-
- } else {
- return (elem[ name ] = value);
- }
+ return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ?
+ ret :
+ ( elem[ name ] = value );
} else {
- if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
- return ret;
-
- } else {
- return elem[ name ];
- }
- }
- },
-
+ return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ?
+ ret :
+ elem[ name ];
+ }
+ },
+
propHooks: {
tabIndex: {
get: function( elem ) {
// elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
// http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
- var attributeNode = elem.getAttributeNode("tabindex");
-
- return attributeNode && attributeNode.specified ?
- parseInt( attributeNode.value, 10 ) :
+ // Use proper attribute retrieval(#12072)
+ var tabindex = jQuery.find.attr( elem, "tabindex" );
+
+ return tabindex ?
+ parseInt( tabindex, 10 ) :
rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
0 :
- undefined;
+ -1;
}
}
}
});
-// Add the tabindex propHook to attrHooks for back-compat
-jQuery.attrHooks.tabIndex = jQuery.propHooks.tabIndex;
-
-// Hook for boolean attributes
+// Hooks for boolean attributes
boolHook = {
- get: function( elem, name ) {
- // Align boolean attributes with corresponding properties
- // Fall back to attribute presence where some booleans are not supported
- var attrNode;
- return jQuery.prop( elem, name ) === true || ( attrNode = elem.getAttributeNode( name ) ) && attrNode.nodeValue !== false ?
- name.toLowerCase() :
- undefined;
- },
set: function( elem, value, name ) {
- var propName;
if ( value === false ) {
// Remove boolean attributes when set to false
jQuery.removeAttr( elem, name );
+ } else if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) {
+ // IE<8 needs the *property* name
+ elem.setAttribute( !getSetAttribute && jQuery.propFix[ name ] || name, name );
+
+ // Use defaultChecked and defaultSelected for oldIE
} else {
- // value is true since we know at this point it's type boolean and not false
- // Set boolean attributes to the same name and set the DOM property
- propName = jQuery.propFix[ name ] || name;
- if ( propName in elem ) {
- // Only set the IDL specifically if it already exists on the element
- elem[ propName ] = true;
- }
-
- elem.setAttribute( name, name.toLowerCase() );
- }
+ elem[ jQuery.camelCase( "default-" + name ) ] = elem[ name ] = true;
+ }
+
return name;
}
};
+jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) {
+ var getter = jQuery.expr.attrHandle[ name ] || jQuery.find.attr;
+
+ jQuery.expr.attrHandle[ name ] = getSetInput && getSetAttribute || !ruseDefault.test( name ) ?
+ function( elem, name, isXML ) {
+ var fn = jQuery.expr.attrHandle[ name ],
+ ret = isXML ?
+ undefined :
+ /* jshint eqeqeq: false */
+ (jQuery.expr.attrHandle[ name ] = undefined) !=
+ getter( elem, name, isXML ) ?
+
+ name.toLowerCase() :
+ null;
+ jQuery.expr.attrHandle[ name ] = fn;
+ return ret;
+ } :
+ function( elem, name, isXML ) {
+ return isXML ?
+ undefined :
+ elem[ jQuery.camelCase( "default-" + name ) ] ?
+ name.toLowerCase() :
+ null;
+ };
+});
+
+// fix oldIE attroperties
+if ( !getSetInput || !getSetAttribute ) {
+ jQuery.attrHooks.value = {
+ set: function( elem, value, name ) {
+ if ( jQuery.nodeName( elem, "input" ) ) {
+ // Does not return so that setAttribute is also used
+ elem.defaultValue = value;
+ } else {
+ // Use nodeHook if defined (#1954); otherwise setAttribute is fine
+ return nodeHook && nodeHook.set( elem, value, name );
+ }
+ }
+ };
+}
// IE6/7 do not support getting/setting some attributes with get/setAttribute
-if ( !jQuery.support.getSetAttribute ) {
-
+if ( !getSetAttribute ) {
+
// Use this for any attribute in IE6/7
// This fixes almost every IE6/7 issue
- nodeHook = jQuery.valHooks.button = {
- get: function( elem, name ) {
- var ret;
- ret = elem.getAttributeNode( name );
- // Return undefined if nodeValue is empty string
- return ret && ret.nodeValue !== "" ?
- ret.nodeValue :
- undefined;
- },
+ nodeHook = {
set: function( elem, value, name ) {
// Set the existing or create a new attribute node
var ret = elem.getAttributeNode( name );
if ( !ret ) {
- ret = document.createAttribute( name );
- elem.setAttributeNode( ret );
- }
- return (ret.nodeValue = value + "");
+ elem.setAttributeNode(
+ (ret = elem.ownerDocument.createAttribute( name ))
+ );
+ }
+
+ ret.value = value += "";
+
+ // Break association with cloned elements by also using setAttribute (#9646)
+ return name === "value" || value === elem.getAttribute( name ) ?
+ value :
+ undefined;
+ }
+ };
+ jQuery.expr.attrHandle.id = jQuery.expr.attrHandle.name = jQuery.expr.attrHandle.coords =
+ // Some attributes are constructed with empty-string values when not defined
+ function( elem, name, isXML ) {
+ var ret;
+ return isXML ?
+ undefined :
+ (ret = elem.getAttributeNode( name )) && ret.value !== "" ?
+ ret.value :
+ null;
+ };
+ jQuery.valHooks.button = {
+ get: function( elem, name ) {
+ var ret = elem.getAttributeNode( name );
+ return ret && ret.specified ?
+ ret.value :
+ undefined;
+ },
+ set: nodeHook.set
+ };
+
+ // Set contenteditable to false on removals(#10429)
+ // Setting to empty string throws an error as an invalid value
+ jQuery.attrHooks.contenteditable = {
+ set: function( elem, value, name ) {
+ nodeHook.set( elem, value === "" ? false : value, name );
}
};
// Set width and height to auto instead of 0 on empty string( Bug #8150 )
// This is for removals
jQuery.each([ "width", "height" ], function( i, name ) {
- jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
+ jQuery.attrHooks[ name ] = {
set: function( elem, value ) {
if ( value === "" ) {
elem.setAttribute( name, "auto" );
return value;
}
}
- });
+ };
});
}
// Some attributes require a special call on IE
+// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
if ( !jQuery.support.hrefNormalized ) {
- jQuery.each([ "href", "src", "width", "height" ], function( i, name ) {
- jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
+ // href/src property should get the full normalized URL (#10299/#12915)
+ jQuery.each([ "href", "src" ], function( i, name ) {
+ jQuery.propHooks[ name ] = {
get: function( elem ) {
- var ret = elem.getAttribute( name, 2 );
- return ret === null ? undefined : ret;
- }
- });
+ return elem.getAttribute( name, 4 );
+ }
+ };
});
}
@@ -2516,11 +4636,12 @@
jQuery.attrHooks.style = {
get: function( elem ) {
// Return undefined in the case of empty string
- // Normalize to lowercase since IE uppercases css property names
- return elem.style.cssText.toLowerCase() || undefined;
+ // Note: IE uppercases css property names, but if we were to .toLowerCase()
+ // .cssText, that would destroy case senstitivity in URL's, like in "background"
+ return elem.style.cssText || undefined;
},
set: function( elem, value ) {
- return (elem.style.cssText = "" + value);
+ return ( elem.style.cssText = value + "" );
}
};
}
@@ -2528,7 +4649,7 @@
// Safari mis-reports the default selected property of an option
// Accessing the parent's selectedIndex property fixes it
if ( !jQuery.support.optSelected ) {
- jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, {
+ jQuery.propHooks.selected = {
get: function( elem ) {
var parent = elem.parentNode;
@@ -2542,143 +4663,153 @@
}
return null;
}
- });
+ };
+}
+
+jQuery.each([
+ "tabIndex",
+ "readOnly",
+ "maxLength",
+ "cellSpacing",
+ "cellPadding",
+ "rowSpan",
+ "colSpan",
+ "useMap",
+ "frameBorder",
+ "contentEditable"
+], function() {
+ jQuery.propFix[ this.toLowerCase() ] = this;
+});
+
+// IE6/7 call enctype encoding
+if ( !jQuery.support.enctype ) {
+ jQuery.propFix.enctype = "encoding";
}
// Radios and checkboxes getter/setter
-if ( !jQuery.support.checkOn ) {
- jQuery.each([ "radio", "checkbox" ], function() {
- jQuery.valHooks[ this ] = {
- get: function( elem ) {
- // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
- return elem.getAttribute("value") === null ? "on" : elem.value;
- }
- };
- });
-}
jQuery.each([ "radio", "checkbox" ], function() {
- jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], {
+ jQuery.valHooks[ this ] = {
set: function( elem, value ) {
if ( jQuery.isArray( value ) ) {
- return (elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0);
- }
- }
- });
+ return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
+ }
+ }
+ };
+ if ( !jQuery.support.checkOn ) {
+ jQuery.valHooks[ this ].get = function( elem ) {
+ // Support: Webkit
+ // "" is returned instead of "on" if a value isn't specified
+ return elem.getAttribute("value") === null ? "on" : elem.value;
+ };
+ }
});
-
-
-
-
-var rnamespaces = /\.(.*)$/,
- rformElems = /^(?:textarea|input|select)$/i,
- rperiod = /\./g,
- rspaces = / /g,
- rescape = /[^\w\s.|`]/g,
- fcleanup = function( nm ) {
- return nm.replace(rescape, "\\$&");
- };
+var rformElems = /^(?:input|select|textarea)$/i,
+ rkeyEvent = /^key/,
+ rmouseEvent = /^(?:mouse|contextmenu)|click/,
+ rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
+ rtypenamespace = /^([^.]*)(?:\.(.+)|)$/;
+
+function returnTrue() {
+ return true;
+}
+
+function returnFalse() {
+ return false;
+}
+
+function safeActiveElement() {
+ try {
+ return document.activeElement;
+ } catch ( err ) { }
+}
/*
- * A number of helper functions used for managing events.
- * Many of the ideas behind this code originated from
- * Dean Edwards' addEvent library.
+ * Helper functions for managing events -- not part of the public interface.
+ * Props to Dean Edwards' addEvent library for many of the ideas.
*/
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 ) {
+ global: {},
+
+ add: function( elem, types, handler, data, selector ) {
+ var tmp, events, t, handleObjIn,
+ special, eventHandle, handleObj,
+ handlers, type, namespaces, origType,
+ elemData = jQuery._data( elem );
+
+ // Don't attach events to noData or text/comment nodes (but allow plain objects)
+ if ( !elemData ) {
return;
}
- if ( handler === false ) {
- handler = returnFalse;
- } else if ( !handler ) {
- // Fixes bug #7229. Fix recommended by jdalton
- return;
- }
-
- var handleObjIn, handleObj;
-
+ // Caller can pass in an object of custom data in lieu of the handler
if ( handler.handler ) {
handleObjIn = handler;
handler = handleObjIn.handler;
- }
-
- // Make sure that the function being executed has a unique ID
+ selector = handleObjIn.selector;
+ }
+
+ // Make sure that the handler has a unique ID, used to find/remove it later
if ( !handler.guid ) {
handler.guid = jQuery.guid++;
}
- // Init the element's event structure
- var elemData = jQuery._data( elem );
-
- // If no elemData is found then we must be trying to bind to one of the
- // banned noData elements
- if ( !elemData ) {
- return;
- }
-
- var events = elemData.events,
- eventHandle = elemData.handle;
-
- if ( !events ) {
- elemData.events = events = {};
- }
-
- if ( !eventHandle ) {
- elemData.handle = eventHandle = function( e ) {
+ // Init the element's event structure and main handler, if this is the first
+ if ( !(events = elemData.events) ) {
+ events = elemData.events = {};
+ }
+ if ( !(eventHandle = elemData.handle) ) {
+ eventHandle = elemData.handle = function( e ) {
// Discard the second event of a jQuery.event.trigger() and
// when an event is called after a page has unloaded
- return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ?
- jQuery.event.handle.apply( eventHandle.elem, arguments ) :
+ return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ?
+ jQuery.event.dispatch.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;
+ // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
+ eventHandle.elem = elem;
+ }
// Handle multiple events separated by a space
- // jQuery(...).bind("mouseover mouseout", fn);
- types = types.split(" ");
-
- var type, i = 0, namespaces;
-
- while ( (type = types[ i++ ]) ) {
- handleObj = handleObjIn ?
- jQuery.extend({}, handleObjIn) :
- { handler: handler, data: data };
-
- // Namespaced event handlers
- if ( type.indexOf(".") > -1 ) {
- namespaces = type.split(".");
- type = namespaces.shift();
- handleObj.namespace = namespaces.slice(0).sort().join(".");
-
- } else {
- namespaces = [];
- handleObj.namespace = "";
- }
-
- handleObj.type = type;
- if ( !handleObj.guid ) {
- handleObj.guid = handler.guid;
- }
-
- // Get the current list of functions bound to this event
- var handlers = events[ type ],
- special = jQuery.event.special[ type ] || {};
-
- // Init the event handler queue
- if ( !handlers ) {
+ types = ( types || "" ).match( core_rnotwhite ) || [""];
+ t = types.length;
+ while ( t-- ) {
+ tmp = rtypenamespace.exec( types[t] ) || [];
+ type = origType = tmp[1];
+ namespaces = ( tmp[2] || "" ).split( "." ).sort();
+
+ // There *must* be a type, no attaching namespace-only handlers
+ if ( !type ) {
+ continue;
+ }
+
+ // If event changes its type, use the special event handlers for the changed type
+ special = jQuery.event.special[ type ] || {};
+
+ // If selector defined, determine special event api type, otherwise given type
+ type = ( selector ? special.delegateType : special.bindType ) || type;
+
+ // Update special based on newly reset type
+ special = jQuery.event.special[ type ] || {};
+
+ // handleObj is passed to all event handlers
+ handleObj = jQuery.extend({
+ type: type,
+ origType: origType,
+ data: data,
+ handler: handler,
+ guid: handler.guid,
+ selector: selector,
+ needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
+ namespace: namespaces.join(".")
+ }, handleObjIn );
+
+ // Init the event handler queue if we're the first
+ if ( !(handlers = events[ type ]) ) {
handlers = events[ type ] = [];
-
- // Check for a special event handler
- // Only use addEventListener/attachEvent if the special
- // events handler returns false
+ handlers.delegateCount = 0;
+
+ // 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 ) {
@@ -2698,10 +4829,14 @@
}
}
- // Add the function to the element's handler list
- handlers.push( handleObj );
-
- // Keep track of which events have been used, for event optimization
+ // Add to the element's handler list, delegates in front
+ if ( selector ) {
+ handlers.splice( handlers.delegateCount++, 0, handleObj );
+ } else {
+ handlers.push( handleObj );
+ }
+
+ // Keep track of which events have ever been used, for event optimization
jQuery.event.global[ type ] = true;
}
@@ -2709,150 +4844,97 @@
elem = null;
},
- global: {},
-
// Detach an event or set of events from an element
- remove: function( elem, types, handler, pos ) {
- // don't do events on text and comment nodes
- if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
- return;
- }
-
- if ( handler === false ) {
- handler = returnFalse;
- }
-
- var ret, type, fn, j, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
- elemData = jQuery.hasData( elem ) && jQuery._data( elem ),
- events = elemData && elemData.events;
-
- if ( !elemData || !events ) {
- return;
- }
-
- // types is actually an event object here
- if ( types && types.type ) {
- handler = types.handler;
- types = types.type;
- }
-
- // Unbind all events for the element
- if ( !types || typeof types === "string" && types.charAt(0) === "." ) {
- types = types || "";
-
- for ( type in events ) {
- jQuery.event.remove( elem, type + types );
- }
-
+ remove: function( elem, types, handler, selector, mappedTypes ) {
+ var j, handleObj, tmp,
+ origCount, t, events,
+ special, handlers, type,
+ namespaces, origType,
+ elemData = jQuery.hasData( elem ) && jQuery._data( elem );
+
+ if ( !elemData || !(events = elemData.events) ) {
return;
}
- // Handle multiple events separated by a space
- // jQuery(...).unbind("mouseover mouseout", fn);
- types = types.split(" ");
-
- while ( (type = types[ i++ ]) ) {
- origType = type;
- handleObj = null;
- all = type.indexOf(".") < 0;
- namespaces = [];
-
- if ( !all ) {
- // Namespaced event handlers
- namespaces = type.split(".");
- type = namespaces.shift();
-
- namespace = new RegExp("(^|\\.)" +
- jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)");
- }
-
- eventType = events[ type ];
-
- if ( !eventType ) {
- continue;
- }
-
- if ( !handler ) {
- for ( j = 0; j < eventType.length; j++ ) {
- handleObj = eventType[ j ];
-
- if ( all || namespace.test( handleObj.namespace ) ) {
- jQuery.event.remove( elem, origType, handleObj.handler, j );
- eventType.splice( j--, 1 );
- }
- }
-
+ // Once for each type.namespace in types; type may be omitted
+ types = ( types || "" ).match( core_rnotwhite ) || [""];
+ t = types.length;
+ while ( t-- ) {
+ tmp = rtypenamespace.exec( types[t] ) || [];
+ type = origType = tmp[1];
+ namespaces = ( tmp[2] || "" ).split( "." ).sort();
+
+ // Unbind all events (on this namespace, if provided) for the element
+ if ( !type ) {
+ for ( type in events ) {
+ jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
+ }
continue;
}
special = jQuery.event.special[ type ] || {};
-
- for ( j = pos || 0; j < eventType.length; j++ ) {
- handleObj = eventType[ j ];
-
- if ( handler.guid === handleObj.guid ) {
- // remove the given handler for the given type
- if ( all || namespace.test( handleObj.namespace ) ) {
- if ( pos == null ) {
- eventType.splice( j--, 1 );
- }
-
- if ( special.remove ) {
- special.remove.call( elem, handleObj );
- }
+ type = ( selector ? special.delegateType : special.bindType ) || type;
+ handlers = events[ type ] || [];
+ tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" );
+
+ // Remove matching events
+ origCount = j = handlers.length;
+ while ( j-- ) {
+ handleObj = handlers[ j ];
+
+ if ( ( mappedTypes || origType === handleObj.origType ) &&
+ ( !handler || handler.guid === handleObj.guid ) &&
+ ( !tmp || tmp.test( handleObj.namespace ) ) &&
+ ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
+ handlers.splice( j, 1 );
+
+ if ( handleObj.selector ) {
+ handlers.delegateCount--;
}
-
- if ( pos != null ) {
- break;
+ if ( special.remove ) {
+ special.remove.call( elem, handleObj );
}
}
}
- // 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 ) {
+ // Remove generic event handler if we removed something and no more handlers exist
+ // (avoids potential for endless recursion during removal of special event handlers)
+ if ( origCount && !handlers.length ) {
+ if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
jQuery.removeEvent( elem, type, elemData.handle );
}
- ret = null;
delete events[ type ];
}
}
// Remove the expando if it's no longer used
if ( jQuery.isEmptyObject( events ) ) {
- var handle = elemData.handle;
- if ( handle ) {
- handle.elem = null;
- }
-
- delete elemData.events;
delete elemData.handle;
- if ( jQuery.isEmptyObject( elemData ) ) {
- jQuery.removeData( elem, undefined, true );
- }
- }
- },
-
- // Events that are safe to short-circuit if no handlers are attached.
- // Native DOM events should not be added, they may have inline handlers.
- customEvent: {
- "getData": true,
- "setData": true,
- "changeData": true
+ // removeData also checks for emptiness and clears the expando if empty
+ // so use it instead of delete
+ jQuery._removeData( elem, "events" );
+ }
},
trigger: function( event, data, elem, onlyHandlers ) {
- // Event object or event type
- var type = event.type || event,
- namespaces = [],
- exclusive;
-
- if ( type.indexOf("!") >= 0 ) {
- // Exclusive events trigger only for the exact event (no namespaces)
- type = type.slice(0, -1);
- exclusive = true;
+ var handle, ontype, cur,
+ bubbleType, special, tmp, i,
+ eventPath = [ elem || document ],
+ type = core_hasOwn.call( event, "type" ) ? event.type : event,
+ namespaces = core_hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : [];
+
+ cur = tmp = elem = elem || document;
+
+ // Don't do events on text and comment nodes
+ if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
+ return;
+ }
+
+ // focus/blur morphs to focusin/out; ensure we're not firing them right now
+ if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
+ return;
}
if ( type.indexOf(".") >= 0 ) {
@@ -2861,261 +4943,396 @@
type = namespaces.shift();
namespaces.sort();
}
-
- if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) {
- // No jQuery handlers for this event type, and it can't have inline handlers
- return;
- }
-
- // Caller can pass in an Event, Object, or just an event type string
- event = typeof event === "object" ?
- // jQuery.Event object
- event[ jQuery.expando ] ? event :
- // Object literal
- new jQuery.Event( type, event ) :
- // Just the event type (string)
- new jQuery.Event( type );
-
- event.type = type;
- event.exclusive = exclusive;
+ ontype = type.indexOf(":") < 0 && "on" + type;
+
+ // Caller can pass in a jQuery.Event object, Object, or just an event type string
+ event = event[ jQuery.expando ] ?
+ event :
+ new jQuery.Event( type, typeof event === "object" && event );
+
+ // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
+ event.isTrigger = onlyHandlers ? 2 : 3;
event.namespace = namespaces.join(".");
- event.namespace_re = new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)");
-
- // triggerHandler() and global events don't bubble or run the default action
- if ( onlyHandlers || !elem ) {
- event.preventDefault();
- event.stopPropagation();
- }
-
- // Handle a global trigger
- if ( !elem ) {
- // TODO: Stop taunting the data cache; remove global events and always attach to document
- jQuery.each( jQuery.cache, function() {
- // internalKey variable is just used to make it easier to find
- // and potentially change this stuff later; currently it just
- // points to jQuery.expando
- var internalKey = jQuery.expando,
- internalCache = this[ internalKey ];
- if ( internalCache && internalCache.events && internalCache.events[ type ] ) {
- jQuery.event.trigger( event, data, internalCache.handle.elem );
- }
- });
- return;
- }
-
- // Don't do events on text and comment nodes
- if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
- return;
- }
+ event.namespace_re = event.namespace ?
+ new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) :
+ null;
// Clean up the event in case it is being reused
event.result = undefined;
- event.target = elem;
+ if ( !event.target ) {
+ event.target = elem;
+ }
// Clone any incoming data and prepend the event, creating the handler arg list
- data = data != null ? jQuery.makeArray( data ) : [];
- data.unshift( event );
-
- var cur = elem,
- // IE doesn't like method names with a colon (#3533, #8272)
- ontype = type.indexOf(":") < 0 ? "on" + type : "";
-
- // Fire event on the current element, then bubble up the DOM tree
- do {
- var handle = jQuery._data( cur, "handle" );
-
- event.currentTarget = cur;
+ data = data == null ?
+ [ event ] :
+ jQuery.makeArray( data, [ event ] );
+
+ // Allow special events to draw outside the lines
+ special = jQuery.event.special[ type ] || {};
+ if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
+ return;
+ }
+
+ // Determine event propagation path in advance, per W3C events spec (#9951)
+ // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
+ if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
+
+ bubbleType = special.delegateType || type;
+ if ( !rfocusMorph.test( bubbleType + type ) ) {
+ cur = cur.parentNode;
+ }
+ for ( ; cur; cur = cur.parentNode ) {
+ eventPath.push( cur );
+ tmp = cur;
+ }
+
+ // Only add window if we got to document (e.g., not plain obj or detached DOM)
+ if ( tmp === (elem.ownerDocument || document) ) {
+ eventPath.push( tmp.defaultView || tmp.parentWindow || window );
+ }
+ }
+
+ // Fire handlers on the event path
+ i = 0;
+ while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) {
+
+ event.type = i > 1 ?
+ bubbleType :
+ special.bindType || type;
+
+ // jQuery handler
+ handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
if ( handle ) {
handle.apply( cur, data );
}
- // Trigger an inline bound script
- if ( ontype && jQuery.acceptData( cur ) && cur[ ontype ] && cur[ ontype ].apply( cur, data ) === false ) {
- event.result = false;
+ // Native handler
+ handle = ontype && cur[ ontype ];
+ if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) {
event.preventDefault();
}
-
- // Bubble up to document, then to window
- cur = cur.parentNode || cur.ownerDocument || cur === event.target.ownerDocument && window;
- } while ( cur && !event.isPropagationStopped() );
+ }
+ event.type = type;
// If nobody prevented the default action, do it now
- if ( !event.isDefaultPrevented() ) {
- var old,
- special = jQuery.event.special[ type ] || {};
-
- if ( (!special._default || special._default.call( elem.ownerDocument, event ) === false) &&
- !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {
+ if ( !onlyHandlers && !event.isDefaultPrevented() ) {
+
+ if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) &&
+ jQuery.acceptData( elem ) ) {
// Call a native DOM method on the target with the same name name as the event.
- // Can't use an .isFunction)() check here because IE6/7 fails that test.
- // IE<9 dies on focus to hidden element (#1486), may want to revisit a try/catch.
- try {
- if ( ontype && elem[ type ] ) {
- // Don't re-trigger an onFOO event when we call its FOO() method
- old = elem[ ontype ];
-
- if ( old ) {
- elem[ ontype ] = null;
- }
-
- jQuery.event.triggered = type;
+ // Can't use an .isFunction() check here because IE6/7 fails that test.
+ // Don't do default actions on window, that's where global variables be (#6170)
+ if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) {
+
+ // Don't re-trigger an onFOO event when we call its FOO() method
+ tmp = elem[ ontype ];
+
+ if ( tmp ) {
+ elem[ ontype ] = null;
+ }
+
+ // Prevent re-triggering of the same event, since we already bubbled it above
+ jQuery.event.triggered = type;
+ try {
elem[ type ]();
+ } catch ( e ) {
+ // IE<9 dies on focus/blur to hidden element (#1486,#12518)
+ // only reproducible on winXP IE8 native, not IE9 in IE8 mode
}
- } catch ( ieError ) {}
-
- if ( old ) {
- elem[ ontype ] = old;
- }
-
- jQuery.event.triggered = undefined;
- }
- }
-
+ jQuery.event.triggered = undefined;
+
+ if ( tmp ) {
+ elem[ ontype ] = tmp;
+ }
+ }
+ }
+ }
+
return event.result;
},
- handle: function( event ) {
- event = jQuery.event.fix( event || window.event );
- // Snapshot the handlers list since a called handler may add/remove events.
- var handlers = ((jQuery._data( this, "events" ) || {})[ event.type ] || []).slice(0),
- run_all = !event.exclusive && !event.namespace,
- args = Array.prototype.slice.call( arguments, 0 );
-
- // Use the fix-ed Event rather than the (read-only) native event
+ dispatch: function( event ) {
+
+ // Make a writable jQuery.Event from the native event object
+ event = jQuery.event.fix( event );
+
+ var i, ret, handleObj, matched, j,
+ handlerQueue = [],
+ args = core_slice.call( arguments ),
+ handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [],
+ special = jQuery.event.special[ event.type ] || {};
+
+ // Use the fix-ed jQuery.Event rather than the (read-only) native event
args[0] = event;
- event.currentTarget = this;
-
- for ( var j = 0, l = handlers.length; j < l; j++ ) {
- var handleObj = handlers[ j ];
-
- // Triggered event must 1) be non-exclusive and have no namespace, or
- // 2) have namespace(s) a subset or equal to those in the bound event.
- if ( run_all || event.namespace_re.test( handleObj.namespace ) ) {
- // Pass in a reference to the handler function itself
- // So that we can later remove it
- event.handler = handleObj.handler;
- event.data = handleObj.data;
- event.handleObj = handleObj;
-
- var ret = handleObj.handler.apply( this, args );
-
- if ( ret !== undefined ) {
- event.result = ret;
- if ( ret === false ) {
- event.preventDefault();
- event.stopPropagation();
+ event.delegateTarget = this;
+
+ // Call the preDispatch hook for the mapped type, and let it bail if desired
+ if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
+ return;
+ }
+
+ // Determine handlers
+ handlerQueue = jQuery.event.handlers.call( this, event, handlers );
+
+ // Run delegates first; they may want to stop propagation beneath us
+ i = 0;
+ while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) {
+ event.currentTarget = matched.elem;
+
+ j = 0;
+ while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) {
+
+ // Triggered event must either 1) have no namespace, or
+ // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
+ if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) {
+
+ event.handleObj = handleObj;
+ event.data = handleObj.data;
+
+ ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
+ .apply( matched.elem, args );
+
+ if ( ret !== undefined ) {
+ if ( (event.result = ret) === false ) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
}
}
-
- if ( event.isImmediatePropagationStopped() ) {
- break;
- }
- }
- }
+ }
+ }
+
+ // Call the postDispatch hook for the mapped type
+ if ( special.postDispatch ) {
+ special.postDispatch.call( this, event );
+ }
+
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 pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+ handlers: function( event, handlers ) {
+ var sel, handleObj, matches, i,
+ handlerQueue = [],
+ delegateCount = handlers.delegateCount,
+ cur = event.target;
+
+ // Find delegate handlers
+ // Black-hole SVG instance trees (#13180)
+ // Avoid non-left-click bubbling in Firefox (#3861)
+ if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) {
+
+ /* jshint eqeqeq: false */
+ for ( ; cur != this; cur = cur.parentNode || this ) {
+ /* jshint eqeqeq: true */
+
+ // Don't check non-elements (#13208)
+ // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
+ if ( cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click") ) {
+ matches = [];
+ for ( i = 0; i < delegateCount; i++ ) {
+ handleObj = handlers[ i ];
+
+ // Don't conflict with Object.prototype properties (#13203)
+ sel = handleObj.selector + " ";
+
+ if ( matches[ sel ] === undefined ) {
+ matches[ sel ] = handleObj.needsContext ?
+ jQuery( sel, this ).index( cur ) >= 0 :
+ jQuery.find( sel, this, null, [ cur ] ).length;
+ }
+ if ( matches[ sel ] ) {
+ matches.push( handleObj );
+ }
+ }
+ if ( matches.length ) {
+ handlerQueue.push({ elem: cur, handlers: matches });
+ }
+ }
+ }
+ }
+
+ // Add the remaining (directly-bound) handlers
+ if ( delegateCount < handlers.length ) {
+ handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) });
+ }
+
+ return handlerQueue;
+ },
fix: function( event ) {
if ( event[ jQuery.expando ] ) {
return event;
}
- // store a copy of the original event object
- // and "clone" to set read-only properties
- var originalEvent = event;
- event = jQuery.Event( originalEvent );
-
- for ( var i = this.props.length, prop; i; ) {
- prop = this.props[ --i ];
+ // Create a writable copy of the event object and normalize some properties
+ var i, prop, copy,
+ type = event.type,
+ originalEvent = event,
+ fixHook = this.fixHooks[ type ];
+
+ if ( !fixHook ) {
+ this.fixHooks[ type ] = fixHook =
+ rmouseEvent.test( type ) ? this.mouseHooks :
+ rkeyEvent.test( type ) ? this.keyHooks :
+ {};
+ }
+ copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
+
+ event = new jQuery.Event( originalEvent );
+
+ i = copy.length;
+ while ( i-- ) {
+ prop = copy[ i ];
event[ prop ] = originalEvent[ prop ];
}
- // Fix target property, if necessary
+ // Support: IE<9
+ // Fix target property (#1925)
if ( !event.target ) {
- // Fixes #1925 where srcElement might not be defined either
- event.target = event.srcElement || document;
- }
-
- // check if target is a textnode (safari)
+ event.target = originalEvent.srcElement || document;
+ }
+
+ // Support: Chrome 23+, Safari?
+ // Target should not be a text node (#504, #13143)
if ( event.target.nodeType === 3 ) {
event.target = event.target.parentNode;
}
- // Add relatedTarget, if necessary
- if ( !event.relatedTarget && event.fromElement ) {
- event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
- }
-
- // Calculate pageX/Y if missing and clientX/Y available
- if ( event.pageX == null && event.clientX != null ) {
- var eventDocument = event.target.ownerDocument || document,
- doc = eventDocument.documentElement,
- body = eventDocument.body;
-
- event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
- event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
- }
-
- // Add which for key events
- if ( event.which == null && (event.charCode != null || event.keyCode != null) ) {
- event.which = event.charCode != null ? event.charCode : event.keyCode;
- }
-
- // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
- if ( !event.metaKey && event.ctrlKey ) {
- event.metaKey = event.ctrlKey;
- }
-
- // Add which for click: 1 === left; 2 === middle; 3 === right
- // Note: button is not normalized, so don't use it
- if ( !event.which && event.button !== undefined ) {
- event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
- }
-
- return event;
- },
-
- // Deprecated, use jQuery.guid instead
- guid: 1E8,
-
- // Deprecated, use jQuery.proxy instead
- proxy: jQuery.proxy,
+ // Support: IE<9
+ // For mouse/key events, metaKey==false if it's undefined (#3368, #11328)
+ event.metaKey = !!event.metaKey;
+
+ return fixHook.filter ? fixHook.filter( event, originalEvent ) : event;
+ },
+
+ // Includes some event props shared by KeyEvent and MouseEvent
+ props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
+
+ fixHooks: {},
+
+ keyHooks: {
+ props: "char charCode key keyCode".split(" "),
+ filter: function( event, original ) {
+
+ // Add which for key events
+ if ( event.which == null ) {
+ event.which = original.charCode != null ? original.charCode : original.keyCode;
+ }
+
+ return event;
+ }
+ },
+
+ mouseHooks: {
+ props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
+ filter: function( event, original ) {
+ var body, eventDoc, doc,
+ button = original.button,
+ fromElement = original.fromElement;
+
+ // Calculate pageX/Y if missing and clientX/Y available
+ if ( event.pageX == null && original.clientX != null ) {
+ eventDoc = event.target.ownerDocument || document;
+ doc = eventDoc.documentElement;
+ body = eventDoc.body;
+
+ event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
+ event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 );
+ }
+
+ // Add relatedTarget, if necessary
+ if ( !event.relatedTarget && fromElement ) {
+ event.relatedTarget = fromElement === event.target ? original.toElement : fromElement;
+ }
+
+ // Add which for click: 1 === left; 2 === middle; 3 === right
+ // Note: button is not normalized, so don't use it
+ if ( !event.which && button !== undefined ) {
+ event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
+ }
+
+ return event;
+ }
+ },
special: {
- ready: {
- // Make sure the ready event is setup
- setup: jQuery.bindReady,
- teardown: jQuery.noop
+ load: {
+ // Prevent triggered image.load events from bubbling to window.load
+ noBubble: true
+ },
+ focus: {
+ // Fire native event if possible so blur/focus sequence is correct
+ trigger: function() {
+ if ( this !== safeActiveElement() && this.focus ) {
+ try {
+ this.focus();
+ return false;
+ } catch ( e ) {
+ // Support: IE<9
+ // If we error on focus to hidden element (#1486, #12518),
+ // let .trigger() run the handlers
+ }
+ }
+ },
+ delegateType: "focusin"
},
-
- live: {
- add: function( handleObj ) {
- jQuery.event.add( this,
- liveConvert( handleObj.origType, handleObj.selector ),
- jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) );
+ blur: {
+ trigger: function() {
+ if ( this === safeActiveElement() && this.blur ) {
+ this.blur();
+ return false;
+ }
},
-
- remove: function( handleObj ) {
- jQuery.event.remove( this, liveConvert( handleObj.origType, handleObj.selector ), handleObj );
+ delegateType: "focusout"
+ },
+ click: {
+ // For checkbox, fire native event so checked state will be right
+ trigger: function() {
+ if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) {
+ this.click();
+ return false;
+ }
+ },
+
+ // For cross-browser consistency, don't fire native .click() on links
+ _default: function( event ) {
+ return jQuery.nodeName( event.target, "a" );
}
},
beforeunload: {
- setup: function( data, namespaces, eventHandle ) {
- // We only want to do this special case on windows
- if ( jQuery.isWindow( this ) ) {
- this.onbeforeunload = eventHandle;
- }
- },
-
- teardown: function( namespaces, eventHandle ) {
- if ( this.onbeforeunload === eventHandle ) {
- this.onbeforeunload = null;
- }
- }
+ postDispatch: function( event ) {
+
+ // Even when returnValue equals to undefined Firefox will still show alert
+ if ( event.result !== undefined ) {
+ event.originalEvent.returnValue = event.result;
+ }
+ }
+ }
+ },
+
+ simulate: function( type, elem, event, bubble ) {
+ // Piggyback on a donor event to simulate a different one.
+ // Fake originalEvent to avoid donor's stopPropagation, but if the
+ // simulated event prevents default then we do the same on the donor.
+ var e = jQuery.extend(
+ new jQuery.Event(),
+ event,
+ {
+ type: type,
+ isSimulated: true,
+ originalEvent: {}
+ }
+ );
+ if ( bubble ) {
+ jQuery.event.trigger( e, null, elem );
+ } else {
+ jQuery.event.dispatch.call( elem, e );
+ }
+ if ( e.isDefaultPrevented() ) {
+ event.preventDefault();
}
}
};
@@ -3127,14 +5344,23 @@
}
} :
function( elem, type, handle ) {
+ var name = "on" + type;
+
if ( elem.detachEvent ) {
- elem.detachEvent( "on" + type, handle );
+
+ // #8545, #7054, preventing memory leaks for custom events in IE6-8
+ // detachEvent needed property on element, by name of that event, to properly expose it to GC
+ if ( typeof elem[ name ] === core_strundefined ) {
+ elem[ name ] = null;
+ }
+
+ elem.detachEvent( name, handle );
}
};
jQuery.Event = function( src, props ) {
// Allow instantiation without the 'new' keyword
- if ( !this.preventDefault ) {
+ if ( !(this instanceof jQuery.Event) ) {
return new jQuery.Event( src, props );
}
@@ -3145,8 +5371,8 @@
// Events bubbling up the document may have been marked as prevented
// by a handler lower down the tree; reflect the correct value.
- this.isDefaultPrevented = (src.defaultPrevented || src.returnValue === false ||
- src.getPreventDefault && src.getPreventDefault()) ? returnTrue : returnFalse;
+ this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false ||
+ src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse;
// Event type
} else {
@@ -3158,274 +5384,191 @@
jQuery.extend( this, props );
}
- // timeStamp is buggy for some events on Firefox(#3843)
- // So we won't rely on the native value
- this.timeStamp = jQuery.now();
+ // Create a timestamp if incoming event doesn't have one
+ this.timeStamp = src && src.timeStamp || jQuery.now();
// Mark it as fixed
this[ jQuery.expando ] = true;
};
-function returnFalse() {
- return false;
-}
-function returnTrue() {
- return true;
-}
-
// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
jQuery.Event.prototype = {
+ isDefaultPrevented: returnFalse,
+ isPropagationStopped: returnFalse,
+ isImmediatePropagationStopped: returnFalse,
+
preventDefault: function() {
+ var e = this.originalEvent;
+
this.isDefaultPrevented = returnTrue;
-
- var e = this.originalEvent;
if ( !e ) {
return;
}
- // if preventDefault exists run it on the original event
+ // 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)
+ // Support: IE
+ // Otherwise set the returnValue property of the original event to false
} else {
e.returnValue = false;
}
},
stopPropagation: function() {
+ var e = this.originalEvent;
+
this.isPropagationStopped = returnTrue;
-
- var e = this.originalEvent;
if ( !e ) {
return;
}
- // if stopPropagation exists run it on the original event
+ // 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)
+
+ // Support: IE
+ // Set the cancelBubble property of the original event to true
e.cancelBubble = true;
},
stopImmediatePropagation: function() {
this.isImmediatePropagationStopped = returnTrue;
this.stopPropagation();
- },
- isDefaultPrevented: returnFalse,
- isPropagationStopped: returnFalse,
- isImmediatePropagationStopped: returnFalse
+ }
};
-// Checks if an event happened on an element within another element
-// Used in jQuery.event.special.mouseenter and mouseleave handlers
-var withinElement = function( event ) {
-
- // Check if mouse(over|out) are still within the same parent element
- var related = event.relatedTarget,
- inside = false,
- eventType = event.type;
-
- event.type = event.data;
-
- if ( related !== this ) {
-
- if ( related ) {
- inside = jQuery.contains( this, related );
- }
-
- if ( !inside ) {
-
- jQuery.event.handle.apply( this, arguments );
-
- event.type = eventType;
- }
- }
-},
-
-// In case of event delegation, we only need to rename the event.type,
-// liveHandler will take care of the rest.
-delegate = function( event ) {
- event.type = event.data;
- jQuery.event.handle.apply( this, arguments );
-};
-
-// Create mouseenter and mouseleave events
+// Create mouseenter/leave events using mouseover/out and event-time checks
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 );
+ delegateType: fix,
+ bindType: fix,
+
+ handle: function( event ) {
+ var ret,
+ target = this,
+ related = event.relatedTarget,
+ handleObj = event.handleObj;
+
+ // For mousenter/leave call the handler if related is outside the target.
+ // NB: No relatedTarget if the mouse left/entered the browser window
+ if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
+ event.type = handleObj.origType;
+ ret = handleObj.handler.apply( this, arguments );
+ event.type = fix;
+ }
+ return ret;
}
};
});
-// submit delegation
+// IE submit delegation
if ( !jQuery.support.submitBubbles ) {
jQuery.event.special.submit = {
- setup: function( data, namespaces ) {
- if ( !jQuery.nodeName( this, "form" ) ) {
- jQuery.event.add(this, "click.specialSubmit", function( e ) {
- // Avoid triggering error on non-existent type attribute in IE VML (#7071)
- var elem = e.target,
- type = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.type : "";
-
- if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
- trigger( "submit", this, arguments );
- }
- });
-
- jQuery.event.add(this, "keypress.specialSubmit", function( e ) {
- var elem = e.target,
- type = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.type : "";
-
- if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
- trigger( "submit", this, arguments );
- }
- });
-
- } else {
+ setup: function() {
+ // Only need this for delegated form submit events
+ if ( jQuery.nodeName( this, "form" ) ) {
return false;
}
+
+ // Lazy-add a submit handler when a descendant form may potentially be submitted
+ jQuery.event.add( this, "click._submit keypress._submit", function( e ) {
+ // Node name check avoids a VML-related crash in IE (#9807)
+ var elem = e.target,
+ form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined;
+ if ( form && !jQuery._data( form, "submitBubbles" ) ) {
+ jQuery.event.add( form, "submit._submit", function( event ) {
+ event._submit_bubble = true;
+ });
+ jQuery._data( form, "submitBubbles", true );
+ }
+ });
+ // return undefined since we don't need an event listener
+ },
+
+ postDispatch: function( event ) {
+ // If form was submitted by the user, bubble the event up the tree
+ if ( event._submit_bubble ) {
+ delete event._submit_bubble;
+ if ( this.parentNode && !event.isTrigger ) {
+ jQuery.event.simulate( "submit", this.parentNode, event, true );
+ }
+ }
},
- teardown: function( namespaces ) {
- jQuery.event.remove( this, ".specialSubmit" );
+ teardown: function() {
+ // Only need this for delegated form submit events
+ if ( jQuery.nodeName( this, "form" ) ) {
+ return false;
+ }
+
+ // Remove delegated handlers; cleanData eventually reaps submit handlers attached above
+ jQuery.event.remove( this, "._submit" );
}
};
-
}
-// change delegation, happens here so we have bind.
+// IE change delegation and checkbox/radio fix
if ( !jQuery.support.changeBubbles ) {
- var changeFilters,
-
- getVal = function( elem ) {
- var type = jQuery.nodeName( elem, "input" ) ? elem.type : "",
- val = elem.value;
-
- if ( type === "radio" || type === "checkbox" ) {
- val = elem.checked;
-
- } else if ( type === "select-multiple" ) {
- val = elem.selectedIndex > -1 ?
- jQuery.map( elem.options, function( elem ) {
- return elem.selected;
- }).join("-") :
- "";
-
- } else if ( jQuery.nodeName( elem, "select" ) ) {
- val = elem.selectedIndex;
- }
-
- return val;
- },
-
- testChange = function testChange( e ) {
- var elem = e.target, data, val;
-
- if ( !rformElems.test( elem.nodeName ) || elem.readOnly ) {
- return;
- }
-
- data = jQuery._data( elem, "_change_data" );
- val = getVal(elem);
-
- // the current data will be also retrieved by beforeactivate
- if ( e.type !== "focusout" || elem.type !== "radio" ) {
- jQuery._data( elem, "_change_data", val );
- }
-
- if ( data === undefined || val === data ) {
- return;
- }
-
- if ( data != null || val ) {
- e.type = "change";
- e.liveFired = undefined;
- jQuery.event.trigger( e, arguments[1], elem );
- }
- };
-
jQuery.event.special.change = {
- filters: {
- focusout: testChange,
-
- beforedeactivate: testChange,
-
- click: function( e ) {
- var elem = e.target, type = jQuery.nodeName( elem, "input" ) ? elem.type : "";
-
- if ( type === "radio" || type === "checkbox" || jQuery.nodeName( elem, "select" ) ) {
- testChange.call( this, e );
- }
- },
-
- // Change has to be called before submit
- // Keydown will be called before keypress, which is used in submit-event delegation
- keydown: function( e ) {
- var elem = e.target, type = jQuery.nodeName( elem, "input" ) ? elem.type : "";
-
- if ( (e.keyCode === 13 && !jQuery.nodeName( elem, "textarea" ) ) ||
- (e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
- type === "select-multiple" ) {
- testChange.call( this, e );
- }
- },
-
- // Beforeactivate happens also before the previous element is blurred
- // with this event you can't trigger a change event, but you can store
- // information
- beforeactivate: function( e ) {
+
+ setup: function() {
+
+ if ( rformElems.test( this.nodeName ) ) {
+ // IE doesn't fire change on a check/radio until blur; trigger it on click
+ // after a propertychange. Eat the blur-change in special.change.handle.
+ // This still fires onchange a second time for check/radio after blur.
+ if ( this.type === "checkbox" || this.type === "radio" ) {
+ jQuery.event.add( this, "propertychange._change", function( event ) {
+ if ( event.originalEvent.propertyName === "checked" ) {
+ this._just_changed = true;
+ }
+ });
+ jQuery.event.add( this, "click._change", function( event ) {
+ if ( this._just_changed && !event.isTrigger ) {
+ this._just_changed = false;
+ }
+ // Allow triggered, simulated change events (#11500)
+ jQuery.event.simulate( "change", this, event, true );
+ });
+ }
+ return false;
+ }
+ // Delegated event; lazy-add a change handler on descendant inputs
+ jQuery.event.add( this, "beforeactivate._change", function( e ) {
var elem = e.target;
- jQuery._data( elem, "_change_data", getVal(elem) );
+
+ if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) {
+ jQuery.event.add( elem, "change._change", function( event ) {
+ if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
+ jQuery.event.simulate( "change", this.parentNode, event, true );
+ }
+ });
+ jQuery._data( elem, "changeBubbles", true );
+ }
+ });
+ },
+
+ handle: function( event ) {
+ var elem = event.target;
+
+ // Swallow native change events from checkbox/radio, we already triggered them above
+ if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) {
+ return event.handleObj.handler.apply( this, arguments );
}
},
- setup: function( data, namespaces ) {
- if ( this.type === "file" ) {
- return false;
- }
-
- for ( var type in changeFilters ) {
- jQuery.event.add( this, type + ".specialChange", changeFilters[type] );
- }
-
- return rformElems.test( this.nodeName );
- },
-
- teardown: function( namespaces ) {
- jQuery.event.remove( this, ".specialChange" );
-
- return rformElems.test( this.nodeName );
+ teardown: function() {
+ jQuery.event.remove( this, "._change" );
+
+ return !rformElems.test( this.nodeName );
}
};
-
- changeFilters = jQuery.event.special.change.filters;
-
- // Handle when the input is .focus()'d
- changeFilters.focus = changeFilters.beforeactivate;
-}
-
-function trigger( type, elem, args ) {
- // Piggyback on a donor event to simulate a different one.
- // Fake originalEvent to avoid donor's stopPropagation, but if the
- // simulated event prevents default then we do the same on the donor.
- // Don't pass args or remember liveFired; they apply to the donor event.
- var event = jQuery.extend( {}, args[ 0 ] );
- event.type = type;
- event.originalEvent = {};
- event.liveFired = undefined;
- jQuery.event.handle.call( elem, event );
- if ( event.isDefaultPrevented() ) {
- args[ 0 ].preventDefault();
- }
}
// Create "bubbling" focus and blur events
@@ -3433,7 +5576,10 @@
jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
// Attach a single capturing handler while someone wants focusin/focusout
- var attaches = 0;
+ var attaches = 0,
+ handler = function( event ) {
+ jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
+ };
jQuery.event.special[ fix ] = {
setup: function() {
@@ -3447,89 +5593,97 @@
}
}
};
-
- function handler( donor ) {
- // Donor event is always a native one; fix it and switch its type.
- // Let focusin/out handler cancel the donor focus/blur event.
- var e = jQuery.event.fix( donor );
- e.type = fix;
- e.originalEvent = {};
- jQuery.event.trigger( e, null, e.target );
- if ( e.isDefaultPrevented() ) {
- donor.preventDefault();
- }
- }
});
}
-jQuery.each(["bind", "one"], function( i, name ) {
- jQuery.fn[ name ] = function( type, data, fn ) {
- var handler;
-
- // Handle object literals
- if ( typeof type === "object" ) {
- for ( var key in type ) {
- this[ name ](key, data, type[key], fn);
+jQuery.fn.extend({
+
+ on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
+ var type, origFn;
+
+ // Types can be a map of types/handlers
+ if ( typeof types === "object" ) {
+ // ( types-Object, selector, data )
+ if ( typeof selector !== "string" ) {
+ // ( types-Object, data )
+ data = data || selector;
+ selector = undefined;
+ }
+ for ( type in types ) {
+ this.on( type, selector, data, types[ type ], one );
}
return this;
}
- if ( arguments.length === 2 || data === false ) {
- fn = data;
- data = undefined;
- }
-
- if ( name === "one" ) {
- handler = function( event ) {
- jQuery( this ).unbind( event, handler );
- return fn.apply( this, arguments );
+ if ( data == null && fn == null ) {
+ // ( types, fn )
+ fn = selector;
+ data = selector = undefined;
+ } else if ( fn == null ) {
+ if ( typeof selector === "string" ) {
+ // ( types, selector, fn )
+ fn = data;
+ data = undefined;
+ } else {
+ // ( types, data, fn )
+ fn = data;
+ data = selector;
+ selector = undefined;
+ }
+ }
+ if ( fn === false ) {
+ fn = returnFalse;
+ } else if ( !fn ) {
+ return this;
+ }
+
+ if ( one === 1 ) {
+ origFn = fn;
+ fn = function( event ) {
+ // Can use an empty set, since event contains the info
+ jQuery().off( event );
+ return origFn.apply( this, arguments );
};
- handler.guid = fn.guid || jQuery.guid++;
- } else {
- handler = fn;
- }
-
- if ( type === "unload" && name !== "one" ) {
- this.one( type, data, fn );
-
- } else {
- for ( var i = 0, l = this.length; i < l; i++ ) {
- jQuery.event.add( this[i], type, handler, data );
- }
- }
-
- return this;
- };
-});
-
-jQuery.fn.extend({
- unbind: function( type, fn ) {
- // Handle object literals
- if ( typeof type === "object" && !type.preventDefault ) {
- for ( var key in type ) {
- this.unbind(key, type[key]);
- }
-
- } else {
- for ( var i = 0, l = this.length; i < l; i++ ) {
- jQuery.event.remove( this[i], type, fn );
- }
- }
-
- return this;
- },
-
- delegate: function( selector, types, data, fn ) {
- return this.live( types, data, fn, selector );
- },
-
- undelegate: function( selector, types, fn ) {
- if ( arguments.length === 0 ) {
- return this.unbind( "live" );
-
- } else {
- return this.die( types, null, fn, selector );
- }
+ // Use same guid so caller can remove using origFn
+ fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
+ }
+ return this.each( function() {
+ jQuery.event.add( this, types, fn, data, selector );
+ });
+ },
+ one: function( types, selector, data, fn ) {
+ return this.on( types, selector, data, fn, 1 );
+ },
+ off: function( types, selector, fn ) {
+ var handleObj, type;
+ if ( types && types.preventDefault && types.handleObj ) {
+ // ( event ) dispatched jQuery.Event
+ handleObj = types.handleObj;
+ jQuery( types.delegateTarget ).off(
+ handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
+ handleObj.selector,
+ handleObj.handler
+ );
+ return this;
+ }
+ if ( typeof types === "object" ) {
+ // ( types-object [, selector] )
+ for ( type in types ) {
+ this.off( type, selector, types[ type ] );
+ }
+ return this;
+ }
+ if ( selector === false || typeof selector === "function" ) {
+ // ( types [, fn] )
+ fn = selector;
+ selector = undefined;
+ }
+ if ( fn === false ) {
+ fn = returnFalse;
+ }
+ return this.each(function() {
+ jQuery.event.remove( this, types, fn, selector );
+ });
},
trigger: function( type, data ) {
@@ -3537,1664 +5691,16 @@
jQuery.event.trigger( type, data, this );
});
},
-
triggerHandler: function( type, data ) {
- if ( this[0] ) {
- return jQuery.event.trigger( type, data, this[0], true );
- }
- },
-
- toggle: function( fn ) {
- // Save reference to arguments for access in closure
- var args = arguments,
- guid = fn.guid || jQuery.guid++,
- i = 0,
- toggler = function( event ) {
- // Figure out which function to execute
- var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i;
- jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 );
-
- // Make sure that clicks stop
- event.preventDefault();
-
- // and execute the function
- return args[ lastToggle ].apply( this, arguments ) || false;
- };
-
- // link all the functions, so any of them can unbind this click handler
- toggler.guid = guid;
- while ( i < args.length ) {
- args[ i++ ].guid = guid;
- }
-
- return this.click( toggler );
- },
-
- hover: function( fnOver, fnOut ) {
- return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
- }
-});
-
-var liveMap = {
- focus: "focusin",
- blur: "focusout",
- mouseenter: "mouseover",
- mouseleave: "mouseout"
-};
-
-jQuery.each(["live", "die"], function( i, name ) {
- jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) {
- var type, i = 0, match, namespaces, preType,
- selector = origSelector || this.selector,
- context = origSelector ? this : jQuery( this.context );
-
- if ( typeof types === "object" && !types.preventDefault ) {
- for ( var key in types ) {
- context[ name ]( key, data, types[key], selector );
- }
-
- return this;
- }
-
- if ( name === "die" && !types &&
- origSelector && origSelector.charAt(0) === "." ) {
-
- context.unbind( origSelector );
-
- return this;
- }
-
- if ( data === false || jQuery.isFunction( data ) ) {
- fn = data || returnFalse;
- data = undefined;
- }
-
- types = (types || "").split(" ");
-
- while ( (type = types[ i++ ]) != null ) {
- match = rnamespaces.exec( type );
- namespaces = "";
-
- if ( match ) {
- namespaces = match[0];
- type = type.replace( rnamespaces, "" );
- }
-
- if ( type === "hover" ) {
- types.push( "mouseenter" + namespaces, "mouseleave" + namespaces );
- continue;
- }
-
- preType = type;
-
- if ( liveMap[ type ] ) {
- types.push( liveMap[ type ] + namespaces );
- type = type + namespaces;
-
- } else {
- type = (liveMap[ type ] || type) + namespaces;
- }
-
- if ( name === "live" ) {
- // bind live handler
- for ( var j = 0, l = context.length; j < l; j++ ) {
- jQuery.event.add( context[j], "live." + liveConvert( type, selector ),
- { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } );
- }
-
- } else {
- // unbind live handler
- context.unbind( "live." + liveConvert( type, selector ), fn );
- }
- }
-
- return this;
- };
-});
-
-function liveHandler( event ) {
- var stop, maxLevel, related, match, handleObj, elem, j, i, l, data, close, namespace, ret,
- elems = [],
- selectors = [],
- events = jQuery._data( this, "events" );
-
- // Make sure we avoid non-left-click bubbling in Firefox (#3861) and disabled elements in IE (#6911)
- if ( event.liveFired === this || !events || !events.live || event.target.disabled || event.button && event.type === "click" ) {
- return;
- }
-
- if ( event.namespace ) {
- namespace = new RegExp("(^|\\.)" + event.namespace.split(".").join("\\.(?:.*\\.)?") + "(\\.|$)");
- }
-
- event.liveFired = this;
-
- var live = events.live.slice(0);
-
- for ( j = 0; j < live.length; j++ ) {
- handleObj = live[j];
-
- if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) {
- selectors.push( handleObj.selector );
-
- } else {
- live.splice( j--, 1 );
- }
- }
-
- match = jQuery( event.target ).closest( selectors, event.currentTarget );
-
- for ( i = 0, l = match.length; i < l; i++ ) {
- close = match[i];
-
- for ( j = 0; j < live.length; j++ ) {
- handleObj = live[j];
-
- if ( close.selector === handleObj.selector && (!namespace || namespace.test( handleObj.namespace )) && !close.elem.disabled ) {
- elem = close.elem;
- related = null;
-
- // Those two events require additional checking
- if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) {
- event.type = handleObj.preType;
- related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0];
-
- // Make sure not to accidentally match a child element with the same selector
- if ( related && jQuery.contains( elem, related ) ) {
- related = elem;
- }
- }
-
- if ( !related || related !== elem ) {
- elems.push({ elem: elem, handleObj: handleObj, level: close.level });
- }
- }
- }
- }
-
- for ( i = 0, l = elems.length; i < l; i++ ) {
- match = elems[i];
-
- if ( maxLevel && match.level > maxLevel ) {
- break;
- }
-
- event.currentTarget = match.elem;
- event.data = match.handleObj.data;
- event.handleObj = match.handleObj;
-
- ret = match.handleObj.origHandler.apply( match.elem, arguments );
-
- if ( ret === false || event.isPropagationStopped() ) {
- maxLevel = match.level;
-
- if ( ret === false ) {
- stop = false;
- }
- if ( event.isImmediatePropagationStopped() ) {
- break;
- }
- }
- }
-
- return stop;
-}
-
-function liveConvert( type, selector ) {
- return (type && type !== "*" ? type + "." : "") + selector.replace(rperiod, "`").replace(rspaces, "&");
-}
-
-jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
- "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
- "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
-
- // Handle event binding
- jQuery.fn[ name ] = function( data, fn ) {
- if ( fn == null ) {
- fn = data;
- data = null;
- }
-
- return arguments.length > 0 ?
- this.bind( name, data, fn ) :
- this.trigger( name );
- };
-
- if ( jQuery.attrFn ) {
- jQuery.attrFn[ name ] = true;
+ var elem = this[0];
+ if ( elem ) {
+ return jQuery.event.trigger( type, data, elem, true );
+ }
}
});
-
-
-
-/*!
- * Sizzle CSS Selector Engine
- * Copyright 2011, The Dojo Foundation
- * Released under the MIT, BSD, and GPL Licenses.
- * More information: http://sizzlejs.com/
- */
-(function(){
-
-var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
- done = 0,
- toString = Object.prototype.toString,
- hasDuplicate = false,
- baseHasDuplicate = true,
- rBackslash = /\\/g,
- rNonWord = /\W/;
-
-// Here we check if the JavaScript engine is using some sort of
-// optimization where it does not always call our comparision
-// function. If that is the case, discard the hasDuplicate value.
-// Thus far that includes Google Chrome.
-[0, 0].sort(function() {
- baseHasDuplicate = false;
- return 0;
-});
-
-var Sizzle = function( selector, context, results, seed ) {
- results = results || [];
- context = context || document;
-
- var origContext = context;
-
- if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
- return [];
- }
-
- if ( !selector || typeof selector !== "string" ) {
- return results;
- }
-
- var m, set, checkSet, extra, ret, cur, pop, i,
- prune = true,
- contextXML = Sizzle.isXML( context ),
- parts = [],
- soFar = selector;
-
- // Reset the position of the chunker regexp (start from head)
- do {
- chunker.exec( "" );
- m = chunker.exec( soFar );
-
- if ( m ) {
- soFar = m[3];
-
- parts.push( m[1] );
-
- if ( m[2] ) {
- extra = m[3];
- break;
- }
- }
- } while ( m );
-
- if ( parts.length > 1 && origPOS.exec( selector ) ) {
-
- if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
- set = posProcess( parts[0] + parts[1], context );
-
- } else {
- set = Expr.relative[ parts[0] ] ?
- [ context ] :
- Sizzle( parts.shift(), context );
-
- while ( parts.length ) {
- selector = parts.shift();
-
- if ( Expr.relative[ selector ] ) {
- selector += parts.shift();
- }
-
- set = posProcess( selector, set );
- }
- }
-
- } else {
- // Take a shortcut and set the context if the root selector is an ID
- // (but not if it'll be faster if the inner selector is an ID)
- if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
- Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
-
- ret = Sizzle.find( parts.shift(), context, contextXML );
- context = ret.expr ?
- Sizzle.filter( ret.expr, ret.set )[0] :
- ret.set[0];
- }
-
- if ( context ) {
- ret = seed ?
- { expr: parts.pop(), set: makeArray(seed) } :
- Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
-
- set = ret.expr ?
- Sizzle.filter( ret.expr, ret.set ) :
- ret.set;
-
- if ( parts.length > 0 ) {
- checkSet = makeArray( set );
-
- } else {
- prune = false;
- }
-
- while ( parts.length ) {
- cur = parts.pop();
- pop = cur;
-
- if ( !Expr.relative[ cur ] ) {
- cur = "";
- } else {
- pop = parts.pop();
- }
-
- if ( pop == null ) {
- pop = context;
- }
-
- Expr.relative[ cur ]( checkSet, pop, contextXML );
- }
-
- } else {
- checkSet = parts = [];
- }
- }
-
- if ( !checkSet ) {
- checkSet = set;
- }
-
- if ( !checkSet ) {
- Sizzle.error( cur || selector );
- }
-
- if ( toString.call(checkSet) === "[object Array]" ) {
- if ( !prune ) {
- results.push.apply( results, checkSet );
-
- } else if ( context && context.nodeType === 1 ) {
- for ( i = 0; checkSet[i] != null; i++ ) {
- if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {
- results.push( set[i] );
- }
- }
-
- } else {
- for ( i = 0; checkSet[i] != null; i++ ) {
- if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
- results.push( set[i] );
- }
- }
- }
-
- } else {
- makeArray( checkSet, results );
- }
-
- if ( extra ) {
- Sizzle( extra, origContext, results, seed );
- Sizzle.uniqueSort( results );
- }
-
- return results;
-};
-
-Sizzle.uniqueSort = function( results ) {
- if ( sortOrder ) {
- hasDuplicate = baseHasDuplicate;
- results.sort( sortOrder );
-
- if ( hasDuplicate ) {
- for ( var i = 1; i < results.length; i++ ) {
- if ( results[i] === results[ i - 1 ] ) {
- results.splice( i--, 1 );
- }
- }
- }
- }
-
- return results;
-};
-
-Sizzle.matches = function( expr, set ) {
- return Sizzle( expr, null, null, set );
-};
-
-Sizzle.matchesSelector = function( node, expr ) {
- return Sizzle( expr, null, null, [node] ).length > 0;
-};
-
-Sizzle.find = function( expr, context, isXML ) {
- var set;
-
- if ( !expr ) {
- return [];
- }
-
- for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
- var match,
- type = Expr.order[i];
-
- if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
- var left = match[1];
- match.splice( 1, 1 );
-
- if ( left.substr( left.length - 1 ) !== "\\" ) {
- match[1] = (match[1] || "").replace( rBackslash, "" );
- set = Expr.find[ type ]( match, context, isXML );
-
- if ( set != null ) {
- expr = expr.replace( Expr.match[ type ], "" );
- break;
- }
- }
- }
- }
-
- if ( !set ) {
- set = typeof context.getElementsByTagName !== "undefined" ?
- context.getElementsByTagName( "*" ) :
- [];
- }
-
- return { set: set, expr: expr };
-};
-
-Sizzle.filter = function( expr, set, inplace, not ) {
- var match, anyFound,
- old = expr,
- result = [],
- curLoop = set,
- isXMLFilter = set && set[0] && Sizzle.isXML( set[0] );
-
- while ( expr && set.length ) {
- for ( var type in Expr.filter ) {
- if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
- var found, item,
- filter = Expr.filter[ type ],
- left = match[1];
-
- anyFound = false;
-
- match.splice(1,1);
-
- if ( left.substr( left.length - 1 ) === "\\" ) {
- continue;
- }
-
- if ( curLoop === result ) {
- result = [];
- }
-
- if ( Expr.preFilter[ type ] ) {
- match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
-
- if ( !match ) {
- anyFound = found = true;
-
- } else if ( match === true ) {
- continue;
- }
- }
-
- if ( match ) {
- for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
- if ( item ) {
- found = filter( item, match, i, curLoop );
- var pass = not ^ !!found;
-
- if ( inplace && found != null ) {
- if ( pass ) {
- anyFound = true;
-
- } else {
- curLoop[i] = false;
- }
-
- } else if ( pass ) {
- result.push( item );
- anyFound = true;
- }
- }
- }
- }
-
- if ( found !== undefined ) {
- if ( !inplace ) {
- curLoop = result;
- }
-
- expr = expr.replace( Expr.match[ type ], "" );
-
- if ( !anyFound ) {
- return [];
- }
-
- break;
- }
- }
- }
-
- // Improper expression
- if ( expr === old ) {
- if ( anyFound == null ) {
- Sizzle.error( expr );
-
- } else {
- break;
- }
- }
-
- old = expr;
- }
-
- return curLoop;
-};
-
-Sizzle.error = function( msg ) {
- throw "Syntax error, unrecognized expression: " + msg;
-};
-
-var Expr = Sizzle.selectors = {
- order: [ "ID", "NAME", "TAG" ],
-
- match: {
- ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
- CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
- NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,
- ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,
- TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,
- CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,
- POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,
- PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
- },
-
- leftMatch: {},
-
- attrMap: {
- "class": "className",
- "for": "htmlFor"
- },
-
- attrHandle: {
- href: function( elem ) {
- return elem.getAttribute( "href" );
- },
- type: function( elem ) {
- return elem.getAttribute( "type" );
- }
- },
-
- relative: {
- "+": function(checkSet, part){
- var isPartStr = typeof part === "string",
- isTag = isPartStr && !rNonWord.test( part ),
- isPartStrNotTag = isPartStr && !isTag;
-
- if ( isTag ) {
- part = part.toLowerCase();
- }
-
- for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
- if ( (elem = checkSet[i]) ) {
- while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
-
- checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?
- elem || false :
- elem === part;
- }
- }
-
- if ( isPartStrNotTag ) {
- Sizzle.filter( part, checkSet, true );
- }
- },
-
- ">": function( checkSet, part ) {
- var elem,
- isPartStr = typeof part === "string",
- i = 0,
- l = checkSet.length;
-
- if ( isPartStr && !rNonWord.test( part ) ) {
- part = part.toLowerCase();
-
- for ( ; i < l; i++ ) {
- elem = checkSet[i];
-
- if ( elem ) {
- var parent = elem.parentNode;
- checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
- }
- }
-
- } else {
- for ( ; i < l; i++ ) {
- elem = checkSet[i];
-
- if ( elem ) {
- checkSet[i] = isPartStr ?
- elem.parentNode :
- elem.parentNode === part;
- }
- }
-
- if ( isPartStr ) {
- Sizzle.filter( part, checkSet, true );
- }
- }
- },
-
- "": function(checkSet, part, isXML){
- var nodeCheck,
- doneName = done++,
- checkFn = dirCheck;
-
- if ( typeof part === "string" && !rNonWord.test( part ) ) {
- part = part.toLowerCase();
- nodeCheck = part;
- checkFn = dirNodeCheck;
- }
-
- checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML );
- },
-
- "~": function( checkSet, part, isXML ) {
- var nodeCheck,
- doneName = done++,
- checkFn = dirCheck;
-
- if ( typeof part === "string" && !rNonWord.test( part ) ) {
- part = part.toLowerCase();
- nodeCheck = part;
- checkFn = dirNodeCheck;
- }
-
- checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML );
- }
- },
-
- find: {
- ID: function( match, context, isXML ) {
- if ( typeof context.getElementById !== "undefined" && !isXML ) {
- var m = context.getElementById(match[1]);
- // Check parentNode to catch when Blackberry 4.6 returns
- // nodes that are no longer in the document #6963
- return m && m.parentNode ? [m] : [];
- }
- },
-
- NAME: function( match, context ) {
- if ( typeof context.getElementsByName !== "undefined" ) {
- var ret = [],
- results = context.getElementsByName( match[1] );
-
- for ( var i = 0, l = results.length; i < l; i++ ) {
- if ( results[i].getAttribute("name") === match[1] ) {
- ret.push( results[i] );
- }
- }
-
- return ret.length === 0 ? null : ret;
- }
- },
-
- TAG: function( match, context ) {
- if ( typeof context.getElementsByTagName !== "undefined" ) {
- return context.getElementsByTagName( match[1] );
- }
- }
- },
- preFilter: {
- CLASS: function( match, curLoop, inplace, result, not, isXML ) {
- match = " " + match[1].replace( rBackslash, "" ) + " ";
-
- if ( isXML ) {
- return match;
- }
-
- for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
- if ( elem ) {
- if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) {
- if ( !inplace ) {
- result.push( elem );
- }
-
- } else if ( inplace ) {
- curLoop[i] = false;
- }
- }
- }
-
- return false;
- },
-
- ID: function( match ) {
- return match[1].replace( rBackslash, "" );
- },
-
- TAG: function( match, curLoop ) {
- return match[1].replace( rBackslash, "" ).toLowerCase();
- },
-
- CHILD: function( match ) {
- if ( match[1] === "nth" ) {
- if ( !match[2] ) {
- Sizzle.error( match[0] );
- }
-
- match[2] = match[2].replace(/^\+|\s*/g, '');
-
- // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
- var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec(
- match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
- !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
-
- // calculate the numbers (first)n+(last) including if they are negative
- match[2] = (test[1] + (test[2] || 1)) - 0;
- match[3] = test[3] - 0;
- }
- else if ( match[2] ) {
- Sizzle.error( match[0] );
- }
-
- // TODO: Move to normal caching system
- match[0] = done++;
-
- return match;
- },
-
- ATTR: function( match, curLoop, inplace, result, not, isXML ) {
- var name = match[1] = match[1].replace( rBackslash, "" );
-
- if ( !isXML && Expr.attrMap[name] ) {
- match[1] = Expr.attrMap[name];
- }
-
- // Handle if an un-quoted value was used
- match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" );
-
- if ( match[2] === "~=" ) {
- match[4] = " " + match[4] + " ";
- }
-
- return match;
- },
-
- PSEUDO: function( match, curLoop, inplace, result, not ) {
- if ( match[1] === "not" ) {
- // If we're dealing with a complex expression, or a simple one
- if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
- match[3] = Sizzle(match[3], null, null, curLoop);
-
- } else {
- var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
-
- if ( !inplace ) {
- result.push.apply( result, ret );
- }
-
- return false;
- }
-
- } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
- return true;
- }
-
- return match;
- },
-
- POS: function( match ) {
- match.unshift( true );
-
- return match;
- }
- },
-
- filters: {
- enabled: function( elem ) {
- return elem.disabled === false && elem.type !== "hidden";
- },
-
- disabled: function( elem ) {
- return elem.disabled === true;
- },
-
- checked: function( elem ) {
- return elem.checked === true;
- },
-
- selected: function( elem ) {
- // Accessing this property makes selected-by-default
- // options in Safari work properly
- if ( elem.parentNode ) {
- elem.parentNode.selectedIndex;
- }
-
- return elem.selected === true;
- },
-
- parent: function( elem ) {
- return !!elem.firstChild;
- },
-
- empty: function( elem ) {
- return !elem.firstChild;
- },
-
- has: function( elem, i, match ) {
- return !!Sizzle( match[3], elem ).length;
- },
-
- header: function( elem ) {
- return (/h\d/i).test( elem.nodeName );
- },
-
- text: function( elem ) {
- var attr = elem.getAttribute( "type" ), type = elem.type;
- // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
- // use getAttribute instead to test this case
- return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null );
- },
-
- radio: function( elem ) {
- return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type;
- },
-
- checkbox: function( elem ) {
- return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type;
- },
-
- file: function( elem ) {
- return elem.nodeName.toLowerCase() === "input" && "file" === elem.type;
- },
-
- password: function( elem ) {
- return elem.nodeName.toLowerCase() === "input" && "password" === elem.type;
- },
-
- submit: function( elem ) {
- var name = elem.nodeName.toLowerCase();
- return (name === "input" || name === "button") && "submit" === elem.type;
- },
-
- image: function( elem ) {
- return elem.nodeName.toLowerCase() === "input" && "image" === elem.type;
- },
-
- reset: function( elem ) {
- var name = elem.nodeName.toLowerCase();
- return (name === "input" || name === "button") && "reset" === elem.type;
- },
-
- button: function( elem ) {
- var name = elem.nodeName.toLowerCase();
- return name === "input" && "button" === elem.type || name === "button";
- },
-
- input: function( elem ) {
- return (/input|select|textarea|button/i).test( elem.nodeName );
- },
-
- focus: function( elem ) {
- return elem === elem.ownerDocument.activeElement;
- }
- },
- setFilters: {
- first: function( elem, i ) {
- return i === 0;
- },
-
- last: function( elem, i, match, array ) {
- return i === array.length - 1;
- },
-
- even: function( elem, i ) {
- return i % 2 === 0;
- },
-
- odd: function( elem, i ) {
- return i % 2 === 1;
- },
-
- lt: function( elem, i, match ) {
- return i < match[3] - 0;
- },
-
- gt: function( elem, i, match ) {
- return i > match[3] - 0;
- },
-
- nth: function( elem, i, match ) {
- return match[3] - 0 === i;
- },
-
- eq: function( elem, i, match ) {
- return match[3] - 0 === i;
- }
- },
- filter: {
- PSEUDO: function( elem, match, i, array ) {
- var name = match[1],
- filter = Expr.filters[ name ];
-
- if ( filter ) {
- return filter( elem, i, match, array );
-
- } else if ( name === "contains" ) {
- return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0;
-
- } else if ( name === "not" ) {
- var not = match[3];
-
- for ( var j = 0, l = not.length; j < l; j++ ) {
- if ( not[j] === elem ) {
- return false;
- }
- }
-
- return true;
-
- } else {
- Sizzle.error( name );
- }
- },
-
- CHILD: function( elem, match ) {
- var type = match[1],
- node = elem;
-
- switch ( type ) {
- case "only":
- case "first":
- while ( (node = node.previousSibling) ) {
- if ( node.nodeType === 1 ) {
- return false;
- }
- }
-
- if ( type === "first" ) {
- return true;
- }
-
- node = elem;
-
- case "last":
- while ( (node = node.nextSibling) ) {
- if ( node.nodeType === 1 ) {
- return false;
- }
- }
-
- return true;
-
- case "nth":
- var first = match[2],
- last = match[3];
-
- if ( first === 1 && last === 0 ) {
- return true;
- }
-
- var doneName = match[0],
- parent = elem.parentNode;
-
- if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
- var count = 0;
-
- for ( node = parent.firstChild; node; node = node.nextSibling ) {
- if ( node.nodeType === 1 ) {
- node.nodeIndex = ++count;
- }
- }
-
- parent.sizcache = doneName;
- }
-
- var diff = elem.nodeIndex - last;
-
- if ( first === 0 ) {
- return diff === 0;
-
- } else {
- return ( diff % first === 0 && diff / first >= 0 );
- }
- }
- },
-
- ID: function( elem, match ) {
- return elem.nodeType === 1 && elem.getAttribute("id") === match;
- },
-
- TAG: function( elem, match ) {
- return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match;
- },
-
- CLASS: function( elem, match ) {
- return (" " + (elem.className || elem.getAttribute("class")) + " ")
- .indexOf( match ) > -1;
- },
-
- ATTR: function( elem, match ) {
- var name = match[1],
- result = Expr.attrHandle[ name ] ?
- Expr.attrHandle[ name ]( elem ) :
- elem[ name ] != null ?
- elem[ name ] :
- elem.getAttribute( name ),
- value = result + "",
- type = match[2],
- check = match[4];
-
- return result == null ?
- type === "!=" :
- type === "=" ?
- value === check :
- type === "*=" ?
- value.indexOf(check) >= 0 :
- type === "~=" ?
- (" " + value + " ").indexOf(check) >= 0 :
- !check ?
- value && result !== false :
- type === "!=" ?
- value !== check :
- type === "^=" ?
- value.indexOf(check) === 0 :
- type === "$=" ?
- value.substr(value.length - check.length) === check :
- type === "|=" ?
- value === check || value.substr(0, check.length + 1) === check + "-" :
- false;
- },
-
- POS: function( elem, match, i, array ) {
- var name = match[2],
- filter = Expr.setFilters[ name ];
-
- if ( filter ) {
- return filter( elem, i, match, array );
- }
- }
- }
-};
-
-var origPOS = Expr.match.POS,
- fescape = function(all, num){
- return "\\" + (num - 0 + 1);
- };
-
-for ( var type in Expr.match ) {
- Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) );
- Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) );
-}
-
-var makeArray = function( array, results ) {
- array = Array.prototype.slice.call( array, 0 );
-
- if ( results ) {
- results.push.apply( results, array );
- return results;
- }
-
- return array;
-};
-
-// Perform a simple check to determine if the browser is capable of
-// converting a NodeList to an array using builtin methods.
-// Also verifies that the returned array holds DOM nodes
-// (which is not the case in the Blackberry browser)
-try {
- Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;
-
-// Provide a fallback method if it does not work
-} catch( e ) {
- makeArray = function( array, results ) {
- var i = 0,
- ret = results || [];
-
- if ( toString.call(array) === "[object Array]" ) {
- Array.prototype.push.apply( ret, array );
-
- } else {
- if ( typeof array.length === "number" ) {
- for ( var l = array.length; i < l; i++ ) {
- ret.push( array[i] );
- }
-
- } else {
- for ( ; array[i]; i++ ) {
- ret.push( array[i] );
- }
- }
- }
-
- return ret;
- };
-}
-
-var sortOrder, siblingCheck;
-
-if ( document.documentElement.compareDocumentPosition ) {
- sortOrder = function( a, b ) {
- if ( a === b ) {
- hasDuplicate = true;
- return 0;
- }
-
- if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
- return a.compareDocumentPosition ? -1 : 1;
- }
-
- return a.compareDocumentPosition(b) & 4 ? -1 : 1;
- };
-
-} else {
- sortOrder = function( a, b ) {
- // The nodes are identical, we can exit early
- if ( a === b ) {
- hasDuplicate = true;
- return 0;
-
- // Fallback to using sourceIndex (in IE) if it's available on both nodes
- } else if ( a.sourceIndex && b.sourceIndex ) {
- return a.sourceIndex - b.sourceIndex;
- }
-
- var al, bl,
- ap = [],
- bp = [],
- aup = a.parentNode,
- bup = b.parentNode,
- cur = aup;
-
- // If the nodes are siblings (or identical) we can do a quick check
- if ( aup === bup ) {
- return siblingCheck( a, b );
-
- // If no parents were found then the nodes are disconnected
- } else if ( !aup ) {
- return -1;
-
- } else if ( !bup ) {
- return 1;
- }
-
- // Otherwise they're somewhere else in the tree so we need
- // to build up a full list of the parentNodes for comparison
- while ( cur ) {
- ap.unshift( cur );
- cur = cur.parentNode;
- }
-
- cur = bup;
-
- while ( cur ) {
- bp.unshift( cur );
- cur = cur.parentNode;
- }
-
- al = ap.length;
- bl = bp.length;
-
- // Start walking down the tree looking for a discrepancy
- for ( var i = 0; i < al && i < bl; i++ ) {
- if ( ap[i] !== bp[i] ) {
- return siblingCheck( ap[i], bp[i] );
- }
- }
-
- // We ended someplace up the tree so do a sibling check
- return i === al ?
- siblingCheck( a, bp[i], -1 ) :
- siblingCheck( ap[i], b, 1 );
- };
-
- siblingCheck = function( a, b, ret ) {
- if ( a === b ) {
- return ret;
- }
-
- var cur = a.nextSibling;
-
- while ( cur ) {
- if ( cur === b ) {
- return -1;
- }
-
- cur = cur.nextSibling;
- }
-
- return 1;
- };
-}
-
-// Utility function for retreiving the text value of an array of DOM nodes
-Sizzle.getText = function( elems ) {
- var ret = "", elem;
-
- for ( var i = 0; elems[i]; i++ ) {
- elem = elems[i];
-
- // Get the text from text nodes and CDATA nodes
- if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
- ret += elem.nodeValue;
-
- // Traverse everything else, except comment nodes
- } else if ( elem.nodeType !== 8 ) {
- ret += Sizzle.getText( elem.childNodes );
- }
- }
-
- return ret;
-};
-
-// Check to see if the browser returns elements by name when
-// querying by getElementById (and provide a workaround)
-(function(){
- // We're going to inject a fake input element with a specified name
- var form = document.createElement("div"),
- id = "script" + (new Date()).getTime(),
- root = document.documentElement;
-
- form.innerHTML = " ";
-
- // Inject it into the root element, check its status, and remove it quickly
- root.insertBefore( form, root.firstChild );
-
- // The workaround has to do additional checks after a getElementById
- // Which slows things down for other browsers (hence the branching)
- if ( document.getElementById( id ) ) {
- Expr.find.ID = function( match, context, isXML ) {
- if ( typeof context.getElementById !== "undefined" && !isXML ) {
- var m = context.getElementById(match[1]);
-
- return m ?
- m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ?
- [m] :
- undefined :
- [];
- }
- };
-
- Expr.filter.ID = function( elem, match ) {
- var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
-
- return elem.nodeType === 1 && node && node.nodeValue === match;
- };
- }
-
- root.removeChild( form );
-
- // release memory in IE
- root = form = null;
-})();
-
-(function(){
- // Check to see if the browser returns only elements
- // when doing getElementsByTagName("*")
-
- // Create a fake element
- var div = document.createElement("div");
- div.appendChild( document.createComment("") );
-
- // Make sure no comments are found
- if ( div.getElementsByTagName("*").length > 0 ) {
- Expr.find.TAG = function( match, context ) {
- var results = context.getElementsByTagName( match[1] );
-
- // Filter out possible comments
- if ( match[1] === "*" ) {
- var tmp = [];
-
- for ( var i = 0; results[i]; i++ ) {
- if ( results[i].nodeType === 1 ) {
- tmp.push( results[i] );
- }
- }
-
- results = tmp;
- }
-
- return results;
- };
- }
-
- // Check to see if an attribute returns normalized href attributes
- div.innerHTML = " ";
-
- if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
- div.firstChild.getAttribute("href") !== "#" ) {
-
- Expr.attrHandle.href = function( elem ) {
- return elem.getAttribute( "href", 2 );
- };
- }
-
- // release memory in IE
- div = null;
-})();
-
-if ( document.querySelectorAll ) {
- (function(){
- var oldSizzle = Sizzle,
- div = document.createElement("div"),
- id = "__sizzle__";
-
- div.innerHTML = "
";
-
- // Safari can't handle uppercase or unicode characters when
- // in quirks mode.
- if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
- return;
- }
-
- Sizzle = function( query, context, extra, seed ) {
- context = context || document;
-
- // Only use querySelectorAll on non-XML documents
- // (ID selectors don't work in non-HTML documents)
- if ( !seed && !Sizzle.isXML(context) ) {
- // See if we find a selector to speed up
- var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query );
-
- if ( match && (context.nodeType === 1 || context.nodeType === 9) ) {
- // Speed-up: Sizzle("TAG")
- if ( match[1] ) {
- return makeArray( context.getElementsByTagName( query ), extra );
-
- // Speed-up: Sizzle(".CLASS")
- } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) {
- return makeArray( context.getElementsByClassName( match[2] ), extra );
- }
- }
-
- if ( context.nodeType === 9 ) {
- // Speed-up: Sizzle("body")
- // The body element only exists once, optimize finding it
- if ( query === "body" && context.body ) {
- return makeArray( [ context.body ], extra );
-
- // Speed-up: Sizzle("#ID")
- } else if ( match && match[3] ) {
- var elem = context.getElementById( match[3] );
-
- // Check parentNode to catch when Blackberry 4.6 returns
- // nodes that are no longer in the document #6963
- if ( elem && elem.parentNode ) {
- // Handle the case where IE and Opera return items
- // by name instead of ID
- if ( elem.id === match[3] ) {
- return makeArray( [ elem ], extra );
- }
-
- } else {
- return makeArray( [], extra );
- }
- }
-
- try {
- return makeArray( context.querySelectorAll(query), extra );
- } catch(qsaError) {}
-
- // qSA works strangely on Element-rooted queries
- // We can work around this by specifying an extra ID on the root
- // and working up from there (Thanks to Andrew Dupont for the technique)
- // IE 8 doesn't work on object elements
- } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
- var oldContext = context,
- old = context.getAttribute( "id" ),
- nid = old || id,
- hasParent = context.parentNode,
- relativeHierarchySelector = /^\s*[+~]/.test( query );
-
- if ( !old ) {
- context.setAttribute( "id", nid );
- } else {
- nid = nid.replace( /'/g, "\\$&" );
- }
- if ( relativeHierarchySelector && hasParent ) {
- context = context.parentNode;
- }
-
- try {
- if ( !relativeHierarchySelector || hasParent ) {
- return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra );
- }
-
- } catch(pseudoError) {
- } finally {
- if ( !old ) {
- oldContext.removeAttribute( "id" );
- }
- }
- }
- }
-
- return oldSizzle(query, context, extra, seed);
- };
-
- for ( var prop in oldSizzle ) {
- Sizzle[ prop ] = oldSizzle[ prop ];
- }
-
- // release memory in IE
- div = null;
- })();
-}
-
-(function(){
- var html = document.documentElement,
- matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector;
-
- if ( matches ) {
- // Check to see if it's possible to do matchesSelector
- // on a disconnected node (IE 9 fails this)
- var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ),
- pseudoWorks = false;
-
- try {
- // This should fail with an exception
- // Gecko does not error, returns false instead
- matches.call( document.documentElement, "[test!='']:sizzle" );
-
- } catch( pseudoError ) {
- pseudoWorks = true;
- }
-
- Sizzle.matchesSelector = function( node, expr ) {
- // Make sure that attribute selectors are quoted
- expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']");
-
- if ( !Sizzle.isXML( node ) ) {
- try {
- if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) {
- var ret = matches.call( node, expr );
-
- // IE 9's matchesSelector returns false on disconnected nodes
- if ( ret || !disconnectedMatch ||
- // As well, disconnected nodes are said to be in a document
- // fragment in IE 9, so check for that
- node.document && node.document.nodeType !== 11 ) {
- return ret;
- }
- }
- } catch(e) {}
- }
-
- return Sizzle(expr, null, null, [node]).length > 0;
- };
- }
-})();
-
-(function(){
- var div = document.createElement("div");
-
- div.innerHTML = "
";
-
- // Opera can't find a second classname (in 9.6)
- // Also, make sure that getElementsByClassName actually exists
- if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {
- return;
- }
-
- // Safari caches class attributes, doesn't catch changes (in 3.2)
- div.lastChild.className = "e";
-
- if ( div.getElementsByClassName("e").length === 1 ) {
- return;
- }
-
- Expr.order.splice(1, 0, "CLASS");
- Expr.find.CLASS = function( match, context, isXML ) {
- if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
- return context.getElementsByClassName(match[1]);
- }
- };
-
- // release memory in IE
- div = null;
-})();
-
-function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
- for ( var i = 0, l = checkSet.length; i < l; i++ ) {
- var elem = checkSet[i];
-
- if ( elem ) {
- var match = false;
-
- elem = elem[dir];
-
- while ( elem ) {
- if ( elem.sizcache === doneName ) {
- match = checkSet[elem.sizset];
- break;
- }
-
- if ( elem.nodeType === 1 && !isXML ){
- elem.sizcache = doneName;
- elem.sizset = i;
- }
-
- if ( elem.nodeName.toLowerCase() === cur ) {
- match = elem;
- break;
- }
-
- elem = elem[dir];
- }
-
- checkSet[i] = match;
- }
- }
-}
-
-function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
- for ( var i = 0, l = checkSet.length; i < l; i++ ) {
- var elem = checkSet[i];
-
- if ( elem ) {
- var match = false;
-
- elem = elem[dir];
-
- while ( elem ) {
- if ( elem.sizcache === doneName ) {
- match = checkSet[elem.sizset];
- break;
- }
-
- if ( elem.nodeType === 1 ) {
- if ( !isXML ) {
- elem.sizcache = doneName;
- elem.sizset = i;
- }
-
- if ( typeof cur !== "string" ) {
- if ( elem === cur ) {
- match = true;
- break;
- }
-
- } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
- match = elem;
- break;
- }
- }
-
- elem = elem[dir];
- }
-
- checkSet[i] = match;
- }
- }
-}
-
-if ( document.documentElement.contains ) {
- Sizzle.contains = function( a, b ) {
- return a !== b && (a.contains ? a.contains(b) : true);
- };
-
-} else if ( document.documentElement.compareDocumentPosition ) {
- Sizzle.contains = function( a, b ) {
- return !!(a.compareDocumentPosition(b) & 16);
- };
-
-} else {
- Sizzle.contains = function() {
- return false;
- };
-}
-
-Sizzle.isXML = function( elem ) {
- // documentElement is verified for cases where it doesn't yet exist
- // (such as loading iframes in IE - #4833)
- var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
-
- return documentElement ? documentElement.nodeName !== "HTML" : false;
-};
-
-var posProcess = function( selector, context ) {
- var match,
- tmpSet = [],
- later = "",
- root = context.nodeType ? [context] : context;
-
- // Position selectors must be done after the filter
- // And so must :not(positional) so we move all PSEUDOs to the end
- while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
- later += match[0];
- selector = selector.replace( Expr.match.PSEUDO, "" );
- }
-
- selector = Expr.relative[selector] ? selector + "*" : selector;
-
- for ( var i = 0, l = root.length; i < l; i++ ) {
- Sizzle( selector, root[i], tmpSet );
- }
-
- return Sizzle.filter( later, tmpSet );
-};
-
-// EXPOSE
-jQuery.find = Sizzle;
-jQuery.expr = Sizzle.selectors;
-jQuery.expr[":"] = jQuery.expr.filters;
-jQuery.unique = Sizzle.uniqueSort;
-jQuery.text = Sizzle.getText;
-jQuery.isXMLDoc = Sizzle.isXML;
-jQuery.contains = Sizzle.contains;
-
-
-})();
-
-
-var runtil = /Until$/,
- rparentsprev = /^(?:parents|prevUntil|prevAll)/,
- // Note: This RegExp should be improved, or likely pulled from Sizzle
- rmultiselector = /,/,
- isSimple = /^.[^:#\[\.,]*$/,
- slice = Array.prototype.slice,
- POS = jQuery.expr.match.POS,
+var isSimple = /^.[^:#\[\.,]*$/,
+ rparentsprev = /^(?:parents|prev(?:Until|All))/,
+ rneedsContext = jQuery.expr.match.needsContext,
// methods guaranteed to produce a unique set when starting from a unique set
guaranteedUnique = {
children: true,
@@ -5205,46 +5711,38 @@
jQuery.fn.extend({
find: function( selector ) {
- var self = this,
- i, l;
+ var i,
+ ret = [],
+ self = this,
+ len = self.length;
if ( typeof selector !== "string" ) {
- return jQuery( selector ).filter(function() {
- for ( i = 0, l = self.length; i < l; i++ ) {
+ return this.pushStack( jQuery( selector ).filter(function() {
+ for ( i = 0; i < len; i++ ) {
if ( jQuery.contains( self[ i ], this ) ) {
return true;
}
}
- });
- }
-
- var ret = this.pushStack( "", "find", selector ),
- length, n, r;
-
- for ( i = 0, l = this.length; i < l; i++ ) {
- length = ret.length;
- jQuery.find( selector, this[i], ret );
-
- if ( i > 0 ) {
- // Make sure that the results are unique
- for ( n = length; n < ret.length; n++ ) {
- for ( r = 0; r < length; r++ ) {
- if ( ret[r] === ret[n] ) {
- ret.splice(n--, 1);
- break;
- }
- }
- }
- }
- }
-
+ }) );
+ }
+
+ for ( i = 0; i < len; i++ ) {
+ jQuery.find( selector, self[ i ], ret );
+ }
+
+ // Needed because $( selector, context ) becomes $( context ).find( selector )
+ ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret );
+ ret.selector = this.selector ? this.selector + " " + selector : selector;
return ret;
},
has: function( target ) {
- var targets = jQuery( target );
+ var i,
+ targets = jQuery( target, this ),
+ len = targets.length;
+
return this.filter(function() {
- for ( var i = 0, l = targets.length; i < l; i++ ) {
+ for ( i = 0; i < len; i++ ) {
if ( jQuery.contains( this, targets[i] ) ) {
return true;
}
@@ -5253,81 +5751,52 @@
},
not: function( selector ) {
- return this.pushStack( winnow(this, selector, false), "not", selector);
+ return this.pushStack( winnow(this, selector || [], true) );
},
filter: function( selector ) {
- return this.pushStack( winnow(this, selector, true), "filter", selector );
+ return this.pushStack( winnow(this, selector || [], false) );
},
is: function( selector ) {
- return !!selector && ( typeof selector === "string" ?
- jQuery.filter( selector, this ).length > 0 :
- this.filter( selector ).length > 0 );
+ return !!winnow(
+ this,
+
+ // If this is a positional/relative selector, check membership in the returned set
+ // so $("p:first").is("p:last") won't return true for a doc with two "p".
+ typeof selector === "string" && rneedsContext.test( selector ) ?
+ jQuery( selector ) :
+ selector || [],
+ false
+ ).length;
},
closest: function( selectors, context ) {
- var ret = [], i, l, cur = this[0];
-
- // Array
- if ( jQuery.isArray( selectors ) ) {
- var match, selector,
- matches = {},
- level = 1;
-
- if ( cur && selectors.length ) {
- for ( i = 0, l = selectors.length; i < l; i++ ) {
- selector = selectors[i];
-
- if ( !matches[ selector ] ) {
- matches[ selector ] = POS.test( selector ) ?
- jQuery( selector, context || this.context ) :
- selector;
- }
- }
-
- while ( cur && cur.ownerDocument && cur !== context ) {
- for ( selector in matches ) {
- match = matches[ selector ];
-
- if ( match.jquery ? match.index( cur ) > -1 : jQuery( cur ).is( match ) ) {
- ret.push({ selector: selector, elem: cur, level: level });
- }
- }
-
- cur = cur.parentNode;
- level++;
- }
- }
-
- return ret;
- }
-
- // String
- var pos = POS.test( selectors ) || typeof selectors !== "string" ?
+ var cur,
+ i = 0,
+ l = this.length,
+ ret = [],
+ pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
jQuery( selectors, context || this.context ) :
0;
- for ( i = 0, l = this.length; i < l; i++ ) {
- cur = this[i];
-
- while ( cur ) {
- if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
- ret.push( cur );
+ for ( ; i < l; i++ ) {
+ for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) {
+ // Always skip document fragments
+ if ( cur.nodeType < 11 && (pos ?
+ pos.index(cur) > -1 :
+
+ // Don't pass non-elements to Sizzle
+ cur.nodeType === 1 &&
+ jQuery.find.matchesSelector(cur, selectors)) ) {
+
+ cur = ret.push( cur );
break;
-
- } else {
- cur = cur.parentNode;
- if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) {
- break;
- }
- }
- }
- }
-
- ret = ret.length > 1 ? jQuery.unique( ret ) : ret;
-
- return this.pushStack( ret, "closest", selectors );
+ }
+ }
+ }
+
+ return this.pushStack( ret.length > 1 ? jQuery.unique( ret ) : ret );
},
// Determine the position of an element within
@@ -5336,7 +5805,7 @@
// No argument, return index in parent
if ( !elem ) {
- return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1;
+ return ( this[0] && this[0].parentNode ) ? this.first().prevAll().length : -1;
}
// index in selector
@@ -5356,20 +5825,22 @@
jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),
all = jQuery.merge( this.get(), set );
- return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?
- all :
- jQuery.unique( all ) );
- },
-
- andSelf: function() {
- return this.add( this.prevObject );
+ return this.pushStack( jQuery.unique(all) );
+ },
+
+ addBack: function( selector ) {
+ return this.add( selector == null ?
+ this.prevObject : this.prevObject.filter(selector)
+ );
}
});
-// 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;
+function sibling( cur, dir ) {
+ do {
+ cur = cur[ dir ];
+ } while ( cur && cur.nodeType !== 1 );
+
+ return cur;
}
jQuery.each({
@@ -5384,10 +5855,10 @@
return jQuery.dir( elem, "parentNode", until );
},
next: function( elem ) {
- return jQuery.nth( elem, 2, "nextSibling" );
+ return sibling( elem, "nextSibling" );
},
prev: function( elem ) {
- return jQuery.nth( elem, 2, "previousSibling" );
+ return sibling( elem, "previousSibling" );
},
nextAll: function( elem ) {
return jQuery.dir( elem, "nextSibling" );
@@ -5402,7 +5873,7 @@
return jQuery.dir( elem, "previousSibling", until );
},
siblings: function( elem ) {
- return jQuery.sibling( elem.parentNode.firstChild, elem );
+ return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
},
children: function( elem ) {
return jQuery.sibling( elem.firstChild );
@@ -5410,18 +5881,13 @@
contents: function( elem ) {
return jQuery.nodeName( elem, "iframe" ) ?
elem.contentDocument || elem.contentWindow.document :
- jQuery.makeArray( elem.childNodes );
+ jQuery.merge( [], elem.childNodes );
}
}, function( name, fn ) {
jQuery.fn[ name ] = function( until, selector ) {
- var ret = jQuery.map( this, fn, until ),
- // The variable 'args' was introduced in
- // https://github.com/jquery/jquery/commit/52a0238
- // to work around a bug in Chrome 10 (Dev) and should be removed when the bug is fixed.
- // http://code.google.com/p/v8/issues/detail?id=1050
- args = slice.call(arguments);
-
- if ( !runtil.test( name ) ) {
+ var ret = jQuery.map( this, fn, until );
+
+ if ( name.slice( -5 ) !== "Until" ) {
selector = until;
}
@@ -5429,25 +5895,35 @@
ret = jQuery.filter( selector, ret );
}
- ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret;
-
- if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {
- ret = ret.reverse();
- }
-
- return this.pushStack( ret, name, args.join(",") );
+ if ( this.length > 1 ) {
+ // Remove duplicates
+ if ( !guaranteedUnique[ name ] ) {
+ ret = jQuery.unique( ret );
+ }
+
+ // Reverse order for parents* and prev-derivatives
+ if ( rparentsprev.test( name ) ) {
+ ret = ret.reverse();
+ }
+ }
+
+ return this.pushStack( ret );
};
});
jQuery.extend({
filter: function( expr, elems, not ) {
+ var elem = elems[ 0 ];
+
if ( not ) {
expr = ":not(" + expr + ")";
}
- return elems.length === 1 ?
- jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :
- jQuery.find.matches(expr, elems);
+ return elems.length === 1 && elem.nodeType === 1 ?
+ jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] :
+ jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
+ return elem.nodeType === 1;
+ }));
},
dir: function( elem, dir, until ) {
@@ -5463,19 +5939,6 @@
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 = [];
@@ -5490,91 +5953,779 @@
});
// Implement the identical functionality for filter and not
-function winnow( elements, qualifier, keep ) {
-
- // Can't pass null or undefined to indexOf in Firefox 4
- // Set to 0 to skip string check
- qualifier = qualifier || 0;
-
+function winnow( elements, qualifier, not ) {
if ( jQuery.isFunction( qualifier ) ) {
- return jQuery.grep(elements, function( elem, i ) {
- var retVal = !!qualifier.call( elem, i, elem );
- return retVal === keep;
+ return jQuery.grep( elements, function( elem, i ) {
+ /* jshint -W018 */
+ return !!qualifier.call( elem, i, elem ) !== not;
});
- } else if ( qualifier.nodeType ) {
- return jQuery.grep(elements, function( elem, i ) {
- return (elem === qualifier) === keep;
+ }
+
+ if ( qualifier.nodeType ) {
+ return jQuery.grep( elements, function( elem ) {
+ return ( elem === qualifier ) !== not;
});
- } else if ( typeof qualifier === "string" ) {
- var filtered = jQuery.grep(elements, function( elem ) {
- return elem.nodeType === 1;
- });
-
+ }
+
+ if ( typeof qualifier === "string" ) {
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;
+ return jQuery.filter( qualifier, elements, not );
+ }
+
+ qualifier = jQuery.filter( qualifier, elements );
+ }
+
+ return jQuery.grep( elements, function( elem ) {
+ return ( jQuery.inArray( elem, qualifier ) >= 0 ) !== not;
});
}
-
-
-
-
-var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
+function createSafeFragment( document ) {
+ var list = nodeNames.split( "|" ),
+ safeFrag = document.createDocumentFragment();
+
+ if ( safeFrag.createElement ) {
+ while ( list.length ) {
+ safeFrag.createElement(
+ list.pop()
+ );
+ }
+ }
+ return safeFrag;
+}
+
+var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" +
+ "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
+ rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g,
+ rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"),
rleadingWhitespace = /^\s+/,
- rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,
+ rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
rtagName = /<([\w:]+)/,
rtbody = /\s*$/g,
+
+ // We have to close these tags to support XHTML (#13200)
wrapMap = {
option: [ 1, "", " " ],
legend: [ 1, "", " " ],
+ area: [ 1, "", " " ],
+ param: [ 1, "", " " ],
thead: [ 1, "" ],
tr: [ 2, "" ],
+ col: [ 2, "" ],
td: [ 3, "" ],
- col: [ 2, "" ],
- area: [ 1, "", " " ],
- _default: [ 0, "", "" ]
- };
+
+ // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags,
+ // unless wrapped in a div with non-breaking characters in front of it.
+ _default: jQuery.support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X", "
" ]
+ },
+ safeFragment = createSafeFragment( document ),
+ fragmentDiv = safeFragment.appendChild( document.createElement("div") );
wrapMap.optgroup = wrapMap.option;
wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
wrapMap.th = wrapMap.td;
-// IE can't serialize and