--- /dev/null
--- /dev/null
++## See online installation and setup documentation at
++
++ http://ceph.com/docs/master/install/manual-deployment/
++
++-------- -------- --------
++
++## "systemd" requires manual activation of services:
++
++ ## MON
++ # systemctl start ceph-mon
++ # systemctl enable ceph-mon
++
++ ## OSD.0 (set other OSDs like this)
++ # systemctl start ceph-osd@0
++ # systemctl enable ceph-osd@0
++
++ ## MDS
++ # systemctl start ceph-mds
++ # systemctl enable ceph-mds
++
++ ## "ceph" meta-service (starts/stops all the above like old init script)
++ # systemctl start ceph
++ # systemctl enable ceph
++
++ The ceph cluster can be set in the "/etc/default/ceph" file
++ by setting the CLUSTER environment variable.
++
++-------- -------- --------
++
++## Upgrade procedure (0.72.2 to 0.80):
++
++ * Read "Upgrade Sequencing" in release notes:
++
++ http://ceph.com/docs/firefly/release-notes/
++
++ * Upgrade packages.
++
++ * Restart MONs.
++
++ * Restart all OSDs.
++
++ * Run `ceph osd crush tunables default`.
++
++ * (Restart MDSes).
++
++ * Consider setting the 'hashpspool' flag on your pools (new default):
++
++ ceph osd pool set {pool} hashpspool true
++
++ This changes the pool to use a new hashing algorithm for the distribution of
++ Placement Groups (PGs) to OSDs. This new algorithm ensures a better distribution
++ to all OSDs. Be aware that this change will temporarily put some of your PGs into
++ "misplaced" state and cause additional I/O until all PGs are moved to their new
++ location. See http://tracker.ceph.com/issues/4128 for the details about the new
++ algorithm.
++
++ Read more about tunables in
++
++ http://ceph.com/docs/master/rados/operations/crush-map/#tunables
++
++ Upgrading all OSDs and setting correct tunables is necessary to avoid the errors like:
++
++ ## rbdmap errors:
++ libceph: mon2 192.168.0.222:6789 socket error on read
++
++ Wrong tunables may produce the following error:
++
++ libceph: mon0 192.168.0.222:6789 socket error on read
++ libceph: mon2 192.168.0.250:6789 feature set mismatch, my 4a042a42 < server's 2004a042a42, missing 20000000000
++
++ ## MDS errors:
++ one or more OSDs do not support TMAP2OMAP; upgrade OSDs before starting MDS (or downgrade MDS)
++
++ See also:
++
++ http://ceph.com/docs/firefly/install/upgrading-ceph/
++
++-------- -------- --------
++
++ Jerasure pool(s) will bump requirements to Linux_3.15 (not yet released) for
++ kernel CephFS and RBD clients.
++
++-------- -------- --------
++
++ RBD kernel driver do not support authentication so the following setting
++ in "/etc/ceph/ceph.conf" may be used to relax client auth. requirements:
++
++ cephx cluster require signatures = true
++ cephx service require signatures = false
++
++-------- -------- --------
++
++> How to mount CephFS using fuse client from "/etc/fstab"?
++
++ Add (and modify) the following sample to "/etc/fstab":
++
++ mount.fuse.ceph#conf=/etc/ceph/ceph.conf,id=admin /mnt/ceph fuse _netdev,noatime,allow_other 0 0
++
++ This is equivalent of running
++
++ ceph-fuse /mnt/ceph --id=admin -o noatime,allow_other
++
++ as root.
++
++-------- -------- --------
++
++ To avoid known issue with kernel FS client it is recommended to use
++ 'readdir_max_entries' mount option, for example:
++
++ mount -t ceph 1.2.3.4:/ /mnt/ceph -o readdir_max_entries=64
++
++-------- -------- --------
++
++ Beware of "mlocate" scanning of OSD file systems. To avoid problems add
++ "/var/lib/ceph" to PRUNEPATHS in the "/etc/updatedb.conf" like in the
++ following example:
++
++ PRUNEPATHS="/tmp /var/spool /media /mnt /var/lib/ceph"
++
++-------- -------- --------
--- /dev/null
- var/lib/ceph/bootstrap-rbd
+var/lib/ceph/bootstrap-mds
+var/lib/ceph/bootstrap-mgr
+var/lib/ceph/bootstrap-osd
+var/lib/ceph/bootstrap-rgw
+var/lib/ceph/tmp
--- /dev/null
- usr/bin/ceph-kvstore-tool
++## install from source tree
++debian/etc/pm/sleep.d/* /etc/pm/sleep.d/
++etc/bash_completion.d/ceph /usr/share/bash-completion/completions/
+etc/init.d/ceph
+usr/bin/ceph-debugpack
+usr/bin/ceph-detect-init
++usr/bin/ceph-kvstore-tool
+usr/bin/ceph-run
+usr/bin/crushtool
+usr/bin/monmaptool
+usr/bin/osdmaptool
- usr/lib/ceph/erasure-code/*
++usr/lib/*/ceph/erasure-code/*
++usr/lib/*/rados-classes/*
+usr/lib/ceph/ceph_common.sh
- usr/lib/rados-classes/*
+usr/lib/python*/dist-packages/ceph_detect_init*
- usr/share/man/man8/ceph-deploy.8
+usr/sbin/ceph-create-keys
+usr/share/doc/ceph/sample.ceph.conf
+usr/share/man/man8/ceph-create-keys.8
+usr/share/man/man8/ceph-debugpack.8
- usr/share/man/man8/ceph-kvstore-tool.8
+usr/share/man/man8/ceph-detect-init.8
++usr/share/man/man8/ceph-kvstore-tool.8
+usr/share/man/man8/ceph-run.8
+usr/share/man/man8/crushtool.8
+usr/share/man/man8/monmaptool.8
+usr/share/man/man8/osdmaptool.8
--- /dev/null
- configure)
- rm -f /etc/init/ceph.conf
- [ -x /sbin/start ] && start ceph-all || :
-
- # adjust file and directory permissions
- for DIR in /var/lib/ceph/* ; do
- if ! dpkg-statoverride --list $DIR >/dev/null
- then
- chown $SERVER_USER:$SERVER_GROUP $DIR
- fi
- done
- ;;
- abort-upgrade|abort-remove|abort-deconfigure)
- :
- ;;
-
- *)
- echo "postinst called with unknown argument \`$1'" >&2
- exit 1
- ;;
+#!/bin/sh
+# vim: set noet ts=8:
+# postinst script for ceph
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+#
+# postinst configure <most-recently-configured-version>
+# old-postinst abort-upgrade <new-version>
+# conflictor's-postinst abort-remove in-favour <package> <new-version>
+# postinst abort-remove
+# deconfigured's-postinst abort-deconfigure in-favour <failed-install-package> <version> [<removing conflicting-package> <version>]
+#
+# The current action is to simply remove the mistakenly-added
+# /etc/init/ceph.conf file; this could be done in any of these cases,
+# although technically it will leave the system in a different state
+# than the original install that included that file. So instead we
+# only remove on "configure", since that's the only time we know we're
+# successful in installing a newer package than the erroneous version.
+
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+[ -f "/etc/default/ceph" ] && . /etc/default/ceph
+[ -z "$SERVER_USER" ] && SERVER_USER=ceph
+[ -z "$SERVER_GROUP" ] && SERVER_GROUP=ceph
+
+case "$1" in
-
-
++ configure)
++ rm -f /etc/init/ceph.conf
++ for DIR in `ls -1 /var/lib/ceph` ; do
++ if ! dpkg-statoverride --list /var/lib/ceph/$DIR >/dev/null; then
++ if [ -d /run/systemd/system ] && [ $DIR = 'mon' ]; then
++ # NOTE: upgrade file permissions for mon filesystem on
++ # systemd based installs only due to automatic
++ # restarting of ceph-mon daemon
++ chown -R $SERVER_USER:$SERVER_GROUP /var/lib/ceph/$DIR
++ else
++ chown $SERVER_USER:$SERVER_GROUP /var/lib/ceph/$DIR
++ fi
++ fi
++ done
++ ;;
++ abort-upgrade|abort-remove|abort-deconfigure)
++ :
++ ;;
++ *)
++ echo "postinst called with unknown argument \`$1'" >&2
++ exit 1
++ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
--- /dev/null
- remove)
- [ -x /sbin/stop ] && stop ceph-all || true
- invoke-rc.d ceph stop || {
- RESULT=$?
- if [ $RESULT != 100 ]; then
- exit $RESULT
- fi
- }
+#!/bin/sh
+# vim: set noet ts=8:
+
+set -e
+
+case "$1" in
-
- *)
++ remove)
++ invoke-rc.d ceph stop || {
++ RESULT=$?
++ if [ $RESULT != 100 ]; then
++ exit $RESULT
++ fi
++ }
+ ;;
++ *)
+ ;;
+esac
+
+#DEBHELPER#
+
+exit 0
--- /dev/null
- #! /usr/bin/dh-exec --with=install
-
- etc/bash_completion.d/ceph
- etc/bash_completion.d/rados
- etc/bash_completion.d/radosgw-admin
- etc/bash_completion.d/rbd
++#!/usr/bin/dh-exec --with=install
++etc/bash_completion.d/rados /usr/share/bash-completion/completions/
++etc/bash_completion.d/radosgw-admin /usr/share/bash-completion/completions/
++etc/bash_completion.d/rbd /usr/share/bash-completion/completions/
++src/etc-rbdmap => etc/ceph/rbdmap
++etc/default/ceph
++udev/50-rbd.rules /lib/udev/rules.d
+usr/bin/ceph
+usr/bin/ceph-authtool
++usr/bin/ceph-brag
+usr/bin/ceph-conf
++usr/bin/ceph-crush-location
+usr/bin/ceph-dencoder
++usr/bin/ceph-post-file
+usr/bin/ceph-rbdnamer
+usr/bin/ceph-syn
- usr/bin/ceph-crush-location
- usr/bin/cephfs-data-scan
- usr/bin/cephfs-journal-tool
- usr/bin/cephfs-table-tool
+usr/bin/rados
+usr/bin/radosgw-admin
+usr/bin/rbd
- usr/bin/rbdmap
+usr/bin/rbd-replay*
- usr/bin/ceph-post-file
- usr/bin/ceph-brag
++usr/bin/rbdmap
++usr/lib/*/ceph/compressor/*
++usr/lib/*/ceph/crypto/* [amd64]
++usr/lib/python*/dist-packages/ceph_argparse.py
++usr/lib/python*/dist-packages/ceph_daemon.py
+usr/sbin/mount.ceph sbin
- usr/lib/ceph/compressor/*
- usr/lib/ceph/crypto/* [amd64]
++usr/share/ceph/id_rsa_drop.ceph.com
++usr/share/ceph/id_rsa_drop.ceph.com.pub
++usr/share/ceph/known_hosts_drop.ceph.com
+usr/share/man/man8/ceph-authtool.8
+usr/share/man/man8/ceph-conf.8
+usr/share/man/man8/ceph-dencoder.8
++usr/share/man/man8/ceph-post-file.8
+usr/share/man/man8/ceph-rbdnamer.8
+usr/share/man/man8/ceph-syn.8
- usr/share/man/man8/ceph-post-file.8
+usr/share/man/man8/ceph.8
+usr/share/man/man8/mount.ceph.8
+usr/share/man/man8/rados.8
+usr/share/man/man8/radosgw-admin.8
++usr/share/man/man8/rbd-replay*.8
+usr/share/man/man8/rbd.8
+usr/share/man/man8/rbdmap.8
- usr/share/man/man8/rbd-replay*.8
- usr/share/ceph/known_hosts_drop.ceph.com
- usr/share/ceph/id_rsa_drop.ceph.com
- usr/share/ceph/id_rsa_drop.ceph.com.pub
- etc/ceph/rbdmap
- lib/udev/rules.d/50-rbd.rules
- usr/lib/python*/dist-packages/ceph_argparse.py*
- usr/lib/python*/dist-packages/ceph_daemon.py*
--- /dev/null
--- /dev/null
++# this is fixed just a few lines below in the configure script
++# it ensures that also existing user get the correct home directory
++maintainer-script-should-not-use-adduser-system-without-home
--- /dev/null
--- /dev/null
++../src/logrotate.conf
--- /dev/null
--- /dev/null
++rm_conffile /etc/bash_completion.d/rados 10.2.5-1~
++rm_conffile /etc/bash_completion.d/rbd 10.2.5-1~
--- /dev/null
--- /dev/null
++debian/man/ceph-crush-location.1
--- /dev/null
- # postinst script for ceph-mds
+#!/bin/sh
+# vim: set noet ts=8:
- echo -n "Adding group $SERVER_GROUP.."
++# postinst script for ceph-common
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+#
+# postinst configure <most-recently-configured-version>
+# old-postinst abort-upgrade <new-version>
+# conflictor's-postinst abort-remove in-favour <package> <new-version>
+# postinst abort-remove
+# deconfigured's-postinst abort-deconfigure in-favour <failed-install-package> <version> [<removing conflicting-package> <version>]
+#
+
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+# Let the admin override these distro-specified defaults. This is NOT
+# recommended!
+[ -f "/etc/default/ceph" ] && . /etc/default/ceph
+
+[ -z "$SERVER_HOME" ] && SERVER_HOME=/var/lib/ceph
+[ -z "$SERVER_USER" ] && SERVER_USER=ceph
+[ -z "$SERVER_NAME" ] && SERVER_NAME="Ceph storage service"
+[ -z "$SERVER_GROUP" ] && SERVER_GROUP=ceph
+[ -z "$SERVER_UID" ] && SERVER_UID=64045 # alloc by Debian base-passwd maintainer
+[ -z "$SERVER_GID" ] && SERVER_GID=$SERVER_UID
+
+
+# Groups that the user will be added to, if undefined, then none.
+[ -z "$SERVER_ADDGROUP" ] && SERVER_ADDGROUP=
+
+case "$1" in
+ configure)
+ # create user to avoid running server as root
+ # 1. create group if not existing
+ if ! getent group | grep -q "^$SERVER_GROUP:" ; then
- echo "..done"
+ addgroup --quiet --system --gid $SERVER_GID \
+ $SERVER_GROUP 2>/dev/null ||true
- echo -n "Adding system user $SERVER_USER.."
+ fi
+ # 2. create user if not existing
+ if ! getent passwd | grep -q "^$SERVER_USER:"; then
- echo "..done"
+ adduser --quiet \
+ --system \
+ --no-create-home \
+ --disabled-password \
+ --uid $SERVER_UID \
+ --gid $SERVER_GID \
+ $SERVER_USER 2>/dev/null || true
- echo -n "Setting system user $SERVER_USER properties.."
+ fi
+ # 3. adjust passwd entry
- $SERVER_USER
- echo "..done"
+ usermod -c "$SERVER_NAME" \
+ -d $SERVER_HOME \
+ -g $SERVER_GROUP \
- echo -n "Fixing /var/run/ceph ownership.."
++ $SERVER_USER 2>/dev/null
+
+ # 5. adjust file and directory permissions
+ if ! dpkg-statoverride --list $SERVER_HOME >/dev/null
+ then
+ chown $SERVER_USER:$SERVER_GROUP $SERVER_HOME
+ chmod u=rwx,g=rx,o= $SERVER_HOME
+ fi
+ if ! dpkg-statoverride --list /var/log/ceph >/dev/null
+ then
+ chown -R $SERVER_USER:$SERVER_GROUP /var/log/ceph
+ # members of group ceph can log here, but cannot remove
+ # others' files. non-members cannot read any logs.
+ chmod u=rwx,g=rwxs,o=t /var/log/ceph
+ fi
+
+ # 6. fix /var/run/ceph
+ if [ -d /var/run/ceph ]; then
- echo "..done"
+ chown $SERVER_USER:$SERVER_GROUP /var/run/ceph
- [ -x /bin/systemd-tmpfiles ] && systemd-tmpfiles --create || true
+ fi
+
+ # create /run/ceph. fail softly if systemd isn't present or
+ # something.
++ which systemd-tmpfiles > /dev/null && systemd-tmpfiles --create || true
+ ;;
+ abort-upgrade|abort-remove|abort-deconfigure)
+ :
+ ;;
+
+ *)
+ echo "postinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
--- /dev/null
- rm -rf /var/log/ceph
+#!/bin/sh
+# postrm script for ceph-common
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <postrm> `remove'
+# * <postrm> `purge'
+# * <old-postrm> `upgrade' <new-version>
+# * <new-postrm> `failed-upgrade' <old-version>
+# * <new-postrm> `abort-install'
+# * <new-postrm> `abort-install' <old-version>
+# * <new-postrm> `abort-upgrade' <old-version>
+# * <disappearer's-postrm> `disappear' <overwriter>
+# <overwriter-version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+ remove)
+ ;;
+
+ purge)
++ rm -rf /var/log/ceph
+ rm -rf /etc/ceph
+ ;;
+
+ upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
+ ;;
+
+ *)
+ echo "postrm called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
+
--- /dev/null
--- /dev/null
++../src/init-rbdmap
--- /dev/null
--- /dev/null
++debian/man/mount.fuse.ceph.8
--- /dev/null
- var/lib/ceph/mds
++/var/lib/ceph/mds
--- /dev/null
+usr/bin/ceph-mds
++usr/bin/cephfs-data-scan
++usr/bin/cephfs-journal-tool
++usr/bin/cephfs-table-tool
+usr/share/man/man8/ceph-mds.8
--- /dev/null
--- /dev/null
++# Ceph service files define their own targets which are then WantedBy
++# multi-user.target
++systemd-service-file-refers-to-unusual-wantedby-target lib/systemd/system/ceph-mds@.service ceph-mds.target
--- /dev/null
--- /dev/null
++rm_conffile /etc/init/ceph-mds-all-starter.conf 10.2.5-1~
++rm_conffile /etc/init/ceph-mds-all.conf 10.2.5-1~
++rm_conffile /etc/init/ceph-mds.conf 10.2.5-1~
--- /dev/null
- configure)
- [ -x /sbin/start ] && start ceph-mds-all || :
-
- if ! dpkg-statoverride --list /var/lib/ceph/mds >/dev/null
- then
- chown $SERVER_USER:$SERVER_GROUP /var/lib/ceph/mds
- fi
- ;;
- abort-upgrade|abort-remove|abort-deconfigure)
- :
- ;;
-
- *)
- echo "postinst called with unknown argument \`$1'" >&2
- exit 1
- ;;
+#!/bin/sh
+# vim: set noet ts=8:
+# postinst script for ceph-mds
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+#
+# postinst configure <most-recently-configured-version>
+# old-postinst abort-upgrade <new-version>
+# conflictor's-postinst abort-remove in-favour <package> <new-version>
+# postinst abort-remove
+# deconfigured's-postinst abort-deconfigure in-favour <failed-install-package> <version> [<removing conflicting-package> <version>]
+#
+
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+[ -f "/etc/default/ceph" ] && . /etc/default/ceph
+[ -z "$SERVER_USER" ] && SERVER_USER=ceph
+[ -z "$SERVER_GROUP" ] && SERVER_GROUP=ceph
+
+case "$1" in
-
-
++ configure)
++ if ! dpkg-statoverride --list /var/lib/ceph/mds >/dev/null; then
++ chown $SERVER_USER:$SERVER_GROUP /var/lib/ceph/mds
++ fi
++ ;;
++ abort-upgrade|abort-remove|abort-deconfigure)
++ :
++ ;;
++ *)
++ echo "postinst called with unknown argument \`$1'" >&2
++ exit 1
++ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
--- /dev/null
- remove)
- [ -x /sbin/stop ] && stop ceph-mds-all || :
- invoke-rc.d ceph stop mds || {
- RESULT=$?
- if [ $RESULT != 100 ]; then
- exit $RESULT
- fi
- }
+#!/bin/sh
+# vim: set noet ts=8:
+
+set -e
+
+case "$1" in
-
- *)
++ remove)
++ invoke-rc.d ceph stop mds || {
++ RESULT=$?
++ if [ $RESULT != 100 ]; then
++ exit $RESULT
++ fi
++ }
+ ;;
++ *)
+ ;;
+esac
+
+#DEBHELPER#
+
+exit 0
--- /dev/null
- usr/lib/ceph/mgr
+usr/bin/ceph-mgr
++usr/lib/*/ceph/mgr
--- /dev/null
- [ -x /sbin/start ] && start ceph-mgr-all || :
+#!/bin/sh
+# vim: set noet ts=8:
+# postinst script for ceph-mgr
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+#
+# postinst configure <most-recently-configured-version>
+# old-postinst abort-upgrade <new-version>
+# conflictor's-postinst abort-remove in-favour <package> <new-version>
+# postinst abort-remove
+# deconfigured's-postinst abort-deconfigure in-favour <failed-install-package> <version> [<removing conflicting-package> <version>]
+#
+
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+[ -f "/etc/default/ceph" ] && . /etc/default/ceph
+[ -z "$SERVER_USER" ] && SERVER_USER=ceph
+[ -z "$SERVER_GROUP" ] && SERVER_GROUP=ceph
+
+case "$1" in
+ configure)
+
+ if ! dpkg-statoverride --list /var/lib/ceph/mgr >/dev/null
+ then
+ chown $SERVER_USER:$SERVER_GROUP /var/lib/ceph/mgr
+ fi
+ ;;
+ abort-upgrade|abort-remove|abort-deconfigure)
+ :
+ ;;
+
+ *)
+ echo "postinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
+
--- /dev/null
- [ -x /sbin/stop ] && stop ceph-mgr-all || :
+#!/bin/sh
+# vim: set noet ts=8:
+
+set -e
+
+case "$1" in
+ remove)
+ invoke-rc.d ceph stop mgr || {
+ RESULT=$?
+ if [ $RESULT != 100 ]; then
+ exit $RESULT
+ fi
+ }
+ ;;
+
+ *)
+ ;;
+esac
+
+#DEBHELPER#
+
+exit 0
--- /dev/null
- usr/lib/python*/dist-packages/ceph_rest_api.py*
+usr/bin/ceph-mon
+usr/bin/ceph-monstore-tool
+usr/bin/ceph-rest-api
++usr/lib/python*/dist-packages/ceph_rest_api.py
+usr/share/man/man8/ceph-mon.8
+usr/share/man/man8/ceph-rest-api.8
--- /dev/null
--- /dev/null
++# Ceph service files define their own targets which are then WantedBy
++# multi-user.target
++systemd-service-file-refers-to-unusual-wantedby-target lib/systemd/system/ceph-mon@.service ceph-mon.target
--- /dev/null
- configure)
- [ -x /sbin/start ] && start ceph-mon-all || :
- ;;
- abort-upgrade|abort-remove|abort-deconfigure)
++#!/bin/sh
+# vim: set noet ts=8:
+# postinst script for ceph-mon
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+#
+# postinst configure <most-recently-configured-version>
+# old-postinst abort-upgrade <new-version>
+# conflictor's-postinst abort-remove in-favour <package> <new-version>
+# postinst abort-remove
+# deconfigured's-postinst abort-deconfigure in-favour <failed-install-package> <version> [<removing conflicting-package> <version>]
+#
+
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+[ -f "/etc/default/ceph" ] && . /etc/default/ceph
+[ -z "$SERVER_USER" ] && SERVER_USER=ceph
+[ -z "$SERVER_GROUP" ] && SERVER_GROUP=ceph
+
+case "$1" in
++ configure|abort-upgrade|abort-remove|abort-deconfigure)
+ :
+ ;;
+
+ *)
+ echo "postinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
+
--- /dev/null
- [ -x /sbin/stop ] && stop ceph-mon-all || true
+#!/bin/sh
+# vim: set noet ts=8:
+
+set -e
+
+case "$1" in
+ remove)
+ invoke-rc.d ceph stop mon || {
+ RESULT=$?
+ if [ $RESULT != 100 ]; then
+ exit $RESULT
+ fi
+ }
+ ;;
+
+ *)
+ ;;
+esac
+
+#DEBHELPER#
+
+exit 0
--- /dev/null
- lib/udev/rules.d/60-ceph-by-parttypeuuid.rules
- lib/udev/rules.d/95-ceph-osd.rules
- usr/bin/ceph-bluestore-tool
++#!/usr/bin/dh-exec
++etc/sysctl/90-ceph-osd.conf => etc/sysctl.d/30-ceph-osd.conf
++udev/95-ceph-osd.rules lib/udev/rules.d
++usr/sbin/ceph-disk
+usr/bin/ceph-clsinfo
+usr/bin/ceph-objectstore-tool
++usr/bin/ceph-bluestore-tool
+usr/bin/ceph-osdomap-tool
- usr/bin/ceph-osd
++udev/60-ceph-by-parttypeuuid.rules lib/udev/rules.d
+usr/bin/ceph_objectstore_bench
++usr/bin/ceph-osd
+usr/lib/ceph/ceph-osd-prestart.sh
- usr/lib/libos_tp.so*
- usr/lib/libosd_tp.so*
- usr/lib/python*/dist-packages/ceph_disk*
- usr/sbin/ceph-disk
+usr/sbin/ceph-volume
+usr/sbin/ceph-volume-systemd
- usr/lib/python*/dist-packages/ceph_volume/*
+usr/lib/python*/dist-packages/ceph_volume-*
++usr/lib/python*/dist-packages/ceph_volume
+usr/share/man/man8/ceph-clsinfo.8
+usr/share/man/man8/ceph-disk.8
+usr/share/man/man8/ceph-volume.8
+usr/share/man/man8/ceph-volume-systemd.8
+usr/share/man/man8/ceph-osd.8
+usr/share/man/man8/ceph-bluestore-tool.8
- etc/sysctl.d/30-ceph-osd.conf
++usr/lib/python*/dist-packages/ceph_disk*
--- /dev/null
--- /dev/null
++# Ceph service files define their own targets which are then WantedBy
++# multi-user.target
++systemd-service-file-refers-to-unusual-wantedby-target lib/systemd/system/ceph-osd@.service ceph-osd.target
--- /dev/null
- configure)
- [ -x /etc/init.d/procps ] && invoke-rc.d procps restart || :
- [ -x /sbin/start ] && start ceph-osd-all || :
- # work around https://tracker.ceph.com/issues/24903
- chown -h ceph:ceph /var/lib/ceph/osd/*/block* 2>&1 > /dev/null || :
- ;;
- abort-upgrade|abort-remove|abort-deconfigure)
++#!/bin/sh
+# vim: set noet ts=8:
+# postinst script for ceph-osd
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+#
+# postinst configure <most-recently-configured-version>
+# old-postinst abort-upgrade <new-version>
+# conflictor's-postinst abort-remove in-favour <package> <new-version>
+# postinst abort-remove
+# deconfigured's-postinst abort-deconfigure in-favour <failed-install-package> <version> [<removing conflicting-package> <version>]
+#
+
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+[ -f "/etc/default/ceph" ] && . /etc/default/ceph
+[ -z "$SERVER_USER" ] && SERVER_USER=ceph
+[ -z "$SERVER_GROUP" ] && SERVER_GROUP=ceph
+
+case "$1" in
++ configure|abort-upgrade|abort-remove|abort-deconfigure)
+ :
+ ;;
+
+ *)
+ echo "postinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
+
--- /dev/null
- [ -x /sbin/stop ] && stop ceph-osd-all || true
+#!/bin/sh
+# vim: set noet ts=8:
+
+set -e
+
+case "$1" in
+ remove)
+ invoke-rc.d ceph stop osd || {
+ RESULT=$?
+ if [ $RESULT != 100 ]; then
+ exit $RESULT
+ fi
+ }
+ ;;
+
+ *)
+ ;;
+esac
+
+#DEBHELPER#
+
+exit 0
--- /dev/null
- usr/lib/ocf/resource.d/ceph/*
++usr/lib/ocf/resource.d/ceph
--- /dev/null
- usr/bin/ceph-client-debug
- usr/bin/ceph-coverage
- usr/bin/ceph_bench_log
- usr/bin/ceph_erasure_code
- usr/bin/ceph_erasure_code_benchmark
- usr/bin/ceph_kvstorebench
- usr/bin/ceph_multi_stress_watch
- usr/bin/ceph_omapbench
- usr/bin/ceph_perf_local
- usr/bin/ceph_perf_msgr_client
- usr/bin/ceph_perf_msgr_server
- usr/bin/ceph_perf_objectstore
- usr/bin/ceph_psim
- usr/bin/ceph_radosacl
- usr/bin/ceph_rgw_jsonparser
- usr/bin/ceph_rgw_multiparser
- usr/bin/ceph_scratchtool
- usr/bin/ceph_scratchtoolpp
- usr/bin/ceph_smalliobench
- usr/bin/ceph_smalliobenchdumb
- usr/bin/ceph_smalliobenchfs
- usr/bin/ceph_smalliobenchrbd
- usr/bin/ceph_test_*
- usr/bin/ceph_tpbench
- usr/bin/ceph_xattr_bench
- usr/lib/ceph/ceph-monstore-update-crush.sh
- usr/share/java/libcephfs-test.jar
++usr/bin/ceph-client-debug /usr/lib/ceph/bin
++usr/bin/ceph-coverage /usr/lib/ceph/bin
++usr/bin/ceph_bench_log /usr/lib/ceph/bin
++usr/bin/ceph_erasure_code /usr/lib/ceph/bin
++usr/bin/ceph_erasure_code_benchmark /usr/lib/ceph/bin
++usr/bin/ceph_kvstorebench /usr/lib/ceph/bin
++usr/bin/ceph_multi_stress_watch /usr/lib/ceph/bin
++usr/bin/ceph_omapbench /usr/lib/ceph/bin
++usr/bin/ceph_perf_* /usr/lib/ceph/bin
++usr/bin/ceph_psim /usr/lib/ceph/bin
++usr/bin/ceph_radosacl /usr/lib/ceph/bin
++usr/bin/ceph_rgw_jsonparser /usr/lib/ceph/bin
++usr/bin/ceph_rgw_multiparser /usr/lib/ceph/bin
++usr/bin/ceph_scratchtool /usr/lib/ceph/bin
++usr/bin/ceph_scratchtoolpp /usr/lib/ceph/bin
++usr/bin/ceph_smalliobench /usr/lib/ceph/bin
++usr/bin/ceph_smalliobenchdumb /usr/lib/ceph/bin
++usr/bin/ceph_smalliobenchfs /usr/lib/ceph/bin
++usr/bin/ceph_smalliobenchrbd /usr/lib/ceph/bin
++usr/bin/ceph_test_* /usr/lib/ceph/bin
++usr/bin/ceph_tpbench /usr/lib/ceph/bin
++usr/bin/ceph_xattr_bench /usr/lib/ceph/bin
++usr/lib/*/ceph/ceph-monstore-update-crush.sh /usr/lib/ceph
--- /dev/null
--- /dev/null
++debian/tmp/usr/share/java/libcephfs-test.jar
--- /dev/null
--- /dev/null
++ceph (10.2.5-1) unstable; urgency=medium
++
++ ## Upgrades from Debian Jessie
++
++ Online upgrades from Ceph versions prior to Hammer (0.94.x) are not
++ supported by upstream. As Debian Jessie has Ceph Firefly (0.80.x) an
++ online upgrade from Jessie to Stretch is not possible. You have to first
++ shutdown all Ceph daemons on all nodes, upgrade everything to the new
++ version and start all daemons again.
++
++ Ceph daemons are not automatically restarted on upgrade to minimize
++ disruption. You have to manually restart them after the upgrade.
++
++ -- Gaudenz Steinlin <gaudenz@debian.org> Sun, 08 Jan 2017 14:57:35 +0100
++
++ceph (9.2.0-1) experimental; urgency=medium
++
++ ## systemd Enablement
++
++ For all distributions that support systemd (Debian Jessie 8.x,
++ Ubuntu >= 16.04), Ceph daemons are now managed using upstream provided
++ systemd files instead of the legacy sysvinit scripts or distro provided
++ systemd files. For example:
++
++ systemctl start ceph.target # start all daemons
++ systemctl status ceph-osd@12 # check status of osd.12
++
++ To upgrade existing deployments that use the older systemd service
++ configurations (Ubuntu >= 15.04, Debian >= Jessie), you need to switch
++ to using the new ceph-mon@ service:
++
++ systemctl stop ceph-mon
++ systemctl disable ceph-mon
++
++ systemctl start ceph-mon@`hostname`
++ systemctl enable ceph-mon@`hostname`
++
++ and also enable the ceph target post upgrade:
++
++ systemctl enable ceph.target
++
++ The main notable distro that is *not* using systemd is Ubuntu 14.04
++ (The next Ubuntu LTS, 16.04, will use systemd instead of upstart).
++
++ ## Ceph daemons no longer run as root
++
++ Ceph daemons now run as user and group 'ceph' by default. The
++ ceph user has a static UID assigned by Debian to ensure consistency
++ across servers within a Ceph deployment.
++
++ If your systems already have a ceph user, upgrading the package will cause
++ problems. We suggest you first remove or rename the existing 'ceph' user
++ and 'ceph' group before upgrading.
++
++ When upgrading, administrators have two options:
++
++ 1. Add the following line to 'ceph.conf' on all hosts:
++
++ setuser match path = /var/lib/ceph/$type/$cluster-$id
++
++ This will make the Ceph daemons run as root (i.e., not drop
++ privileges and switch to user ceph) if the daemon's data
++ directory is still owned by root. Newly deployed daemons will
++ be created with data owned by user ceph and will run with
++ reduced privileges, but upgraded daemons will continue to run as
++ root.
++
++ 2. Fix the data ownership during the upgrade. This is the
++ preferred option, but it is more work and can be very time
++ consuming. The process for each host is to:
++
++ 1. Upgrade the ceph package. This creates the ceph user and group. For
++ example:
++
++ apt-get install ceph
++
++ NOTE: the permissions on /var/lib/ceph/mon will be set to ceph:ceph
++ as part of the package upgrade process on existing *systemd*
++ based installations; the ceph-mon systemd service will be
++ automatically restarted as part of the upgrade. All other
++ filesystem permissions on systemd based installs will
++ remain unmodified by the upgrade.
++
++ 2. Stop the daemon(s):
++
++ systemctl stop ceph-osd@* # debian, ubuntu >= 15.04
++ stop ceph-all # ubuntu 14.04
++
++ 3. Fix the ownership:
++
++ chown -R ceph:ceph /var/lib/ceph
++
++ 4. Restart the daemon(s):
++
++ start ceph-all # ubuntu 14.04
++ systemctl start ceph.target # debian, ubuntu >= 15.04
++
++ Alternatively, the same process can be done with a single daemon
++ type, for example by stopping only monitors and chowning only
++ '/var/lib/ceph/osd'.
++
++ ## KeyValueStore OSD on-disk format changes
++
++ The on-disk format for the experimental KeyValueStore OSD backend has
++ changed. You will need to remove any OSDs using that backend before you
++ upgrade any test clusters that use it.
++
++ ## Deprecated commands
++
++ 'ceph scrub', 'ceph compact' and 'ceph sync force' are now DEPRECATED.
++ Users should instead use 'ceph mon scrub', 'ceph mon compact' and
++ 'ceph mon sync force'.
++
++ ## Full pool behaviour
++
++ When a pool quota is reached, librados operations now block indefinitely,
++ the same way they do when the cluster fills up. (Previously they would
++ return -ENOSPC). By default, a full cluster or pool will now block. If
++ your librados application can handle ENOSPC or EDQUOT errors gracefully,
++ you can get error returns instead by using the new librados
++ OPERATION_FULL_TRY flag.
++
++ -- James Page <james.page@ubuntu.com> Mon, 30 Nov 2015 09:23:09 +0000
++
++ceph (0.80.9-2) unstable; urgency=medium
++
++ ## CRUSH fixes in 0.80.9
++
++ The 0.80.9 point release fixes several issues with CRUSH that trigger excessive
++ data migration when adjusting OSD weights. These are most obvious when a very
++ small weight change (e.g., a change from 0 to .01) triggers a large amount of
++ movement, but the same set of bugs can also lead to excessive (though less
++ noticeable) movement in other cases.
++
++ However, because the bug may already have affected your cluster, fixing it
++ may trigger movement back to the more correct location. For this reason, you
++ must manually opt-in to the fixed behavior.
++
++ In order to set the new tunable to correct the behavior:
++
++ ceph osd crush set-tunable straw_calc_version 1
++
++ Note that this change will have no immediate effect. However, from this
++ point forward, any ‘straw’ bucket in your CRUSH map that is adjusted will get
++ non-buggy internal weights, and that transition may trigger some rebalancing.
++
++ You can estimate how much rebalancing will eventually be necessary on your
++ cluster with:
++
++ ceph osd getcrushmap -o /tmp/cm
++ crushtool -i /tmp/cm --num-rep 3 --test --show-mappings > /tmp/a 2>&1
++ crushtool -i /tmp/cm --set-straw-calc-version 1 -o /tmp/cm2
++ crushtool -i /tmp/cm2 --reweight -o /tmp/cm2
++ crushtool -i /tmp/cm2 --num-rep 3 --test --show-mappings > /tmp/b 2>&1
++ wc -l /tmp/a # num total mappings
++ diff -u /tmp/a /tmp/b | grep -c ^+ # num changed mappings
++
++ Divide the total number of lines in /tmp/a with the number of lines
++ changed. We've found that most clusters are under 10%.
++
++ You can force all of this rebalancing to happen at once with:
++
++ ceph osd crush reweight-all
++
++ Otherwise, it will happen at some unknown point in the future when
++ CRUSH weights are next adjusted.
++
++ ## Mapping rbd devices with rbdmap on systemd systems
++
++ If you have setup rbd mappings in /etc/ceph/rbdmap and corresponding mounts
++ in /etc/fstab things might break with systemd because systemd waits for the
++ rbd device to appear before the legacy rbdmap init file has a chance to run
++ and drops into emergency mode if it times out.
++
++ This can be fixed by adding the nofail option in /etc/fstab to all rbd
++ backed mount points. With this systemd does not wait for the device and
++ proceeds with the boot process. After rbdmap mapped the device, systemd
++ detects the new device and mounts the file system.
++
++ -- Gaudenz Steinlin <gaudenz@debian.org> Mon, 04 May 2015 22:49:48 +0200
--- /dev/null
--- /dev/null
++rm_conffile /etc/bash_completion.d/ceph 10.2.5-1~
++rm_conffile /etc/init/ceph-all.conf 10.2.5-1~
++rm_conffile /etc/init/ceph-create-keys.conf 10.2.5-1~
++rm_conffile /etc/init/ceph-mon-all-starter.conf 10.2.5-1~
++rm_conffile /etc/init/ceph-mon-all.conf 10.2.5-1~
++rm_conffile /etc/init/ceph-mon.conf 10.2.5-1~
++rm_conffile /etc/init/ceph-osd-all-starter.conf 10.2.5-1~
++rm_conffile /etc/init/ceph-osd-all.conf 10.2.5-1~
++rm_conffile /etc/init/ceph-osd.conf 10.2.5-1~
++rm_conffile /etc/init/rbdmap.conf 10.2.5-1~
++rm_conffile /etc/logrotate.d/ceph 10.2.3-0ubuntu5~
--- /dev/null
- ceph (12.2.8-1) stable; urgency=medium
++ceph (12.2.8+dfsg1-5) unstable; urgency=medium
++
++ * [66e03b] Fix linking on archs which require libatomic
++ * [5236f3] Fix Python 3 autopkgtest
++
++ -- Gaudenz Steinlin <gaudenz@debian.org> Fri, 30 Nov 2018 16:49:02 +0100
++
++ceph (12.2.8+dfsg1-4) unstable; urgency=medium
++
++ * [97dfb6] Fix detection of armel for NEON instructions (Closes: #913599)
++
++ -- Gaudenz Steinlin <gaudenz@debian.org> Thu, 22 Nov 2018 22:33:21 +0100
++
++ceph (12.2.8+dfsg1-3) unstable; urgency=medium
++
++ * [168bb9] Build depend on pythonX-dev instead of pythonX-all-dev
++ (Closes: #912905)
++ * [38d140] Patch to check for atomic support during build (Closes: #913601)
++ * [9cc0a5] Disable libboost-context on unsupported archs (Closes: #913600)
++ * [238143] Fix building with -g1 on 32 bit architectures
++ * [3b48d5] Use -mfloat-abi=softfp on armel for NEON instructions
++ (Closes: #913599)
++
++ -- Gaudenz Steinlin <gaudenz@debian.org> Thu, 22 Nov 2018 09:38:09 +0100
++
++ceph (12.2.8+dfsg1-2) unstable; urgency=medium
++
++ * [5c4b36] Add architecture specific symbols for librados2
++ * [38bc1b] Build depend on libatomic1 on armel, m68k, mips, mipsel, powerpc,
++ powerpcspe and sh4
++
++ -- Gaudenz Steinlin <gaudenz@debian.org> Wed, 31 Oct 2018 23:38:05 +0100
++
++ceph (12.2.8+dfsg1-1) unstable; urgency=medium
++
++ [ James Page ]
++ * [9c55f6] Ensure that systemd targets are enabled and started
++ * [17ca38] Support optional runtime loading of openssl in radosgw
++ * [8927f6] Fix build on i386
++ * [774281] Ensure all ceph modules are included in the binary package.
++ * [00ca38] Cherry pick upstream fix to resolve FTBFS on armhf
++ * [5d118c] Add ceph-volume tools to ceph-osd package
++
++ [ Gaudenz Steinlin ]
++ * [ba768b] New upstream version 12.2.8+dfsg1
++ (Closes: #852999, #864535, #893149)
++ * [96eba0] Exclude jsonchecker from upstream source
++ * [7dd73b] Exclude Windows help file from orig tarball
++ * [9d1740] Move ceph initscript and systemd target to ceph-base
++ * [8ae049] Drop obsolete patches (either upstreamed or because of switch to
++ cmake)
++ * [cfd095] Create directory for Ceph Manager bootstrap keys
++ * [858194] Shell scripts moved out of architecture specific directory
++ * [f3f387] Merge ceph-fs-common package into ceph-common
++ * [1789fe] Move radosgw-admin to ceph-common
++ * [6f51fc] Add compressor plugins to ceph-common
++ * [5bd0d1] Add crypto plugins to ceph-common on amd64
++ * [7724f7] New binary package ceph-mgr
++ * [804be6] Remove librgw_file* from ceph-test. These are unit tests for rgw.
++ * [fef87a] Upstream changed the build system to cmake
++ * [134e34] Build Python 3 versions of Python modules (Closes: #883148)
++ * [52a969] Package RADOS gateway Python bindings (python-rgw)
++ * [a48700] Add rados-objclass-dev binary package
++ * [30306f] Update dependencies for new upstream release
++ * [4d07af] libcephfs1 -> libcephfs2 soname bumped upstream
++ * [c4022d] Remove ceph-disk-udev no longer shipped upstream
++ * [9aad82] Remove sample.fetch_config no longer shipped upstream
++ * [fc163d] Update copyright for new upstream release
++ * [21c408] Add /usr/bin/radosgw-es to radosgw package
++ * [f63b2a] ceph-osd: sysctl config to increase the maximum number of AIO
++ requests
++ * [5f1f30] Remove static libraries from -dev packages (removed upstream)
++ * [1db168] Install ceph-detect-init into /usr/bin instead of /usr/sbin
++ * [1fb625] Move ceph-*-tool from ceph-test into daemon packages
++ * [6a5628] Remove obsolete X-Python-Version
++ * [c6fc4d] Update to Debian Policy version 4.2.1
++ * [5821f2] Set multiarch triplet in debian/rules
++ * [9bdce0] Remove override of dh_auto_install
++ * [b71c87] Add libceph-common to librados2 and librados-dev pkgs
++ * [c746ce] Override JSON license lintian warning (false positive, code
++ removed)
++ * [17d65c] Use dh_missing instead of dh_install --list-missing
++ * [cba40a] Remove obsolete ceph-create-keys@.service from ceph-base
++ * [252d62] Update watch file for https and repacking
++ * [ef84cf] Install ceph SysV init script with dh_install
++ * [0c2c84] Add missing source for jquery.flot.js (actually
++ jquery.colorhelpers.js)
++ * [0f1e2e] Add missing sources for JQuery in civetweb
++ * [8010cc] Add missing sources from AdminLTE
++ * [7c425a] Add lintian overrides for source-is-missing false positives
++ * [75f7d2] Update symbols files
++ * [aa7295] Patch to fix build failures with Boost 1.67
++ * [872887] Build with Debian packaged Boost 1.67 instead of bundled Boost 1.66
++ * [abf215] Use google-perftools on all supported architectures
++ * [44dd74] Add missing sources for the mgr dashboard plugin
++ * [39f9d6] Depend on ceph-common for pythonX-cephfs (Closes: #896400)
++ * [7a10f0] Improve autopkgtests
++ * [7515ca] Add Python dependency for ceph-fuse
++ * [d91a92] Make the ceph binary package a pure metapackage
++ * [df1dc8] Add Lintian overrides for systemd targets
++ * [bc7cd4] Install upstream rbdmap systemd service file
++ * [dd44ea] Remove RUNPATH from JNI libraries
++ * [7e9bce] Add dependency on junit4 and libcephfs-java for ceph-test
++ * [dfbe7c] Remove unnecessary ceph-base postrm script (Closes: #867465)
++ * [bad29d] Fix permissions on /var/run/ceph in SysV init script
++ (Closes: #869142)
++ * [6edc7a] Mark libraries as Multi-Arch compatible (Closes: #822740)
++
++ [ Thomas Goirand ]
++ * [46be6f] Change VCS links to point to Salsa
++
++ [ Shengjing Zhu ]
++ * [bfcf95] Don't treat rados-classes and ceph/compressor as shared libraries
++ * [0ef4f9] Add missing interpreter in ceph-{osd,mon}.postinst
++ (Closes: #862684, #862685)
++ * [f75bfc] Change section of libcephfs-jni from libs to java
++ * [f4b5d4] Add patch to fix various spelling errors
++ * [f60d21] Patch to remove link to ceph.com for dashboard favicon
++ * [116028] Backport test build fix from upstream
++ * [b83166] Change architecture of ceph-fuse to linux-any (from amd64)
++
++ -- Gaudenz Steinlin <gaudenz@debian.org> Sun, 28 Oct 2018 23:43:10 +0100
++
++ceph (10.2.5-7.2) unstable; urgency=medium
++
++ * Non-maintainer upload.
++ * Build with -g1 instead of -g on 32bit architectures to fix
++ FTBFS due to the 2GB/3GB address space limits.
++
++ -- Adrian Bunk <bunk@debian.org> Wed, 07 Jun 2017 11:39:39 +0300
++
++ceph (10.2.5-7.1) unstable; urgency=medium
++
++ * Non-maintainer upload.
++ * ceph-mon: Add Breaks+Replaces: ceph-common (<< 10) for taking over
++ /usr/bin/ceph-rest-api. (Closes: #864161)
++
++ -- Andreas Beckmann <anbe@debian.org> Tue, 06 Jun 2017 09:08:30 +0200
++
++ceph (10.2.5-7) unstable; urgency=medium
++
++ * [a12798] Add fix-init-system-detection.patch.
++ This replaces the static init system detection by more appropriate
++ runtime detection which also works if a Debian system is running with
++ sysvinit. (Closes: #862075)
++
++ -- Gaudenz Steinlin <gaudenz@debian.org> Fri, 12 May 2017 12:12:00 +0200
++
++ceph (10.2.5-6) unstable; urgency=medium
++
++ * [e44a30] Disable running dh_auto_install with parallel jobs
++ (Closes: #850906)
++ * [b7e926] Link with libatomic and --as-needed on all archs (Closes: #850831)
++
++ -- Gaudenz Steinlin <gaudenz@debian.org> Wed, 11 Jan 2017 12:02:24 +0100
++
++ceph (10.2.5-5) unstable; urgency=medium
++
++ * [f4675d] Set _FILE_OFFSET_BITS=64 via DEB_CPPFLAGS_MAINT_APPEND
++ * [f60392] Install systemd targets for ceph services (Closes: #850509)
++ * [d3baba] Link with -latomic on mips/mipsel
++
++ -- Gaudenz Steinlin <gaudenz@debian.org> Tue, 10 Jan 2017 09:04:48 +0100
++
++ceph (10.2.5-4) unstable; urgency=medium
++
++ * [88034c] Build with ggc-min-expand=5 on mips/el (Closes: #849657)
++ * [c31e79] Add patch to build rocksdb with -latomic on mips/el
++ * [182dd8] NEWS entry about upgrades from Debian Jessie
++
++ -- Gaudenz Steinlin <gaudenz@debian.org> Sun, 08 Jan 2017 21:34:21 +0100
++
++ceph (10.2.5-3) unstable; urgency=medium
++
++ * [eeff8d] Use -mfloat-abi=softfp on armel for NEON plugin (Closes: #849660)
++
++ -- Gaudenz Steinlin <gaudenz@debian.org> Thu, 05 Jan 2017 09:18:41 +0100
++
++ceph (10.2.5-2) unstable; urgency=medium
++
++ * [3859ca] Only list missing files instead of failing (Closes: #849031)
++ * [47aef7] Remove chrony from recommends (provides time-daemon)
++ (Closes: #827673)
++ * [3e96e6] Upstream fix for CVE-2016-9579 (short CORS request)
++ (Closes: #849048)
++
++ -- Gaudenz Steinlin <gaudenz@debian.org> Sat, 24 Dec 2016 13:14:28 +0100
++
++ceph (10.2.5-1) unstable; urgency=medium
++
++ [ James Page ]
++ * [754935] Imported Upstream version 9.2.0
++ - [df85c3] Resync relevant packaging changes with upstream.
++ - [be5f82] Refresh patches.
++ - [d1f3fe] Add python-setuptools to BD's for ceph-detect-init.
++ - [b2f926] Add lsb-release to BD's to ensure that python modules are
++ installed to correct locations.
++ - [e4d702] Add python-sphinx to BD's to ensure man pages get generated
++ and installed.
++ - [3ead6e] Correct install location for ceph-monstore-update tool.
++ - [269754] [177b7a] Update symbols for new release.
++ * [4c45629] Update NEWS file for infernalis changes.
++ * [940491e] Limit number of parallel builds to 2 to reduce memory footprint
++ on builders.
++ * [a2bed4] New upstream version 10.2.5
++ * [cbfb6b] Sync upstream changes around rbdmap install and conf files.
++ * [5c52b2] Drop patches include upstream, refresh remaining patches
++ * [8f9820] Resync further packaging changes from upstream.
++ * [27d010] Re-add bz2-dev BD.
++ * [e94aa2] Add python-dev to BD's.
++ * [e201c1] d/p/pybind-flags.patch: Ensure that cython *FLAGS are correctly
++ set.
++ * [14cd12] d/p/fix-systemd-escaping.patch: Ensure that leading '/' is stripped
++ from block device paths when escaping for use in systemd unit
++ names.
++ * [33b3aa] Add pull request for systemd fixes
++ * [d781a2] d/ceph-mds.postinst: Fix syntax error.
++ * [ce55ec] Ensure that python flags are correct set for cython rbd build.
++ * [f9e35b] Switch rbd python binding to cython
++ * [8ccae0] Drop ceph_volume_client until its actually in the codebase.
++ * [0a11cc] Install to relative, not absolute /etc/ceph.
++ * [e2415a] Drop ceph-bluefs-tool from ceph package.
++ * [b16966] d/ceph-mds.dirs: Actually create /var/lib/ceph/mds prior to
++ changing permissions (LP: #1544647).
++ * [0c1201] d/ceph.init: Restore link to init-ceph, resolving un-install
++ failures due to missing init script (LP: #1546112).
++
++ [ Gaudenz Steinlin ]
++ * [e3cb86] Remove no longer needed check for dh-autoreconf>=6
++ * [9157b3] Install ceph-brag tool into ceph-common
++ * [74e9f3] Install ceph-client-debug into ceph-test
++ * [476449] Use jh_installlib to install Java libraries
++ * [643f6e] Don't remove configure script on dh_clean
++ * [4bb203] Remove *.la files to binary packages
++ * [b76c6a] Add build dependency on libboost-iostreams-dev
++ * [b0506f] Add build dependency on libldap2-dev
++ * [526ce1] Refresh patches for upstream version 10.2.2
++ * [cf539b] Remove patches applied upstream in 10.2.2
++
++ [ James Page ]
++ * [646b48] Add librgw2 binary packages.
++ * [ae3cda] Drop virtualenv from BD's.
++ * [7ebd7c] Re-enable rocksdb build for bluestore support
++ * [e393e5] Add rbd-mirror package
++ * [00d06d] Add patch to set g++ flags correctly for rocksdb
++ * [3b029a] Enable gperftools on arm64 architecture.
++ * [e88620] Add ceph-bluefs-tool to install.
++ * [4f40a1] Add s390x to list of rocksdb flags
++ * [858334] d/p/skip-setup.py-makefiles.patch,rules: Avoid use of virtualenv to
++ install ceph-disk and ceph-detect-init python modules.
++ * [40c3b7] Update watch file to scan for gz files.
++ * [49b716] Install librgw_file* as part of ceph-test package.
++
++ [ Gaudenz Steinlin ]
++ * [2e156d] d/rules: Install upstart and systemd configurations for rbd-mirror.
++
++ [ James Page ]
++ * [ca0b07] d/copyright: Ensure that jerasure and gf-complete are not stripped
++ from the upstream release tarball.
++ * [eee861] d/p/disable-openssl-linking.patch: Disable build time linking with
++ OpenSSL due to licensing incompatibilities.
++ * [07d7a6] d/*.symbols: Add new symbols for RC.
++ * [2416f1] Fix multiarch paths for librgw
++ * [2da01d] d/rules: Ensure that dh_systemd_start does not insert maintainer
++ script snippets for ceph-mon and ceph-create-keys - service restart
++ should be handled outside of the packaging as it is under upstart
++ and for all other systemd unit files installed (LP: #1563330).
++ * [76ec3b] d/ceph-common.postinst: Silence output of usermod call
++ (LP: #1569249).
++ * [63d60f] d/rules,ceph-common.install: Ensure that /etc/default/ceph is a
++ file and not a directory (LP: #1587516).
++ * [a866ca] d/control: Bumped Standards-Version to 3.9.8, no changes.
++ * [e0811e] d/ceph.install: Drop install of 60-ceph-partuuid-workaround.rules
++ as no longer part of upstream codebase.
++ * [25954f] * d/*: Split ceph-osd and ceph-mon into separate binary packages
++ and add new ceph-base binary package inline with upstream packaging
++ changes (LP: #1596063).
++ * [f1287b] Add missing misc depends
++ * [afeb18] d/rules,control: Drop -dbg packages and let Ubuntu builds take care
++ of ddeb generation instead.
++
++ [ Gaudenz Steinlin ]
++ * [e133b2] Install rbdmap manpage into ceph-common
++ * [b5ed64] Install radosgw-token command into radosgw package
++ * [0a27e1] Adapt installation of Python files to latest Jewel release
++ * [ef2544] Drop rocksdb-flags patch applied upstream
++ * [07aef4] Add patch osd-limit-omap-data-in-push-op
++ * [32f14d] Refresh patches
++
++ [ James Page ]
++ * [f4e95c] Fix install location for mount.ceph
++ * [6a9efc] Update install location for mount.ceph.fuse
++ * [7624f0] d/control: Add Pre-Depends on ceph-common to ceph-osd to ensure
++ that ceph user and group are created prior to unpacking of udev
++ rules (LP: #1631328).
++
++ [ Frode Nordahl ]
++ * [022ee3] Add rgw_rados-creation_time patch
++
++ [ Gaudenz Steinlin ]
++ * [05225f] Remove old ceph logrotate configuration
++
++ [ James Page ]
++ * [cfa82f] Refresh patches
++
++ [ Gaudenz Steinlin ]
++ * [fdec5d] Remove upstart support
++ * [5966f0] Use new RSA key for ceph-post-file
++ * [3748b6] Only install ChangeLog as upstream changelog
++ * [aa1393] Install bash-completions to /usr
++ * [ab0a88] Dependency on lsb-base for initscripts
++ * [4d78b0] Disable radosgw SysV init script on systemd
++ * [ff4c87] Don't install python bytecode
++ * [3cbf5e] Add missing dependencies on python (ceph-osd, ceph-mon)
++ * [8c70df] Update symbols files (librados2 and librbd1)
++ * [e4f0c5] Add ${shlibs:Depends} on Python C extensions
++ * [936838] Allow setting the number of parallel jobs
++ * [e5aa0f] Remove Laszlo and add myself to uploaders
++
++ -- Gaudenz Steinlin <gaudenz@debian.org> Sat, 17 Dec 2016 22:40:22 +0100
++
++ceph (0.94.5-1) experimental; urgency=medium
++
++ * [2d330d6] New upstream release:
++ - [1e93090] Drop patch for CVE-2015-5245, included upstream.
++ - [20adc7d] Refresh all other patches.
++ * [9255e5d] Ensure any erasure coding test libraries and dangling symlinks
++ are not included in the ceph package.
++
++ -- James Page <james.page@ubuntu.com> Mon, 09 Nov 2015 12:09:51 +0000
++
++ceph (0.94.3-1) experimental; urgency=medium
++
++ * [580fef] Imported Upstream version 0.94.3 (Closes: #777814, #795178)
++ * [536935] Add upstream patch to fix CVE-2015-5245 (Closes: #798567)
++
++ -- Gaudenz Steinlin <gaudenz@debian.org> Fri, 18 Sep 2015 16:55:23 +0200
++
++ceph (0.94.2-2) experimental; urgency=medium
++
++ * Revert "Drop virtualenv BD, disable unit tests."
++ * Restore patches for test enablement.
++ * Display test-suite log output in the event of failures.
++
++ -- James Page <james.page@ubuntu.com> Mon, 20 Jul 2015 13:37:06 +0100
++
++ceph (0.94.2-1) experimental; urgency=medium
++
++ * Resync with Ubuntu, introducing Ceph Hammer stable release:
++ - d/*.symbols: Update inline with upstream additions, use regex
++ for ceph version symbol.
++ - d/lib-systemd/system/ceph-create-keys.service: Automatically create
++ admin and bootstrap keys after ceph mon startup.
++ - d/p/vivid-does-systemd.patch: Ensure that disks prepared on vivid
++ or later use systemd for init.
++ - d/lib-systemd/system/*.service: Align nofile limits and restart config
++ with equivalent upstart configurations.
++ - d/p/fix-cycles-arch.patch: Skip initialization of cycles_per_sec
++ if rtdsc (or equivalent) is not supported.
++ - d/ceph{-common}.install,control: Move ceph_argparse.py down into
++ ceph-common package to fixup ceph cli usage/autopkgtest failure.
++ - d/control,ceph-common.install,librbd1.install: Move rbdnamer and
++ associated udev rules into ceph-common package.
++ - d/control,python-*: Split out rbd, rados and cephfs bindings into
++ separate python packages, move some bits into ceph/ceph-common.
++ - d/control: Move python-flask dependency to ceph package, only required
++ for REST API.
++ - d/control: Use google-perftools on arm64.
++ - d/control: Re-order Recommends to prefer ntp over chrony for Ubuntu.
++ - d/p/ceph-osd-prestart-path.patch: Fixup path for ceph-osd upstart
++ configuration pre-start script.
++ - d/p/fix-argparse-defaults.patch: Workaround behavioural change in
++ argparse set_defaults in python 2.7.9
++ * New upstream point release:
++ - d/p/*: Refresh.
++ * d/p/use_system_jerasure.patch,d/control: Drop use of libjerasure
++ as the patch is intrusive and expensive to maintain; will revisit if
++ adopted upstream.
++
++ -- James Page <james.page@ubuntu.com> Tue, 16 Jun 2015 11:31:05 +0100
++
++ceph (0.87-2) experimental; urgency=low
++
++ * Team upload.
++
++ [ Gaudenz Steinlin ]
++ * README.Debian: added clarification about setting the hashpspool flag.
++ (Closes: #769596).
++
++ [ James Page ]
++ * Added new "modules.patch" to mark new erasure coding libraries as
++ modules, wildcard install.
++
++ [ Dmitry Smirnov ]
++ * Recommends: added "ntp" to list of time-daemon alternatives
++ (Closes: #767511).
++ * Introduced native systemd services (except "rbdmap"), (Closes: #769593).
++ * ceph-test: install forgotten files.
++ * Run post-build tests:
++ + updated "virtualenv-never-download.patch" to pass
++ "--system-site-packages" to virtualenv to prevent downloads.
++ + added new patches to disable network-dependent and failing tests.
++ * Patchworks:
++ - bug-9341.patch
++ + bug-10036.patch (to show OSD affinity in "ceph osd tree").
++ Thanks, Mykola Golub.
++ + bug-10059.patch
++ + 0latest-giant.patch (Last-Update: 2014-11-15).
++ + sleep-recover.patch
++ + tests-disable.patch (to disable tests that need cluster).
++ + tests-disable-ceph-disk.patch
++ + use_system_gtest.patch (commented)
++ as first attempt to build with system "libgtest-dev".
++ + use_system_jerasure.patch
++ * Build-Depends:
++ + libjerasure-dev (>= 2.0.0-2~)
++ + virtualenv
++ + valgrind [amd64 armhf i386 powerpc]
++ * rules: pass "--without-lttng" to explicitly disable "lttng" to avoid
++ auto-enable if found.
++ * rules: disabled bundled RocksDB:
++ RocksDB suppose to improve performance of keyvaluestore OSDs but the
++ latter slow down to nearly unusable state when filled over 1 TiB even with
++ RocksDB. Moreover KV backend is experimental and super dangerous -- I lost
++ cluster due to OSD poisoning caused by KV OSD which was plugged only
++ during limited time. LevelDB is good enough, for now I see no reason to
++ use RocksDB especially considering that it is not packaged separately.
++ * Removed myself from Uploaders.
++
++ -- Dmitry Smirnov <onlyjob@debian.org> Wed, 01 Apr 2015 11:47:38 +1100
++
++ceph (0.87-1) experimental; urgency=medium
++
++ * New major upstream release [October 2014].
++ + new "libradosstriper*" binary packages.
++ * Patchworks (removed old patches, refreshed remaining ones).
++ + "bug-9814.patch" to prevent OSD crash. Thanks, Haomai Wang.
++ * Install systemd sleep handler.
++ * Exclude erasure-code plugins from `dh_makeshlibs` processing to avoid
++ useless calls to `ldconfig` in maintainer scripts.
++ * Build-Depends:
++ + libbabeltrace-dev
++ + libbabeltrace-ctf-dev
++ + libbz2-dev
++ + libudev-dev
++ + zlib1g-dev
++ * Build with "--with-babeltrace".
++ * Build and statically link bundled RocksDB.
++
++ -- Dmitry Smirnov <onlyjob@debian.org> Thu, 30 Oct 2014 12:43:49 +1100
++
++ceph (0.80.9-2) unstable; urgency=medium
++
++ * [70fc1d] Add NEWS entry about CRUSH issues fixed in 0.80.9
++ * [f41bb6] Add NEWS entry about rbd backed filesystems and systemd
++
++ -- Gaudenz Steinlin <gaudenz@debian.org> Tue, 05 May 2015 21:29:15 +0200
++
++ceph (0.80.9-1) unstable; urgency=medium
++
++ * [4b4e] Imported Upstream version 0.80.9
++ * [7102] Remove patches firefly-latest and p2139 applied upstream
++ * [5869] Add myself to uploaders
++
++ -- Gaudenz Steinlin <gaudenz@debian.org> Mon, 04 May 2015 08:49:37 +0200
++
++ceph (0.80.7-2) unstable; urgency=medium
++
++ * Team upload.
++ * Build-Depends: +libjerasure-dev (>= 2.0.0-2~)
++ * New patch to use system "jerasure" library instead of its bundled copy.
++ * Removed myself from Uploaders.
+
- * New upstream release
++ -- Dmitry Smirnov <onlyjob@debian.org> Thu, 11 Dec 2014 12:55:38 +1100
+
- -- Ceph Release Team <ceph-maintainers@ceph.com> Thu, 30 Aug 2018 17:24:37 +0000
++ceph (0.80.7-1) unstable; urgency=medium
+
- ceph (12.2.7-1) stable; urgency=medium
++ * New upstream release [October 2014].
++ * Minor update to long description of "rbd-fuse" (Closes: #765462).
+
- * New upstream release
++ -- Dmitry Smirnov <onlyjob@debian.org> Thu, 16 Oct 2014 04:36:23 +1100
+
- -- Ceph Release Team <ceph-maintainers@ceph.com> Mon, 16 Jul 2018 16:00:29 +0000
++ceph (0.80.6-1) unstable; urgency=medium
++
++ * New upstream release [October 2014].
++ * Standards-Version: 3.9.6.
++
++ -- Dmitry Smirnov <onlyjob@debian.org> Thu, 02 Oct 2014 23:07:04 +1000
++
++ceph (0.80.5-2) unstable; urgency=low
++
++ * Patchworks:
++ + new patch for Ceph#9341 to dramatically (e.g seconds instead of
++ hours) reduce rejoin (i.e. MDS restart) time (fuse clients).
++ + new "p2139.patch".
++ + new patch with fixes from Firefly HEAD;
++ includes patch to fix FTBFS on alpha (Closes: #756892).
++ updated "librbd1.symbols";
++ * Build-Depends: mark "yasm" as [amd64] (Closes: #760383).
++ * Recommends: + "time-daemon | chrony".
++
++ -- Dmitry Smirnov <onlyjob@debian.org> Tue, 16 Sep 2014 03:54:15 +1000
+
- ceph (12.2.6-1) stable; urgency=medium
++ceph (0.80.5-1) unstable; urgency=medium
+
- * New upstream release
++ * New upstream stable release:
++ - d/p/firefly-post-release.patch: Dropped, no longer required.
++ - d/lib{rados2,cephfs1}.symbols: Update with new symbols.
+
- -- Ceph Release Team <ceph-maintainers@ceph.com> Mon, 09 Jul 2018 16:18:46 +0000
++ -- James Page <jamespage@debian.org> Wed, 30 Jul 2014 10:15:40 +0100
+
- ceph (12.2.5-1) stable; urgency=medium
++ceph (0.80.4-1) unstable; urgency=medium
+
- * New upstream release
++ * New upstream release [July 2014].
++ * New patches:
++ + rbdmap1-mount.patch
++ + rbdmap2-hooks.patch
++ + rbdmap3-lazyumount.patch
++ + bug-8821.patch
++ * radosgw: removed unused lintian overrides.
+
- -- Ceph Release Team <ceph-maintainers@ceph.com> Mon, 23 Apr 2018 16:18:32 +0000
++ -- Dmitry Smirnov <onlyjob@debian.org> Fri, 18 Jul 2014 02:33:39 +1000
+
- ceph (12.2.4-1) stable; urgency=medium
++ceph (0.80.1-2) unstable; urgency=low
+
- * New upstream release
++ * Megapatch from "firefly" branch with post-0.80.1 fixes.
++ * Patches for upstream bugs 8342, 8624 and some cherry-picks.
++ * New "bash-completion.patch" with Bash completion improvements.
++ * New patch to fix FTBFS on 'hppa' (Closes: #748571).
++ * "sample.ceph.conf.patch": minor update.
+
- -- Ceph Release Team <ceph-maintainers@ceph.com> Mon, 26 Feb 2018 21:47:17 +0000
++ -- Dmitry Smirnov <onlyjob@debian.org> Sat, 05 Jul 2014 20:29:44 +1000
+
- ceph (12.2.3-1) stable; urgency=medium
++ceph (0.80.1-1) unstable; urgency=low
+
- * New upstream release
++ * New upstream release [May 2014].
++ * Dropped all backported patches.
++ * New "sleep-recover" and "client-sleep[1,2,3]" patches to fix
++ fuse-client hang after resume from suspend [#8291]; thanks, Zheng Yan.
++ * New "gcj_search_path.patch" to find "jni.h" with gcj-jdk v4.9.0.
+
- -- Ceph Release Team <ceph-maintainers@ceph.com> Mon, 19 Feb 2018 23:14:45 +0000
++ -- Dmitry Smirnov <onlyjob@debian.org> Wed, 14 May 2014 09:24:15 +1000
+
- ceph (12.2.2-1) stable; urgency=medium
++ceph (0.80-1) unstable; urgency=low
+
- * New upstream release
++ * New upstream release [May 2014].
++ + upload to unstable.
++ * Updated "README.Debian".
++ * Updated "debian/copyright"; Thanks, László Böszörményi.
++ * Added backported patches:
++ [8113, 8175, 8282, 8291, bp0001, sample.ceph.conf].
++ * "gbp.conf": don't merge to experimental.
++ * lintian-overrides: spelling-error-in-binary * tEH the.
+
- -- Ceph Release Team <ceph-maintainers@ceph.com> Thu, 30 Nov 2017 14:59:26 +0000
++ -- Dmitry Smirnov <onlyjob@debian.org> Wed, 07 May 2014 16:43:07 +1000
+
- ceph (12.2.1-1) stable; urgency=medium
++ceph (0.80~rc1-1) experimental; urgency=low
+
- * New upstream release
++ * New upstream pre-release.
++ * Minor re-factoring of udev rules installation.
++ * ceph-common: added ceph-crush-location.1 man page.
++ * ceph-test-dbg: fixed Depends.
+
- -- Ceph Release Team <ceph-maintainers@ceph.com> Tue, 26 Sep 2017 16:27:06 +0000
++ -- Dmitry Smirnov <onlyjob@debian.org> Thu, 24 Apr 2014 02:52:12 +1000
+
- ceph (12.2.0-1) stable; urgency=medium
++ceph (0.79-3) experimental; urgency=low
+
- * New upstream release
++ * New "arch.patch" to detect build architecture using dpkg-architecture.
++ * Mark amd64-only symbols as such.
++ * Minor rules cleanup.
+
- -- Ceph Release Team <ceph-maintainers@ceph.com> Mon, 28 Aug 2017 16:30:16 +0000
++ -- Dmitry Smirnov <onlyjob@debian.org> Sat, 19 Apr 2014 15:56:37 +1000
+
- ceph (12.1.4-1) stable; urgency=medium
++ceph (0.79-2) experimental; urgency=low
+
- * New upstream release
++ [ James Page ]
++ * d/p/modules.patch,d/ceph.install: Mark all jerasure plugins as modules
++ and ensure they are all installed.
+
- -- Ceph Release Team <ceph-maintainers@ceph.com> Tue, 15 Aug 2017 13:45:08 +0000
++ [ Dmitry Smirnov ]
++ * Patchworks:
++ - removed unused "defaults-leveldb-osd.patch".
++ + improved description of "modules.patch". Thanks, James Page.
++ + added new backported patches [#5469, #8008, _1606, spelling].
++ * Added .symbols and "dh_makeshlibs -V" shlibs tightening (Closes: #744382).
++ * README.Debian: added note regarding kernel client mount option.
++ * copyright: added license for man files.
++ * control: "Suggests: logrotate".
+
- ceph (12.1.3-1) stable; urgency=medium
++ -- Dmitry Smirnov <onlyjob@debian.org> Fri, 18 Apr 2014 18:27:01 +1000
+
- * New upstream release
++ceph (0.79-1) experimental; urgency=low
+
- -- Ceph Release Team <ceph-maintainers@ceph.com> Thu, 10 Aug 2017 19:22:38 +0000
++ * New upstream release [April 2014].
++ * Tighten dependency on ceph-common.
++ * Install pm-suspend handler to stop/start ceph services on suspend/resume.
++ * New (inactive) patch to bump OSD's leveldb defaults.
++ * Patches dropped (applied-upstream):
++ - init.patch
++ - logrotate.patch
++ - fix-defaultweight.patch
++ * Refreshed "modules.patch".
+
- ceph (12.1.2-1) stable; urgency=medium
++ -- Dmitry Smirnov <onlyjob@debian.org> Tue, 08 Apr 2014 16:52:04 +1000
+
- * New upstream release
++ceph (0.78-2) experimental; urgency=low
+
- -- Ceph Release Team <ceph-maintainers@ceph.com> Tue, 01 Aug 2017 17:55:37 +0000
++ * Standards to 3.9.5.
++ * debian/copyright: reviewed and updated.
++ * ceph-test: added lintian-override for "binary-without-manpage".
++ * Patchworks:
++ + refreshed/renamed/reordered "virtualenv-never-download.patch".
++ + new "init.patch" for init.d scripts lintianisation.
++ + new "logrotate.patch" to avoid rotating empty logs.
++ + new "fix-defaultweight.patch" to fix weight calculation on OSD start.
++ + new "gcj.patch" with partial fix to FTBFS with gcj-jdk.
++ * Use symlinks to simplify installation of init.d and logrotate scripts.
++ * Added retrospective changelog entry to mention new B-D "libblkid-dev".
++ * Added "debian/clean file".
++ * Added "README.Debian" file with some hopefully useful notes.
++ * Added "mount.fuse.ceph.8" man page.
++ * rules:
++ + "dh --with" optimised.
++ + set JAVAC to prevent FTBFS due to incorrect use of 'gcj', when detected.
++ + verbose mode for 'cp' and 'rm' commands.
++ + build with "--as-needed" to minimise needless linking.
++ * control:
++ + lintian/duplicate-short-description + consistent capitalisation.
++ + removed needless versioned dependencies from Build-Depends.
++ + added myself to Uploaders.
+
- ceph (12.1.1-1) stable; urgency=medium
++ -- Dmitry Smirnov <onlyjob@debian.org> Tue, 25 Mar 2014 07:17:40 +1100
+
- * New upstream release
++ceph (0.78-1) experimental; urgency=medium
++
++ * New upstream release:
++ - d/control: Add "xfslib-dev" and "libblkid-dev" to BD's.
++ - d/*: Sync relevant packaging changes from upstream.
++ - d/p/*: Drop upstreamed patches.
++ - d/p/modules.patch: Mark libcls_user.so and libec_jerasure.so as modules.
++ - d/ceph.install: Only install libec_jerasure.so.
++ * d/ceph-test.install: Install test binaries to /usr/lib/ceph/bin; they
++ really don't need to be installed on the default path.
++ * d/{ceph|radosgw|ceph-mds}.lintian-overrides: Add overrides for intentional
++ difference in naming and structure between upstart configurations and
++ init.d scripts.
++
++ -- James Page <james.page@ubuntu.com> Sat, 22 Mar 2014 18:27:40 +0000
++
++ceph (0.72.2-3) unstable; urgency=medium
++
++ * Team upload.
+
- -- Ceph Release Team <ceph-maintainers@ceph.com> Mon, 17 Jul 2017 16:55:59 +0000
++ [ James Page ]
++ * d/ceph-test.install: Install test binaries to /usr/lib/ceph/bin; they
++ really don't need to be installed on the default path.
++
++ [ Dmitry Smirnov ]
++ * Tightened shlibs with "dh_makeshlibs -V" (Closes: #679686).
++
++ -- Dmitry Smirnov <onlyjob@debian.org> Mon, 14 Apr 2014 17:28:20 +1000
++
++ceph (0.72.2-2) unstable; urgency=medium
++
++ * d/radosgw.{postinst,postrm,preinst}: Handle renaming of radosgw
++ upstart configuration on upgrade@0.72.1-3.
++ * d/{ceph|ceph-mds|radosgw}.{postinst|prerm}: Check to ensure that system
++ is running upstart before trying to start/stop upstart configurations
++ (Closes: #734241, #738845, #738845).
++
++ -- James Page <jamespage@debian.org> Sat, 08 Mar 2014 16:48:28 +0000
++
++ceph (0.72.2-1) unstable; urgency=medium
+
- ceph (12.1.0-1) stable; urgency=medium
-
- * New upstream release
-
- -- Ceph Release Team <ceph-maintainers@ceph.com> Thu, 22 Jun 2017 15:43:47 +0000
-
- ceph (12.0.3-1) stable; urgency=medium
-
- * New upstream release
-
- -- Ceph Release Team <ceph-maintainers@ceph.com> Tue, 16 May 2017 12:42:53 +0000
-
- ceph (12.0.2-1) stable; urgency=medium
-
- * New upstream release
-
- -- Ceph Release Team <ceph-maintainers@ceph.com> Thu, 20 Apr 2017 19:59:57 +0000
-
- ceph (12.0.1-1) stable; urgency=medium
-
- * New upstream release
-
- -- Ceph Release Team <ceph-maintainers@ceph.com> Fri, 24 Mar 2017 15:47:57 +0000
-
- ceph (12.0.0-1) stable; urgency=medium
-
- * New upstream release
-
- -- Ceph Release Team <ceph-maintainers@ceph.com> Wed, 08 Feb 2017 13:57:30 +0000
-
- ceph (11.1.0-1) stable; urgency=medium
-
- * New upstream release
-
- -- Alfredo Deza <adeza@redhat.com> Mon, 12 Dec 2016 18:27:51 +0000
-
- ceph (11.0.2-1) stable; urgency=medium
-
- * New upstream release
-
- -- Alfredo Deza <adeza@redhat.com> Mon, 17 Oct 2016 11:16:49 +0000
-
- ceph (11.0.1-1) stable; urgency=medium
-
- * New upstream release
-
- -- Alfredo Deza <adeza@redhat.com> Tue, 11 Oct 2016 16:27:56 +0000
-
- ceph (11.0.0-1) stable; urgency=low
-
- * New upstream release
-
- -- Sage Weil <sage@newdream.net> Tue, 28 Jun 2016 11:41:16 -0400
-
- ceph (10.2.0-1) stable; urgency=medium
-
- * New upstream release
-
- -- Alfredo Deza <adeza@redhat.com> Wed, 20 Apr 2016 11:29:47 +0000
-
- ceph (10.1.2-1) stable; urgency=medium
-
- * New upstream release
-
- -- Alfredo Deza <adeza@redhat.com> Tue, 12 Apr 2016 17:42:55 +0000
-
- ceph (10.1.1-1) stable; urgency=medium
-
- * New upstream release
-
- -- Alfredo Deza <adeza@redhat.com> Wed, 06 Apr 2016 00:45:18 +0000
-
- ceph (10.1.0-1) stable; urgency=medium
-
- * New upstream release
-
- -- Alfredo Deza <adeza@redhat.com> Thu, 24 Mar 2016 10:53:47 +0000
-
- ceph (10.0.5) stable; urgency=low
-
- * New upstream release (just fixing changelog)
-
- -- Sage Weil <sage@newdream.net> Fri, 11 Mar 2016 12:04:26 -0500
-
- ceph (10.0.4) stable; urgency=low
-
- * New upstream release
-
- -- Sage Weil <sage@newdream.net> Thu, 03 Mar 2016 13:34:18 -0500
-
- ceph (10.0.3) stable; urgency=low
-
- * New upstream release
-
- -- Sage Weil <sage@newdream.net> Mon, 08 Feb 2016 17:10:25 -0500
-
- ceph (10.0.2-1) stable; urgency=low
-
- * New upstream release
-
- -- Alfredo Deza <adeza@redhat.com> Wed, 13 Jan 2016 16:22:26 +0000
-
- ceph (10.0.1-1) stable; urgency=low
-
- * New upstream release
-
- -- Alfredo Deza <adeza@redhat.com> Mon, 14 Dec 2015 23:48:54 +0000
-
- ceph (10.0.0-1) stable; urgency=low
-
- * New upstream release
-
- -- Alfredo Deza <adeza@redhat.com> Mon, 16 Nov 2015 21:41:53 +0000
-
- ceph (9.2.0-1) stable; urgency=low
-
- * New upstream release
-
- -- Jenkins Build Slave User <jenkins-build@jenkins-slave-wheezy.localdomain> Tue, 03 Nov 2015 16:58:32 +0000
-
- ceph (9.1.0-1) stable; urgency=low
-
- * New upstream release
-
- -- Alfredo Deza <adeza@redhat.com> Tue, 13 Oct 2015 05:56:36 -0700
-
- ceph (9.0.3-1) stable; urgency=low
-
- * New upstream release
-
- -- Alfredo Deza <adeza@redhat.com> Fri, 21 Aug 2015 12:46:31 -0700
-
- ceph (9.0.2-1) stable; urgency=low
-
- * New upstream release
-
- -- Alfredo Deza <adeza@redhat.com> Tue, 14 Jul 2015 13:10:31 -0700
-
- ceph (9.0.1-1) stable; urgency=low
-
- * New upstream release
-
- -- Alfredo Deza <adeza@redhat.com> Fri, 05 Jun 2015 10:59:02 -0700
-
- ceph (9.0.0-1) stable; urgency=low
-
- * New upstream release
-
- -- Alfredo Deza <adeza@redhat.com> Mon, 04 May 2015 12:32:58 -0700
-
- ceph (0.94-1) stable; urgency=low
-
- * New upstream release
-
- -- Alfredo Deza <adeza@redhat.com> Tue, 07 Apr 2015 10:05:40 -0700
-
- ceph (0.93-1) stable; urgency=low
-
- * New upstream release
-
- -- Alfredo Deza <adeza@redhat.com> Fri, 27 Feb 2015 09:52:53 -0800
-
- ceph (0.92-1) stable; urgency=low
-
- * New upstream release
-
- -- Alfredo Deza <adeza@redhat.com> Mon, 02 Feb 2015 10:35:27 -0800
-
- ceph (0.91-1) stable; urgency=low
-
- * New upstream release
-
- -- Alfredo Deza <adeza@redhat.com> Tue, 13 Jan 2015 12:10:22 -0800
-
- ceph (0.90-1) stable; urgency=low
-
- * New upstream release
-
- -- Alfredo Deza <adeza@redhat.com> Fri, 19 Dec 2014 06:56:22 -0800
-
- ceph (0.89-1) stable; urgency=low
-
- * New upstream release
-
- -- Alfredo Deza <adeza@redhat.com> Wed, 03 Dec 2014 08:18:33 -0800
-
- ceph (0.88-1) stable; urgency=low
-
- * New upstream release
-
- -- Alfredo Deza <adeza@redhat.com> Tue, 11 Nov 2014 09:33:12 -0800
-
- ceph (0.87-1) stable; urgency=low
-
- * New upstream release
-
- -- Alfredo Deza <alfredo.deza@inktank.com> Wed, 29 Oct 2014 11:03:55 -0700
-
- ceph (0.86-1) stable; urgency=low
-
- * New upstream release
-
- -- Alfredo Deza <alfredo.deza@inktank.com> Tue, 07 Oct 2014 06:20:21 -0700
-
- ceph (0.85-1) stable; urgency=low
-
- * Development release
-
- -- Alfredo Deza <alfredo.deza@inktank.com> Mon, 08 Sep 2014 06:31:31 -0700
-
- ceph (0.84-1) stable; urgency=low
-
- * Development release
-
- -- Alfredo Deza <alfredo.deza@inktank.com> Mon, 18 Aug 2014 09:02:20 -0700
-
- ceph (0.83-1) stable; urgency=low
-
- * Development release
-
- -- Alfredo Deza <alfredo.deza@inktank.com> Tue, 29 Jul 2014 13:42:53 -0700
-
- ceph (0.82-1) stable; urgency=low
-
- * Development release
-
- -- Alfredo Deza <alfredo.deza@inktank.com> Wed, 25 Jun 2014 16:47:51 +0000
-
- ceph (0.81-1) stable; urgency=low
-
- * Development release
-
- -- Alfredo Deza <alfredo.deza@inktank.com> Mon, 02 Jun 2014 18:37:27 +0000
-
- ceph (0.80-1) stable; urgency=low
-
- * New upsream release
-
- -- Alfredo Deza <alfredo.deza@inktank.com> Tue, 06 May 2014 14:03:27 +0000
-
- ceph (0.80-rc1-1) stable; urgency=low
-
- * New upsream release
-
- -- Alfredo Deza <alfredo.deza@inktank.com> Tue, 22 Apr 2014 21:21:44 +0000
-
- ceph (0.79-1) stable; urgency=low
-
- * New upsream release
-
- -- Alfredo Deza <alfredo.deza@inktank.com> Mon, 07 Apr 2014 16:48:36 +0000
-
- ceph (0.78-1) stable; urgency=low
-
- * New upsream release
-
- -- Alfredo Deza <alfredo.deza@inktank.com> Fri, 21 Mar 2014 22:05:12 +0000
-
- ceph (0.77-1) stable; urgency=low
-
- * New upstream release
-
- -- Ken Dreyer <ken.dreyer@inktank.com> Wed, 19 Feb 2014 22:54:06 +0000
-
- ceph (0.76-1) stable; urgency=low
-
- * New upstream release
-
- -- Ken Dreyer <kdreyer@jenkins.front.sepia.ceph.com> Mon, 03 Feb 2014 18:14:59 +0000
-
- ceph (0.75-1) stable; urgency=low
-
- * New upstream release
-
- -- Ken Dreyer <kdreyer@jenkins.front.sepia.ceph.com> Mon, 13 Jan 2014 21:05:07 +0000
-
- ceph (0.74-1) stable; urgency=low
-
- * New upstream release
-
- -- Gary Lowell <glowell@jenkins.front.sepia.ceph.com> Mon, 30 Dec 2013 21:02:35 +0000
-
- ceph (0.73-1) precise; urgency=low
-
- * New upstream release
-
- -- Gary Lowell <gary.lowell@inktank.com> Tue, 10 Dec 2013 04:55:06 +0000
-
- ceph (0.72-1) stable; urgency=low
-
- * New upstream release
-
- -- Gary Lowell <gary.lowell@inktank.com> Thu, 07 Nov 2013 20:25:18 +0000
-
- ceph (0.72-rc1-1) stable; urgency=low
-
- * New upstream release
-
- -- Gary Lowell <gary.lowell@inktank.com> Wed, 30 Oct 2013 00:44:25 +0000
-
- ceph (0.71-1) stable; urgency=low
-
- * New upstream release
-
- -- Gary Lowell <gary.lowell@inktank.com> Thu, 17 Oct 2013 09:19:02 +0000
-
- ceph (0.70-1) stable; urgency=low
-
- * New upstream release
-
- -- Gary Lowell <gary.lowell@inktank.com> Fri, 04 Oct 2013 20:11:51 +0000
-
- ceph (0.69-1) precise; urgency=low
-
- * New upstream release
-
- -- Gary Lowell <gary.lowell@inktank.com> Wed, 18 Sep 2013 01:39:47 +0000
-
- ceph (0.68-1) precise; urgency=low
-
- * New upstream release
-
- -- Gary Lowell <gary.lowell@inktank.com> Tue, 03 Sep 2013 16:10:11 -0700
-
- ceph (0.67-1) precise; urgency=low
-
- * New upstream release
-
- -- Gary Lowell <gary.lowell@inktank.com> Tue, 13 Aug 2013 10:44:30 -0700
-
- ceph (0.67-rc3-1) precise; urgency=low
-
- * New upstream release
-
- -- Gary Lowell <gary.lowell@inktank.com> Tue, 30 Jul 2013 14:37:40 -0700
-
- ceph (0.67-rc2-1) precise; urgency=low
-
- * New upstream release
-
- -- Gary Lowell <gary.lowell@inktank.com> Wed, 24 Jul 2013 16:18:33 -0700
-
- ceph (0.67-rc1-1) precise; urgency=low
-
- * New upstream release
-
- -- Gary Lowell <gary.lowell@inktank.com> Mon, 22 Jul 2013 11:57:01 -0700
-
- ceph (0.66-1) precise; urgency=low
-
- * New upstream release
-
- -- Gary Lowell <gary.lowell@inktank.com> Mon, 08 Jul 2013 15:44:45 -0700
-
- ceph (0.65-1) precise; urgency=low
-
- * New upstream release
-
- -- Gary Lowell <gary.lowell@inktank.com> Tue, 25 Jun 2013 09:19:14 -0700
-
- ceph (0.64-1) precise; urgency=low
-
- * New upstream release
-
- -- Gary Lowell <gary.lowell@inktank.com> Wed, 12 Jun 2013 09:53:54 -0700
-
- ceph (0.63-1) precise; urgency=low
-
- * New upstream release
-
- -- Gary Lowell <gary.lowell@inktank.com> Tue, 28 May 2013 13:57:53 -0700
-
- ceph (0.62) precise; urgency=low
-
- * New upstream release
-
- -- Gary Lowell <gary.lowell@inktank.com> Tue, 14 May 2013 09:08:21 -0700
-
- ceph (0.61-1) precise; urgency=low
-
- * New upstream release
-
- -- Gary Lowell <gary.lowell@inktank.com> Mon, 06 May 2013 13:18:43 -0700
-
- ceph (0.60-1) precise; urgency=low
-
- * New upstream release
-
- -- Gary Lowell <gary.lowell@inktank.com> Mon, 01 Apr 2013 12:22:30 -0700
-
- ceph (0.59-1) precise; urgency=low
-
- * New upstream release
-
- -- Gary Lowell <gary.lowell@inktank.com> Tue, 19 Mar 2013 22:26:37 -0700
-
- ceph (0.58-1) precise; urgency=low
-
- * New upstream release
-
- -- Gary Lowell <gary.lowell@inktank.com> Mon, 04 Mar 2013 15:17:58 -0800
-
- ceph (0.57-1) quantal; urgency=low
-
- * New upstream release
-
- -- Gary Lowell <gary.lowell@inktank.com> Tue, 19 Feb 2013 10:06:39 -0800
-
- ceph (0.56-1) quantal; urgency=low
-
- * New upstream release
-
- -- Gary Lowell <gary.lowell@inktank.com> Mon, 31 Dec 2012 17:08:45 -0800
-
- ceph (0.55.1-1) precise; urgency=low
-
- * New upstream release
-
- -- Gary Lowell <gary.lowell@inktank.com> Wed, 12 Dec 2012 16:24:13 -0800
-
- ceph (0.55-1) precise; urgency=low
-
- * New upstream release
-
- -- Gary Lowell <gary.lowell@inktank.com> Mon, 03 Dec 2012 19:08:14 -0800
-
- ceph (0.54-1) precise; urgency=low
-
- * New upstream release
-
- -- Gary Lowell <gary.lowell@inktank.com> Tue, 13 Nov 2012 13:17:19 -0800
-
- ceph (0.53-1) precise; urgency=low
-
- * New upstream release
-
- -- Gary Lowell <gary.lowell@inktank.com> Tue, 16 Oct 2012 17:40:46 +0000
-
- ceph (0.52-1) precise; urgency=low
-
- * New upstream release
-
- -- Ubuntu <gary.lowell@inktank.com> Thu, 27 Sep 2012 16:16:52 +0000
-
- ceph (0.51-1) experimental; urgency=low
-
- * New upstream release
-
- -- Sage Weil <sage@newdream.net> Sat, 25 Aug 2012 15:58:23 -0700
-
- ceph (0.50-1) experimental; urgency=low
-
- * New upstream release
-
- -- Sage Weil <sage@newdream.net> Mon, 13 Aug 2012 09:44:40 -0700
-
- ceph (0.49-1) experimental; urgency=low
-
- * New upstream release
-
- -- Sage Weil <sage@newdream.net> Fri, 20 Jul 2012 23:26:43 -0700
-
- ceph (0.48argonaut-1) experimental; urgency=low
-
- * New upstream release
-
- -- Sage Weil <sage@newdream.net> Sat, 30 Jun 2012 14:49:30 -0700
-
- ceph (0.47.3-1) experimental; urgency=low
-
- * New upstream release
-
- -- Sage Weil <sage@newdream.net> Wed, 20 Jun 2012 10:57:03 -0700
-
- ceph (0.47.2-1) experimental; urgency=low
-
- * New upstream release
-
- -- Sage Weil <sage@newdream.net> Wed, 23 May 2012 09:00:43 -0700
-
- ceph (0.47.1-1) experimental; urgency=low
-
- * New upstream release
-
- -- Sage Weil <sage@newdream.net> Mon, 21 May 2012 14:28:30 -0700
-
- ceph (0.47-1) experimental; urgency=low
-
- * New upstream release
-
- -- Sage Weil <sage@newdream.net> Sun, 20 May 2012 15:16:03 -0700
-
- ceph (0.46-1) experimental; urgency=low
-
- * New upstream release
-
- -- Sage Weil <sage@newdream.net> Sun, 29 Apr 2012 21:21:01 -0700
-
- ceph (0.45-1) experimental; urgency=low
-
- * New upstream release
-
- -- Sage Weil <sage@newdream.net> Tue, 10 Apr 2012 10:41:57 -0700
-
- ceph (0.44.2-1) experimental; urgency=low
-
- * New upstream release
-
- -- Sage Weil <sage@newdream.net> Thu, 05 Apr 2012 14:54:17 -0700
-
- ceph (0.44.1-1) experimental; urgency=low
-
- * New upstream release
-
- -- Sage Weil <sage@newdream.net> Tue, 27 Mar 2012 13:02:00 -0700
-
- ceph (0.44-1) experimental; urgency=low
-
- * New upstream release
-
- -- Sage Weil <sage@newdream.net> Sun, 18 Mar 2012 12:03:38 -0700
-
- ceph (0.43-1) experimental; urgency=low
-
- * New upstream release
-
- -- Sage Weil <sage@newdream.net> Fri, 02 Mar 2012 08:53:10 -0800
-
- ceph (0.42.2-1) experimental; urgency=low
-
- * New upstream release
-
- -- Sage Weil <sage@newdream.net> Fri, 24 Feb 2012 12:59:38 -0800
-
- ceph (0.42.1-1) experimental; urgency=low
-
- * New upstream release
-
- -- Sage Weil <sage@newdream.net> Thu, 23 Feb 2012 18:46:23 -0800
-
- ceph (0.42-1) experimental; urgency=low
-
- * New upstream relese
-
- -- Sage Weil <sage@newdream.net> Sun, 19 Feb 2012 15:30:20 -0800
-
- ceph (0.41-1) experimental; urgency=low
-
- * New upstream release
-
- -- Sage Weil <sage@newdream.net> Fri, 27 Jan 2012 10:42:11 -0800
-
- ceph (0.40-1) experimental; urgency=low
-
- * New upstream release
-
- -- Sage Weil <sage@newdream.net> Fri, 13 Jan 2012 08:36:02 -0800
-
- ceph (0.39-1) experimental; urgency=low
-
- * New upstream release
-
- -- Sage Weil <sage@newdream.net> Fri, 02 Dec 2011 09:01:20 -0800
-
- ceph (0.38-1) experimental; urgency=low
-
- * New upstream release
-
- -- Sage Weil <sage@newdream.net> Thu, 10 Nov 2011 15:06:44 -0800
-
- ceph (0.37-1) experimental; urgency=low
-
- * New upstream release
-
- -- Sage Weil <sage@newdream.net> Mon, 17 Oct 2011 08:35:42 -0700
-
- ceph (0.36-1) experimental; urgency=low
-
- * New upstream release
-
- -- Sage Weil <sage@newdream.net> Fri, 30 Sep 2011 09:29:29 -0700
-
- ceph (0.35-1) experimental; urgency=low
-
- * New upstream release
-
- -- Sage Weil <sage@newdream.net> Wed, 21 Sep 2011 09:36:03 -0700
-
- ceph (0.34-1) experimental; urgency=low
-
- * New upstream release
-
- -- Sage Weil <sage@newdream.net> Fri, 26 Aug 2011 21:48:35 -0700
++ * New upstream release.
+
- ceph (0.33-1) experimental; urgency=low
++ -- James Page <james.page@ubuntu.com> Wed, 01 Jan 2014 09:32:03 +0000
++
++ceph (0.72.1-3) unstable; urgency=low
++
++ * d/rules,ceph.install: Correct install paths for ceph-* helpers.
++ * d/p/modules: Mark libcls_kvs.so as module.
++ * d/rules: Rename radosgw upstart configuration to radosgw-instance to
++ avoid namespace conflict with init script which breaks backwards
++ compatibility (LP: #1255464).
++
++ -- James Page <james.page@ubuntu.com> Wed, 27 Nov 2013 10:52:48 +0000
++
++ceph (0.72.1-2) unstable; urgency=low
++
++ * Fix upgrade failures from ceph < 0.67.3-1 (Closes: #728164):
++ - d/control: ceph-mds Breaks/Replaces ceph (<< 0.67.3-1).
++ - d/control: ceph-fs-common Breaks/Replaces ceph-common (<< 0.67.3-1).
++ * d/rules,control: Use google-perftools on armhf and powerpc archs.
++
++ -- James Page <james.page@ubuntu.com> Mon, 25 Nov 2013 10:13:19 +0000
++
++ceph (0.72.1-1) unstable; urgency=low
++
++ * New upstream stable release:
++ - d/ceph-test.install: Add new ceph_filestore_tool, ceph-kvstore-tool
++ and ceph_test_cls_hello binaries, drop ceph_test_store_tool.
++ - d/ceph-common.install: Add new ceph-post-file binary and manpage.
++ - d/ceph.install: Tweaked install path /usr/sbin -> /sbin.
++ - d/control: Add new BD's on python-nose and yasm.
++ - d/copyright: Updates inline with changes in codebase.
++ - d/ceph.install,rules: Install rbdmap init file using dh_installinit.
++ - Refresh patches.
++ * d/control,rules: Disable unit testing; it requires a forked version of
++ cram and is still trying to download dependencies using virtualenv.
++
++ -- James Page <james.page@ubuntu.com> Fri, 22 Nov 2013 13:02:29 +0000
++
++ceph (0.67.3-1) unstable; urgency=low
++
++ [ Laszlo Boszormenyi ]
++ * New upstream release (Closes: #693866, #705262).
++ * Update debian/copyright.
++ * Sync with Ubuntu.
++
++ [ James Page ]
++ * d/control,rules,libcephfs-{java,jni}: Enable Java CephFS library,
++ add new BD's on javahelper and default-jdk, add dbg package.
++ * d/control: Add new BD on libboost-thread-dev for RADOS Gateway
++ keystone integration.
++ * d/{control,obsync.install}: Drop obsync package inline with
++ upstream.
++ * d/librbd-dev.install: Pickup new features.h file.
++ * Remove manual calls to ldconfig:
++ - d/lib{rados2|rbd1|cephfs1}.post*: Dropped - all these do is call
++ ldconfig which will automatically be done.
++ - d/rules: Let dh_makeshlibs do its magic with postinst/postrm.
++ * d/tests/*: Added autopkgtests for librbd, librados, python-ceph
++ and the ceph CLI.
++ * d/control: Fix versions of librbd1, librados2 and libcephfs1 for
++ python-ceph as it requires an exact version match.
++ * d/ceph.docs: Drop - README from upstream is only useful for developers
++ (Closes: #722957).
++ * d/rules: Drop --upstart-only from dh_installinit calls for upstart
++ configurations; this is deprecated in Ubuntu and not support in Debian.
++ * d/rules: Exclude jni package from shlibs generation to avoid pointless
++ ldconfig calls in maintainer scripts.
++
++ [ Bastian Blank ]
++ * Use debhelper 9.
++ * Use dh-autoreconf.
++ * Install files from source tree if possible.
++ * Run test-suite:
++ - Build-depend on python-virtualenv.
++ - Ask virtualenv to never download anything.
++ * Fix clean target.
++ * Properly mark library modules:
++ - Don't longer exclude them from stripping.
++ * Drop all libtool .la files.
++ * Generate python dependencies.
++ * Don't exclude stuff from shlibs generation.
++
++ -- Laszlo Boszormenyi (GCS) <gcs@debian.org> Tue, 01 Oct 2013 02:29:08 +0200
++
++ceph (0.48-1) unstable; urgency=low
++
++ * New upstream release, the first with long-term support.
++ * As gceph dropped by upstream, remove it from packaging.
++ * Build with hardening enabled and build-conflict with libcryptopp not to
++ mix up with libnss.
++ * Use symbol versioning (closes: #679686).
++ * Update debian/watch to GitHub tags.
++
++ -- Laszlo Boszormenyi (GCS) <gcs@debian.hu> Sat, 07 Jul 2012 07:53:40 +0200
++
++ceph (0.47.2-1) unstable; urgency=low
+
- * New upstream release.
++ * New upstream release.
++ * Use system leveldb (closes: #667907).
++ * Remove librgw1 , librgw-dev and librgw1-dbg and add rest-bench and
++ rest-bench-dbg packages.
++ * Backport leveldb build fixes from upstream git as
++ fix_leveldb_dep_for_system_library_case.patch and
++ fix_leveldb_includes_for_system_library_case.patch .
++ * Update packaging.
++ * Sync with Ubuntu: switch build-dependency from libcryptopp to libnss as
++ libcryptopp is not seeded.
+
- -- Sage Weil <sage@newdream.net> Mon, 15 Aug 2011 16:42:07 -0700
++ -- Laszlo Boszormenyi (GCS) <gcs@debian.hu> Sun, 03 Jun 2012 13:37:52 +0200
+
- ceph (0.32-1) experimental; urgency=low
++ceph (0.44.1-1) unstable; urgency=low
+
- * New upstream release
++ * New upstream release.
+
- -- Sage Weil <sage@newdream.net> Fri, 29 Jul 2011 21:42:08 -0700
++ -- Laszlo Boszormenyi (GCS) <gcs@debian.hu> Fri, 06 Apr 2012 01:10:15 +0200
+
- ceph (0.30-1) experimental; urgency=low
++ceph (0.43-1) unstable; urgency=low
+
- * New upstream release
++ * New upstream release, now creates /var/run/ceph on each start
++ (closes: #660238).
++ * Update debian/copyright .
+
- -- Sage Weil <sage@newdream.net> Mon, 27 Jun 2011 20:06:06 -0700
++ -- Laszlo Boszormenyi (GCS) <gcs@debian.hu> Sun, 26 Feb 2012 04:07:02 +0100
+
- ceph (0.29.1-1) experimental; urgency=low
++ceph (0.41-1) unstable; urgency=low
+
- * New upstream release
++ * New upstream release.
+
- -- Sage Weil <sage@newdream.net> Thu, 16 Jun 2011 13:10:47 -0700
++ -- Laszlo Boszormenyi (GCS) <gcs@debian.hu> Sun, 05 Feb 2012 10:07:38 +0100
+
- ceph (0.29-1) experimental; urgency=low
++ceph (0.40-1) unstable; urgency=low
+
- * New upstream release
++ * New upstream release (closes: #652037).
++ * Adjust copyright to match upstream source changes.
+
- -- Sage Weil <sage@newdream.net> Mon, 06 Jun 2011 09:59:25 -0700
++ -- Laszlo Boszormenyi (GCS) <gcs@debian.hu> Sat, 14 Jan 2012 12:01:30 +0100
+
- ceph (0.28.2-1) experimental; urgency=low
++ceph (0.38-1) unstable; urgency=low
+
- * New upstream release.
++ * New upstream release (closes: #647764), missingok is now part of logrotate
++ directives (closes: #645651).
++ * Rename ceph-client-tools package to ceph-common , libceph-dev to
++ libcephfs-dev and libceph1{,-dbg} ones to libcephfs1{,-dbg} respectively.
++ * Update upstream VCS locations.
+
- -- Sage Weil <sage@newdream.net> Sat, 28 May 2011 09:14:17 -0700
++ -- Laszlo Boszormenyi (GCS) <gcs@debian.hu> Sun, 27 Nov 2011 21:40:52 +0100
+
- ceph (0.28.1-1) experimental; urgency=low
++ceph (0.35-1) unstable; urgency=low
+
- * New upstream release.
++ * New upstream release.
+
- -- Sage Weil <sage@newdream.net> Mon, 23 May 2011 21:11:30 -0700
++ -- Laszlo Boszormenyi (GCS) <gcs@debian.hu> Sat, 24 Sep 2011 16:51:57 +0200
+
- ceph (0.28-1) experimental; urgency=low
++ceph (0.34-1) unstable; urgency=low
+
- * New upstream release.
++ * New upstream release (closes: #638714).
++ * Make librbd-dev depends on librados-dev as it uses headers from the latter
++ (closes: #636845).
++ * Add new binary packages, gceph, gceph-dbg and obsync . The libcrush ones
++ removed.
++ * Change to quilt source format and tune packaging.
+
- -- Sage Weil <sage@newdream.net> Tue, 17 May 2011 18:03:11 -0700
++ -- Laszlo Boszormenyi (GCS) <gcs@debian.hu> Sun, 28 Aug 2011 15:56:16 +0200
+
- ceph (0.27.1-1) experimental; urgency=low
++ceph (0.27-1.1) unstable; urgency=low
+
- * New upstream release.
++ * Non-maintainer upload.
++ * Remove references to other libraries from dependency_libs field
++ (closes: #621208).
+
- -- Sage Weil <sage@newdream.net> Thu, 05 May 2011 13:42:06 -0700
++ -- Luk Claes <luk@debian.org> Sat, 28 May 2011 22:28:48 +0200
+
- ceph (0.27-1) experimental; urgency=low
++ceph (0.27-1) unstable; urgency=low
+
- * New upstream release.
++ * New upstream release.
+
- -- Sage Weil <sage@newdream.net> Fri, 22 Apr 2011 16:51:49 -0700
++ -- Laszlo Boszormenyi (GCS) <gcs@debian.hu> Mon, 25 Apr 2011 10:09:05 +0200
+
- ceph (0.26-1) experimental; urgency=low
++ceph (0.25.2-1) unstable; urgency=low
+
+ * New upstream release.
- * Make Ceph Linux only and build on all Linux archs (closes: #614890),
- but only build-depend google-perftools on x86 and x64 archs only.
- * Correct section of libcrush1, librados1, librbd1 and libceph1 to libs.
+ * Make Ceph cross buildable (closes: #618939), thanks to Hector Oron.
+ * Disable libatomic-ops on ARMv4t (armel) archs to prevent FTBFS
+ (closes: #615235), thanks go to Hector Oron again.
+ * Rename librados1{,-dbg,-dev} packages to librados2{,-dbg,-dev} ones;
+ conflict with and replace the former ones.
++ * Add librbd1 and librbd-dev packages.
+
- -- Laszlo Boszormenyi (GCS) <gcs@debian.hu> Fri, 01 Apr 2011 16:28:11 +0100
-
- ceph (0.25.2-1) experimental; urgency=low
-
- * New upstream release
-
- -- Sage Weil <sage@newdream.net> Sun, 20 Mar 2011 21:07:38 -0700
-
- ceph (0.25.1-1) experimental; urgency=low
-
- * New upstream release
-
- -- Sage Weil <sage@newdream.net> Mon, 14 Mar 2011 14:43:47 -0700
++ -- Laszlo Boszormenyi (GCS) <gcs@debian.hu> Sun, 27 Mar 2011 15:51:23 +0200
+
- ceph (0.25-1) experimental; urgency=low
++ceph (0.24.3-2) unstable; urgency=low
+
- * New upstream release
++ * Make Ceph Linux only and build on all Linux archs (closes: #614890).
++ * Support parallel building via DEB_BUILD_OPTIONS .
++ * Add watch file, thanks to Clint Byrum (closes: #615021).
++ * Tune packaging.
+
- -- Sage Weil <sage@newdream.net> Fri, 04 Mar 2011 14:39:54 -0800
++ -- Laszlo Boszormenyi (GCS) <gcs@debian.hu> Fri, 25 Feb 2011 15:17:26 +0100
+
- ceph (0.24.3-1) experimental; urgency=low
++ceph (0.24.3-1) unstable; urgency=low
+
- * New upstream release
++ * New upstream bugfix release.
+
- -- Sage Weil <sage@newdream.net> Thu, 10 Feb 2011 09:14:00 -0800
++ -- Laszlo Boszormenyi (GCS) <gcs@debian.hu> Sat, 19 Feb 2011 12:25:43 +0100
+
- ceph (0.24.2-1) experimental; urgency=low
++ceph (0.24.2-1) unstable; urgency=low
+
- * New upstream release.
++ * New upstream bugfix release.
+
- -- Sage Weil <sage@newdream.net> Mon, 24 Jan 2011 11:02:24 -0800
++ -- Laszlo Boszormenyi (GCS) <gcs@debian.hu> Sat, 29 Jan 2011 15:25:14 +0100
+
- ceph (0.24.1-1) experimental; urgency=low
++ceph (0.24.1-1) unstable; urgency=low
+
- * New upstream release.
++ * New upstream bugfix release.
+
- -- Sage Weil <sage@newdream.net> Fri, 07 Jan 2011 16:49:48 -0800
++ -- Laszlo Boszormenyi (GCS) <gcs@debian.hu> Tue, 11 Jan 2011 22:23:18 +0100
+
- ceph (0.24-1) experimental; urgency=low
++ceph (0.24-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Laszlo Boszormenyi (GCS) <gcs@debian.hu> Wed, 01 Dec 2010 09:26:25 -0800
+
+ceph (0.23.1-1) experimental; urgency=low
+
+ * Initial release (Closes: #506040)
+
+ -- Sage Weil <sage@newdream.net> Sun, 21 Nov 2010 15:22:21 -0800
--- /dev/null
--- /dev/null
++src/rocksdb/util/build_version.cc
++src/pybind/*.pyc
++src/test/pybind/*.pyc
--- /dev/null
- Homepage: http://ceph.com/
- Vcs-Git: git://github.com/ceph/ceph.git
- Vcs-Browser: https://github.com/ceph/ceph
+Source: ceph
+Section: admin
+Priority: optional
- Uploaders: Ken Dreyer <kdreyer@redhat.com>,
- Alfredo Deza <adeza@redhat.com>,
- Build-Depends: bc,
- btrfs-tools,
+Maintainer: Ceph Maintainers <ceph-maintainers@lists.ceph.com>
- cpio,
- cryptsetup-bin | cryptsetup,
++Uploaders: James Page <jamespage@debian.org>,
++ Gaudenz Steinlin <gaudenz@debian.org>,
++Homepage: http://ceph.com/
++Vcs-Git: https://salsa.debian.org/ceph-team/ceph.git
++Vcs-Browser: https://salsa.debian.org/ceph-team/ceph
++Build-Depends: chrpath,
+ cmake,
- debhelper (>= 9),
+ cython,
+ cython3,
- dh-systemd,
- gdisk,
- git,
++ debhelper (>= 10),
+ default-jdk,
+ dh-exec,
+ dh-python,
- jq,
+ gperf,
+ javahelper,
- libblkid-dev (>= 2.17),
- libcurl4-openssl-dev,
+ junit4,
+ libaio-dev,
+ libbabeltrace-ctf-dev,
+ libbabeltrace-dev,
- libgoogle-perftools-dev [i386 amd64 arm64],
++ libblkid-dev,
++ libboost-atomic1.67-dev,
++ libboost-context1.67-dev [i386 amd64 armel armhf arm64 mips mipsel powerpc ppc64el],
++ libboost-coroutine1.67-dev [i386 amd64 armel armhf arm64 mips mipsel powerpc ppc64el],
++ libboost-date-time1.67-dev,
++ libboost-filesystem1.67-dev,
++ libboost-iostreams1.67-dev,
++ libboost-program-options1.67-dev,
++ libboost-python1.67-dev,
++ libboost-random1.67-dev,
++ libboost-regex1.67-dev,
++ libboost-system1.67-dev,
++ libboost-thread1.67-dev,
++ libboost1.67-dev,
++ libbz2-dev,
++ libcurl4-gnutls-dev,
++ libedit-dev,
+ libexpat1-dev,
+ libfuse-dev,
- liblttng-ust-dev,
++ libgoogle-perftools-dev [amd64 arm64 armel armhf i386 mips mips64el mipsel ppc64el s390x],
+ libibverbs-dev,
+ libkeyutils-dev,
+ libldap2-dev,
+ libleveldb-dev,
- parted,
+ libnss3-dev,
+ libsnappy-dev,
+ libssl-dev,
+ libtool,
+ libudev-dev,
+ libxml2-dev,
+ lsb-release,
- python (>= 2.7),
- python-all-dev,
- python-coverage,
+ pkg-config,
- python-six,
- python-sphinx,
++ python-dev,
+ python-cherrypy3,
+ python-nose,
+ python-pecan,
+ python-prettytable,
+ python-setuptools,
- python3-all-dev,
+ python-werkzeug,
- socat,
++ python3-dev,
+ python3-setuptools,
- valgrind,
- virtualenv | python-virtualenv,
++ python3-sphinx,
++ uuid-dev,
+ uuid-runtime,
- xmlstarlet,
++ valgrind [amd64 armhf i386 powerpc],
+ xfslibs-dev,
+ xfsprogs,
- Standards-Version: 3.9.3
+ yasm [amd64],
+ zlib1g-dev,
- Architecture: linux-any
- Depends: ceph-mgr (= ${binary:Version}),
- ceph-mon (= ${binary:Version}),
- ceph-osd (= ${binary:Version}),
- Recommends: ceph-mds (= ${binary:Version}),
++Build-Conflicts: libcrypto++-dev,
++Standards-Version: 4.2.1
+
+Package: ceph
- debianutils,
- findutils,
++Architecture: all
++Depends: ceph-mgr (>= ${source:Version}),
++ ceph-mon (>= ${source:Version}),
++ ceph-osd (>= ${source:Version}),
++ ${misc:Depends},
++Suggests: ceph-mds (= ${binary:Version}),
+Description: distributed storage and file system
+ Ceph is a massively scalable, open-source, distributed
+ storage system that runs on commodity hardware and delivers object,
+ block and file system storage.
++ .
++ This package is a metapackage to install most Ceph server components.
+
+Package: ceph-base
+Architecture: linux-any
+Depends: binutils,
+ ceph-common (= ${binary:Version}),
+ cryptsetup-bin | cryptsetup,
- grep,
- logrotate,
- psmisc,
+ gdisk,
- Recommends: btrfs-tools,
- ceph-mds (= ${binary:Version}),
- librados2 (= ${binary:Version}),
- libradosstriper1 (= ${binary:Version}),
- librbd1 (= ${binary:Version}),
++ hdparm | sdparm,
++ lsb-base,
++ parted,
++ uuid-runtime,
+ xfsprogs,
+ ${misc:Depends},
+ ${python:Depends},
+ ${shlibs:Depends},
- Replaces: ceph (<< 10),
- ceph-common (<< 0.78-500),
- ceph-test (<< 12.2.2-14),
- python-ceph (<< 0.92-1223),
- Breaks: ceph (<< 10),
- ceph-test (<< 12.2.2-14),
- python-ceph (<< 0.92-1223),
++Pre-Depends: ${misc:Pre-Depends},
++Breaks: ceph (<< 10.2.7-0exp1),
++ ceph-common (<< 9.2.0-0ubuntu1~),
++ ceph-test (<< 10.2.7-0exp1),
++ python-ceph (<< 0.94.1-1~),
++Replaces: ceph (<< 10.2.7-0exp1),
++ ceph-common (<< 9.2.0-0ubuntu1~),
++ ceph-test (<< 10.2.7-0exp1),
++ python-ceph (<< 0.94.1-1~),
++Recommends: libcephfs2,
++ librados2,
++ libradosstriper1,
++ librbd1,
+ ntp | time-daemon,
- Ceph is a massively scalable, open-source, distributed
- storage system that runs on commodity hardware and delivers object,
- block and file system storage.
++Suggests: logrotate,
+Description: common ceph daemon libraries and management tools
- Package: ceph-base-dbg
++ Ceph is a distributed storage system designed to provide excellent
++ performance, reliability, and scalability.
+ .
+ This package contains the libraries and management tools that are common among
+ the Ceph server daemons (ceph-mon, ceph-mgr, ceph-osd, ceph-mds). These tools
+ are necessary for creating, running, and administering a Ceph storage cluster.
+
- Section: debug
- Priority: extra
- Depends: ceph-base (= ${binary:Version}),
++Package: rbd-mirror
+Architecture: linux-any
- Description: debugging symbols for ceph-base
++Depends: ceph-common (= ${binary:Version}),
++ librados2 (= ${binary:Version}),
+ ${misc:Depends},
- block and file system storage.
- .
- This package contains the libraries and management tools that are common among
- the Ceph server daemons (ceph-mon, ceph-mgr, ceph-osd, ceph-mds). These tools
- are necessary for creating, running, and administering a Ceph storage cluster.
++ ${shlibs:Depends},
++Description: Ceph daemon for mirroring RBD images
++ Ceph is a distributed storage system designed to provide excellent
++ performance, reliability, and scalability.
++ .
++ This package provides a daemon for mirroring RBD images between
++ Ceph clusters, streaming changes asynchronously.
++
++Package: rbd-nbd
++Architecture: linux-any
++Depends: ${misc:Depends},
++ ${shlibs:Depends},
++Description: NBD-based rbd client for the Ceph distributed file system
+ Ceph is a massively scalable, open-source, distributed
+ storage system that runs on commodity hardware and delivers object,
- This package contains the debugging symbols for ceph-base.
++ block and file system storage. This is a
++ NBD-based client that allows one to map Ceph rbd images as local
++ block device.
+ .
- Depends: ceph-base (= ${binary:Version}),
++ NBD base client that allows one to map Ceph rbd images as local
++ block device.
++
++Package: ceph-common
++Architecture: linux-any
++Depends: librbd1 (= ${binary:Version}),
++ python-cephfs (= ${binary:Version}),
++ python-prettytable,
++ python-rados (= ${binary:Version}),
++ python-rbd (= ${binary:Version}),
++ python-requests,
++ python3-prettytable,
++ ${misc:Depends},
++ ${python3:Depends},
++ ${python:Depends},
++ ${shlibs:Depends},
++Conflicts: ceph-client-tools,
++Breaks: ceph (<< 9.2.0-1~),
++ ceph-fs-common (<< 10.2.7-0exp1~),
++ ceph-test (<< 9.2.0-1~),
++ librbd1 (<< 0.94.1-1~),
++ python-ceph (<< 0.94.1-1~),
++ radosgw (<< 10.2.7-0exp1~),
++Replaces: ceph (<< 9.2.0-1~),
++ ceph-client-tools,
++ ceph-fs-common (<< 10.2.7-0exp1~),
++ ceph-test (<< 9.2.0-1~),
++ librbd1 (<< 0.94.1-1~),
++ python-ceph (<< 0.94.1-1~),
++ radosgw (<< 10.2.7-0exp1~),
++Suggests: ceph,
++ ceph-mds,
++Description: common utilities to mount and interact with a ceph storage cluster
++ Ceph is a distributed storage and file system designed to provide
++ excellent performance, reliability, and scalability. This is a collection
++ of common tools that allow one to interact with and administer a Ceph cluster.
+
+Package: ceph-mds
+Architecture: linux-any
- Recommends: ceph-fuse (= ${binary:Version}),
- libcephfs2 (= ${binary:Version}),
- Replaces: ceph (<< 0.93-417),
- Breaks: ceph (<< 0.93-417),
++Depends: ceph,
+ ${misc:Depends},
+ ${shlibs:Depends},
- Ceph is a massively scalable, open-source, distributed
- storage system that runs on commodity hardware and delivers object,
- block and file system storage.
++Recommends: ceph-fuse,
++ libcephfs2,
++Breaks: ceph (<< 0.67.3-1),
++Replaces: ceph (<< 0.67.3-1),
+Description: metadata server for the ceph distributed file system
- Package: ceph-mds-dbg
- Architecture: linux-any
- Section: debug
- Priority: extra
- Depends: ceph-mds (= ${binary:Version}),
- ${misc:Depends},
- Description: debugging symbols for ceph-mds
- Ceph is a massively scalable, open-source, distributed
- storage system that runs on commodity hardware and delivers object,
- block and file system storage.
- .
- This package contains the debugging symbols for ceph-mds.
-
++ Ceph is a distributed storage and network file system designed to
++ provide excellent performance, reliability, and scalability.
+ .
+ This package contains the metadata server daemon, which is used to
+ create a distributed file system on top of the ceph storage cluster.
+
- python-jinja2,
+Package: ceph-mgr
+Architecture: linux-any
+Depends: ceph-base (= ${binary:Version}),
+ python-cherrypy3,
- Replaces: ceph (<< 0.93-417),
- Breaks: ceph (<< 0.93-417),
- Description: manager for the ceph distributed storage system
+ python-openssl,
+ python-pecan,
+ python-werkzeug,
+ ${misc:Depends},
+ ${python:Depends},
+ ${shlibs:Depends},
- Package: ceph-mgr-dbg
- Architecture: linux-any
- Section: debug
- Priority: extra
- Depends: ceph-mgr (= ${binary:Version}),
- ${misc:Depends},
- Description: debugging symbols for ceph-mgr
- Ceph is a massively scalable, open-source, distributed
- storage system that runs on commodity hardware and delivers object,
- block and file system storage.
- .
- This package contains the debugging symbols for ceph-mgr.
-
++Description: Manager for the ceph distributed file system
+ Ceph is a massively scalable, open-source, distributed
+ storage system that runs on commodity hardware and delivers object,
+ block and file system storage.
+ .
+ This package contains the manager daemon, which is used to expose high
+ level management and monitoring functionality.
+
- Recommends: ceph-common,
- Replaces: ceph (<< 10), ceph-test (<< 12.2.2-14)
- Breaks: ceph (<< 10), ceph-test (<< 12.2.2-14)
+Package: ceph-mon
+Architecture: linux-any
+Depends: ceph-base (= ${binary:Version}),
+ python-flask,
+ ${misc:Depends},
++ ${python:Depends},
+ ${shlibs:Depends},
- Package: ceph-mon-dbg
- Architecture: linux-any
- Section: debug
- Priority: extra
- Depends: ceph-mon (= ${binary:Version}),
- ${misc:Depends},
- Description: debugging symbols for ceph-mon
- Ceph is a massively scalable, open-source, distributed
- storage system that runs on commodity hardware and delivers object,
- block and file system storage.
- .
- This package contains the debugging symbols for ceph-mon.
-
++Replaces: ceph (<< 10.2.2-0ubuntu2~),
++ ceph-common (<< 10),
++ ceph-test (<< 10.2.7-0exp1),
++Breaks: ceph (<< 10.2.2-0ubuntu2~),
++ ceph-common (<< 10),
++ ceph-test (<< 10.2.7-0exp1),
+Description: monitor server for the ceph storage system
+ Ceph is a massively scalable, open-source, distributed
+ storage system that runs on commodity hardware and delivers object,
+ block and file system storage.
+ .
+ This package contains the cluster monitor daemon for the Ceph storage
+ system. One or more instances of ceph-mon form a Paxos part-time parliament
+ cluster that provides extremely reliable and durable storage of cluster
+ membership, configuration, and state.
+
- parted,
- lvm2,
+Package: ceph-osd
+Architecture: linux-any
+Depends: ceph-base (= ${binary:Version}),
- Recommends: ceph-common (= ${binary:Version}),
- Replaces: ceph (<< 10), ceph-test (<< 12.2.2-14)
- Breaks: ceph (<< 10), ceph-test (<< 12.2.2-14)
+ ${misc:Depends},
+ ${python:Depends},
+ ${shlibs:Depends},
- Package: ceph-osd-dbg
- Architecture: linux-any
- Section: debug
- Priority: extra
- Depends: ceph-osd (= ${binary:Version}),
- ${misc:Depends},
- Description: debugging symbols for ceph-osd
- Ceph is a massively scalable, open-source, distributed
- storage system that runs on commodity hardware and delivers object,
- block and file system storage.
- .
- This package contains the debugging symbols for ceph-osd.
-
++Pre-Depends: ceph-common (= ${binary:Version}),
++Replaces: ceph (<< 10.2.2-0ubuntu2~),
++ ceph-test (<< 10.2.7-0exp1),
++Breaks: ceph (<< 10.2.2-0ubuntu2~),
++ ceph-test (<< 10.2.7-0exp1),
+Description: OSD server for the ceph storage system
+ Ceph is a massively scalable, open-source, distributed
+ storage system that runs on commodity hardware and delivers object,
+ block and file system storage.
+ .
+ This package contains the Object Storage Daemon for the Ceph storage system.
+ It is responsible for storing objects on a local file system
+ and providing access to them over the network.
+
- Ceph is a massively scalable, open-source, distributed
- storage system that runs on commodity hardware and delivers object,
- block and file system storage. This is a
+Package: ceph-fuse
+Architecture: linux-any
+Depends: ${misc:Depends},
++ ${python:Depends},
+ ${shlibs:Depends},
+Recommends: fuse,
+Description: FUSE-based client for the Ceph distributed file system
- Package: ceph-fuse-dbg
- Architecture: linux-any
- Section: debug
- Priority: extra
- Depends: ceph-fuse (= ${binary:Version}),
- ${misc:Depends},
- Description: debugging symbols for ceph-fuse
- Ceph is a massively scalable, open-source, distributed
- storage system that runs on commodity hardware and delivers object,
- block and file system storage. This is a
- FUSE-based client that allows one to mount a Ceph file system without
- root privileges.
- .
- This package contains the debugging symbols for ceph-fuse.
-
++ Ceph is a distributed network file system designed to provide
++ excellent performance, reliability, and scalability. This is a
+ FUSE-based client that allows one to mount a Ceph file system without
+ root privileges.
+ .
+ Because the FUSE-based client has certain inherent performance
+ limitations, it is recommended that the native Linux kernel client
+ be used if possible. If it is not practical to load a kernel module
+ (insufficient privileges, older kernel, etc.), then the FUSE client will
+ do.
+
- Ceph is a massively scalable, open-source, distributed
- storage system that runs on commodity hardware and delivers object,
- block and file system storage. This is a
- FUSE-based client that allows one to map Ceph rbd images as files.
- .
- FUSE base client that allows one to map Ceph rbd images as files.
-
- Package: rbd-fuse-dbg
- Architecture: linux-any
- Section: debug
- Priority: extra
- Depends: rbd-fuse (= ${binary:Version}),
- ${misc:Depends},
- Description: debugging symbols for rbd-fuse
- Ceph is a massively scalable, open-source, distributed
- storage system that runs on commodity hardware and delivers object,
- block and file system storage. This is a
+Package: rbd-fuse
+Architecture: linux-any
+Depends: ${misc:Depends},
+ ${shlibs:Depends},
+Recommends: fuse,
+Description: FUSE-based rbd client for the Ceph distributed file system
- .
- This package contains the debugging symbols for rbd-fuse.
-
- Package: rbd-mirror
- Architecture: linux-any
- Depends: ceph-common (= ${binary:Version}),
- librados2 (= ${binary:Version}),
- ${misc:Depends},
- ${shlibs:Depends},
- Description: Ceph daemon for mirroring RBD images
- Ceph is a massively scalable, open-source, distributed
- storage system that runs on commodity hardware and delivers object,
- block and file system storage. This is a
- daemon for mirroring RBD images between Ceph clusters, streaming
- changes asynchronously.
-
- Package: rbd-mirror-dbg
- Architecture: linux-any
- Section: debug
- Priority: extra
- Depends: rbd-mirror (= ${binary:Version}),
- ${misc:Depends},
- Description: debugging symbols for rbd-mirror
- Ceph is a massively scalable, open-source, distributed
- storage system that runs on commodity hardware and delivers object,
- block and file system storage. This is a
- daemon for mirroring RBD images between Ceph clusters, streaming
- changes asynchronously.
- .
- This package contains the debugging symbols for rbd-mirror.
-
- Package: rbd-nbd
- Architecture: linux-any
- Depends: ${misc:Depends},
- ${shlibs:Depends},
- Description: NBD-based rbd client for the Ceph distributed file system
- Ceph is a massively scalable, open-source, distributed
- storage system that runs on commodity hardware and delivers object,
- block and file system storage. This is a
- NBD-based client that allows one to map Ceph rbd images as local
- block device.
- .
- NBD base client that allows one to map Ceph rbd images as local
- block device.
-
- Package: rbd-nbd-dbg
- Architecture: linux-any
- Section: debug
- Priority: extra
- Depends: rbd-nbd (= ${binary:Version}),
- ${misc:Depends},
- Description: debugging symbols for rbd-nbd
- Ceph is a massively scalable, open-source, distributed
- storage system that runs on commodity hardware and delivers object,
- block and file system storage. This is a
- NBD-based client that allows one to map Ceph rbd images as local
- block device.
- .
- This package contains the debugging symbols for rbd-nbd.
-
- Package: ceph-common
- Architecture: linux-any
- Depends: librbd1 (= ${binary:Version}),
- python-cephfs (= ${binary:Version}),
- python-prettytable,
- python-rados (= ${binary:Version}),
- python-rbd (= ${binary:Version}),
- python-requests,
- python-rgw (= ${binary:Version}),
- ${misc:Depends},
- ${python:Depends},
- ${shlibs:Depends},
- Conflicts: ceph-client-tools,
- Replaces: ceph (<< 10),
- ceph-client-tools,
- ceph-fs-common (<< 11.0),
- ceph-test (<< 9.0.3-1646),
- librbd1 (<< 0.92-1238),
- python-ceph (<< 0.92-1223),
- Breaks: ceph (<< 10),
- ceph-fs-common (<< 11.0),
- ceph-test (<< 9.0.3-1646),
- librbd1 (<< 0.92-1238),
- python-ceph (<< 0.92-1223),
- Suggests: ceph-base (= ${binary:Version}),
- ceph-mds (= ${binary:Version}),
- Description: common utilities to mount and interact with a ceph storage cluster
- Ceph is a massively scalable, open-source, distributed
- storage system that runs on commodity hardware and delivers object,
- block and file system storage. This is a collection
- of common tools that allow one to interact with and administer a Ceph cluster.
-
- Package: ceph-common-dbg
- Architecture: linux-any
- Depends: ceph-common (= ${binary:Version}),
- ${misc:Depends},
- Conflicts: ceph-client-tools-dbg,
- Replaces: ceph-client-tools-dbg,
- ceph-test-dbg (<< 9.0.3-1646),
- Breaks: ceph-test-dbg (<< 9.0.3-1646),
- Section: debug
- Priority: extra
- Description: debugging symbols for ceph-common
- Ceph is a massively scalable, open-source, distributed
- storage system that runs on commodity hardware and delivers object,
- block and file system storage. This is a collection
- of common tools that allow one to interact with and administer a Ceph cluster.
- .
- This package contains the debugging symbols for ceph-common.
++ Ceph is a distributed network file system designed to provide
++ excellent performance, reliability, and scalability. This is a
+ FUSE-based client that allows one to map Ceph rbd images as files.
- Architecture: linux-any
+
+Package: ceph-resource-agents
- Priority: extra
- Depends: ceph (= ${binary:Version}),
++Architecture: all
+Recommends: pacemaker,
- Ceph is a massively scalable, open-source, distributed
- storage system that runs on commodity hardware and delivers object,
- block and file system storage.
++Depends: ceph (>= ${binary:Version}),
+ resource-agents,
+ ${misc:Depends},
+Description: OCF-compliant resource agents for Ceph
- Architecture: linux-any
- Section: libs
++ Ceph is a distributed storage and network file system designed to provide
++ excellent performance, reliability, and scalability.
+ .
+ This package contains the resource agents (RAs) which integrate
+ Ceph with OCF-compliant cluster resource managers,
+ such as Pacemaker.
+
+Package: librados2
++Architecture: linux-any
++Multi-Arch: same
++Section: libs
+Conflicts: librados,
+ librados1,
+Replaces: librados,
+ librados1,
- Package: librados2-dbg
- Conflicts: librados1-dbg,
- Replaces: librados1-dbg,
- Architecture: linux-any
- Section: debug
- Priority: extra
- Depends: librados2 (= ${binary:Version}),
- ${misc:Depends},
- Description: debugging symbols for librados
- RADOS is a reliable, autonomic distributed object storage cluster
- developed as part of the Ceph distributed storage system. This is a
- shared library allowing applications to access the distributed object
- store using a simple file-like interface.
- .
- This package contains debugging symbols for librados.
-
+Depends: ${misc:Depends},
+ ${shlibs:Depends},
++Pre-Depends: ${misc:Pre-Depends},
+Description: RADOS distributed object store client library
+ RADOS is a reliable, autonomic distributed object storage cluster
+ developed as part of the Ceph distributed storage system. This is a
+ shared library allowing applications to access the distributed object
+ store using a simple file-like interface.
+
- link against librados.
+Package: librados-dev
+Architecture: linux-any
+Section: libdevel
+Depends: librados2 (= ${binary:Version}),
+ ${misc:Depends},
+ ${shlibs:Depends},
+Conflicts: librados1-dev,
+ librados2-dev,
+Replaces: librados1-dev,
+ librados2-dev,
+Description: RADOS distributed object store client library (development files)
+ RADOS is a reliable, autonomic distributed object storage cluster
+ developed as part of the Ceph distributed storage system. This is a
+ shared library allowing applications to access the distributed object
+ store using a simple file-like interface.
+ .
+ This package contains development files needed for building applications that
- Package: libradosstriper1-dbg
- Architecture: linux-any
- Section: debug
- Priority: extra
- Depends: libradosstriper1 (= ${binary:Version}),
- ${misc:Depends},
- Description: debugging symbols for libradosstriper
- libradosstriper is a striping interface built on top of the rados
- library, allowing to stripe bigger objects onto several standard
- rados objects using an interface very similar to the rados one.
- .
- This package contains debugging symbols for libradosstriper.
-
++ link against librados2.
+
+Package: libradosstriper1
+Architecture: linux-any
++Multi-Arch: same
+Section: libs
+Depends: librados2 (= ${binary:Version}),
+ ${misc:Depends},
+ ${shlibs:Depends},
+Description: RADOS striping interface
+ Striping interface built on top of the rados library, allowing
+ to stripe bigger objects onto several standard rados objects using
+ an interface very similar to the rados one.
+
- Package: librbd1-dbg
- Architecture: linux-any
- Section: debug
- Priority: extra
- Depends: librbd1 (= ${binary:Version}),
- ${misc:Depends},
- Description: debugging symbols for librbd1
- RBD is a block device striped across multiple distributed objects
- in RADOS, a reliable, autonomic distributed object storage cluster
- developed as part of the Ceph distributed storage system. This is a
- shared library allowing applications to manage these block devices.
- .
- This package contains debugging symbols for librbd1.
-
+Package: libradosstriper-dev
+Architecture: linux-any
+Section: libdevel
+Depends: libradosstriper1 (= ${binary:Version}),
+ ${misc:Depends},
+Description: RADOS striping interface (development files)
+ libradosstriper is a striping interface built on top of the rados
+ library, allowing to stripe bigger objects onto several standard
+ rados objects using an interface very similar to the rados one.
+ .
+ This package contains development files needed for building applications that
+ link against libradosstriper.
+
+Package: librbd1
+Architecture: linux-any
++Multi-Arch: same
+Section: libs
+Depends: librados2 (= ${binary:Version}),
+ ${misc:Depends},
+ ${shlibs:Depends},
++Pre-Depends: ${misc:Pre-Depends},
+Description: RADOS block device client library
+ RBD is a block device striped across multiple distributed objects
+ in RADOS, a reliable, autonomic distributed object storage cluster
+ developed as part of the Ceph distributed storage system. This is a
+ shared library allowing applications to manage these block devices.
+
- Depends: librados-dev (= ${binary:Version}),
+Package: librbd-dev
+Architecture: linux-any
+Section: libdevel
- Architecture: linux-any
- Section: libs
++Depends: librados-dev,
+ librbd1 (= ${binary:Version}),
+ ${misc:Depends},
+Conflicts: librbd1-dev,
+Replaces: librbd1-dev,
+Description: RADOS block device client library (development files)
+ RBD is a block device striped across multiple distributed objects
+ in RADOS, a reliable, autonomic distributed object storage cluster
+ developed as part of the Ceph distributed storage system. This is a
+ shared library allowing applications to manage these block devices.
+ .
+ This package contains development files needed for building applications that
+ link against librbd1.
+
+Package: libcephfs2
++Architecture: linux-any
++Multi-Arch: same
++Section: libs
+Conflicts: libceph,
+ libceph1,
+ libcephfs,
+Replaces: libceph,
+ libceph1,
+ libcephfs,
- Ceph is a massively scalable, open-source, distributed
- storage system that runs on commodity hardware and delivers object,
- block and file system storage. This is a
- shared library allowing applications to access a Ceph distributed
- file system via a POSIX-like interface.
-
- Package: libcephfs2-dbg
- Architecture: linux-any
- Section: debug
- Priority: extra
- Depends: libcephfs2 (= ${binary:Version}),
- ${misc:Depends},
- Conflicts: libceph1-dbg,
- Replaces: libceph1-dbg,
- Description: debugging symbols for libcephfs2
- Ceph is a massively scalable, open-source, distributed
- storage system that runs on commodity hardware and delivers object,
- block and file system storage. This is a
+Depends: ${misc:Depends},
+ ${shlibs:Depends},
++Pre-Depends: ${misc:Pre-Depends},
+Description: Ceph distributed file system client library
- .
- This package contains debugging symbols for libcephfs2.
++ Ceph is a distributed network file system designed to provide
++ excellent performance, reliability, and scalability. This is a
+ shared library allowing applications to access a Ceph distributed
+ file system via a POSIX-like interface.
- libcephfs2-dev,
+
+Package: libcephfs-dev
+Architecture: linux-any
+Section: libdevel
+Depends: libcephfs2 (= ${binary:Version}),
+ ${misc:Depends},
+Conflicts: libceph-dev,
+ libceph1-dev,
- libcephfs2-dev,
++ libcephfs1-dev,
+Replaces: libceph-dev,
+ libceph1-dev,
- Ceph is a massively scalable, open-source, distributed
- storage system that runs on commodity hardware and delivers object,
- block and file system storage. This is a
++ libcephfs1-dev,
+Description: Ceph distributed file system client library (development files)
- link against libcephfs.
++ Ceph is a distributed network file system designed to provide
++ excellent performance, reliability, and scalability. This is a
+ shared library allowing applications to access a Ceph distributed
+ file system via a POSIX-like interface.
+ .
+ This package contains development files needed for building applications that
- Package: librgw2-dbg
- Architecture: linux-any
- Section: debug
- Priority: extra
- Depends: librgw2 (= ${binary:Version}),
- ${misc:Depends},
- Description: debugging symbols for librbd1
- RADOS is a distributed object store used by the Ceph distributed
- storage system. This package provides a REST gateway to the
- object store that aims to implement a superset of Amazon's S3
- service.
- .
- This package contains debugging symbols for librgw2.
-
++ link against libcephfs2.
+
+Package: librgw2
+Architecture: linux-any
++Multi-Arch: same
+Section: libs
+Depends: librados2 (= ${binary:Version}),
+ ${misc:Depends},
+ ${shlibs:Depends},
+Description: RADOS Gateway client library
+ RADOS is a distributed object store used by the Ceph distributed
+ storage system. This package provides a REST gateway to the
+ object store that aims to implement a superset of Amazon's S3
+ service.
+ .
+ This package contains the library interface and headers only.
+
- mime-support,
+Package: librgw-dev
+Architecture: linux-any
+Section: libdevel
+Depends: librados-dev (= ${binary:Version}),
+ librgw2 (= ${binary:Version}),
+ ${misc:Depends},
+Description: RADOS client library (development files)
+ RADOS is a distributed object store used by the Ceph distributed
+ storage system. This package provides a REST gateway to the
+ object store that aims to implement a superset of Amazon's S3
+ service.
+ .
+ This package contains development files needed for building applications
+ that link against librgw2.
+
+Package: radosgw
+Architecture: linux-any
+Depends: ceph-common (= ${binary:Version}),
+ librgw2 (= ${binary:Version}),
- Recommends: ntp | time-daemon,
++ lsb-base (>= 3.0-6),
+ ${misc:Depends},
+ ${shlibs:Depends},
- Package: radosgw-dbg
- Architecture: linux-any
- Section: debug
- Priority: extra
- Depends: radosgw (= ${binary:Version}),
- ${misc:Depends},
- Description: debugging symbols for radosgw
- RADOS is a distributed object store used by the Ceph distributed
- storage system. This package provides a REST gateway to the
- object store that aims to implement a superset of Amazon's S3
- service as well as the OpenStack Object Storage ("Swift") API.
- .
- This package contains debugging symbols for radosgw.
-
++Suggests: logrotate,
+Description: REST gateway for RADOS distributed object store
+ RADOS is a distributed object store used by the Ceph distributed
+ storage system. This package provides a REST gateway to the
+ object store that aims to implement a superset of Amazon's S3
+ service as well as the OpenStack Object Storage ("Swift") API.
+ .
+ This package contains the proxy daemon and related tools only.
+
- Depends: ceph-common,
+Package: ceph-test
+Architecture: linux-any
- socat,
++Depends: ceph-base (>= 12.2.8+dfsg1-1~),
++ ceph-common,
++ ceph-mon (>= 12.2.8+dfsg1-1~),
++ ceph-osd (>= 12.2.8+dfsg1-1~),
+ curl,
+ jq,
- Replaces: ceph-base (<< 11)
- Breaks: ceph-base (<< 1)
- Description: Ceph test and benchmarking tools
- This package contains tools for testing and benchmarking Ceph.
-
- Package: ceph-test-dbg
- Architecture: linux-any
- Section: debug
- Priority: extra
- Depends: ceph-common (= ${binary:Version}),
- ceph-test (= ${binary:Version}),
- curl,
- ${misc:Depends},
++ junit4,
++ libcephfs-java,
++ xml2,
+ xmlstarlet,
+ ${misc:Depends},
+ ${shlibs:Depends},
- This package contains the debugging symbols for ceph-test.
+Description: Ceph test and benchmarking tools
++ Ceph is a distributed storage system designed to provide excellent
++ performance, reliability, and scalability.
+ .
- Architecture: linux-any
++ This package contains tools for testing and benchmarking Ceph.
+
+Package: python-ceph
- Depends: python-cephfs (= ${binary:Version}),
- python-rados (= ${binary:Version}),
- python-rbd (= ${binary:Version}),
- python-rgw (= ${binary:Version}),
- Description: Meta-package for python libraries for the Ceph libraries
++Architecture: all
+Section: python
- This package is a metapackage for all Python 2 bindings.
++Depends: python-cephfs (>= ${binary:Version}),
++ python-rados (>= ${binary:Version}),
++ python-rbd (>= ${binary:Version}),
++ python-rgw (>= ${binary:Version}),
++ ${misc:Depends},
++Description: Meta-package for Python modules for the Ceph libraries
+ Ceph is a massively scalable, open-source, distributed
+ storage system that runs on commodity hardware and delivers object,
+ block and file system storage.
+ .
- Package: python-rados
- Architecture: linux-any
++ This package is a metapackage for all Python bindings.
+
- Depends: librados2 (= ${binary:Version}),
++Package: python3-ceph
++Architecture: all
+Section: python
- ${python:Depends},
- ${shlibs:Depends},
- Replaces: python-ceph (<< 0.92-1223),
- Breaks: python-ceph (<< 0.92-1223),
- Description: Python 2 libraries for the Ceph librados library
++Depends: python3-cephfs (>= ${binary:Version}),
++ python3-rados (>= ${binary:Version}),
++ python3-rbd (>= ${binary:Version}),
++ python3-rgw (>= ${binary:Version}),
+ ${misc:Depends},
- This package contains Python 2 libraries for interacting with Ceph's
- RADOS object storage.
++Description: Meta-package for Python 3 modules for the Ceph libraries
+ Ceph is a massively scalable, open-source, distributed
+ storage system that runs on commodity hardware and delivers object,
+ block and file system storage.
+ .
- Package: python-rados-dbg
++ This package is a metapackage for all Python 3 bindings.
+
- Section: debug
- Priority: extra
- Depends: python-rados (= ${binary:Version}),
- python-dbg,
- ${misc:Depends},
- Description: Python 2 libraries for the Ceph librados library
++Package: python-rados
+Architecture: linux-any
- This package contains Python 2 libraries for interacting with Ceph's
++Section: python
++Depends: ${misc:Depends},
++ ${python:Depends},
++ ${shlibs:Depends},
++Replaces: python-ceph (<< 0.94.1-1~),
++Breaks: python-ceph (<< 0.94.1-1~),
++Description: Python libraries for the Ceph librados library
+ Ceph is a massively scalable, open-source, distributed
+ storage system that runs on commodity hardware and delivers object,
+ block and file system storage.
+ .
- .
- This package contains the debugging symbols for python-rados.
++ This package contains Python libraries for interacting with Ceph's
+ RADOS object storage.
- Depends: librados2 (= ${binary:Version}),
- ${misc:Depends},
+
+Package: python3-rados
+Architecture: linux-any
+Section: python
- Package: python3-rados-dbg
- Architecture: linux-any
- Section: debug
- Priority: extra
- Depends: python3-rados (= ${binary:Version}),
- python3-dbg,
- ${misc:Depends}
- Description: Python 3 libraries for the Ceph librados library
- Ceph is a massively scalable, open-source, distributed
- storage system that runs on commodity hardware and delivers object,
- block and file system storage.
- .
- This package contains Python 3 libraries for interacting with Ceph's
- RADOS object storage.
- .
- This package contains the debugging symbols for python-rados.
-
++Depends: ${misc:Depends},
+ ${python3:Depends},
+ ${shlibs:Depends},
+Description: Python 3 libraries for the Ceph librados library
+ Ceph is a massively scalable, open-source, distributed
+ storage system that runs on commodity hardware and delivers object,
+ block and file system storage.
+ .
+ This package contains Python 3 libraries for interacting with Ceph's
+ RADOS object storage.
+
- Depends: librbd1 (>= ${binary:Version}),
- ${misc:Depends},
+Package: python-rbd
+Architecture: linux-any
+Section: python
- Replaces: python-ceph (<< 0.92-1223),
- Breaks: python-ceph (<< 0.92-1223),
- Description: Python 2 libraries for the Ceph librbd library
- Ceph is a massively scalable, open-source, distributed
- storage system that runs on commodity hardware and delivers object,
- block and file system storage.
- .
- This package contains Python 2 libraries for interacting with Ceph's
- RBD block device library.
-
- Package: python-rbd-dbg
- Architecture: linux-any
- Section: debug
- Priority: extra
- Depends: python-rbd (= ${binary:Version}),
- python-dbg,
- ${misc:Depends},
- Description: Python 2 libraries for the Ceph librbd library
++Depends: ${misc:Depends},
+ ${python:Depends},
+ ${shlibs:Depends},
- This package contains Python 2 libraries for interacting with Ceph's
++Replaces: python-ceph (<< 0.94.1-1~),
++Breaks: python-ceph (<< 0.94.1-1~),
++Description: Python libraries for the Ceph librbd library
+ Ceph is a massively scalable, open-source, distributed
+ storage system that runs on commodity hardware and delivers object,
+ block and file system storage.
+ .
- .
- This package contains the debugging symbols for python-rbd.
++ This package contains Python libraries for interacting with Ceph's
+ RBD block device library.
- Depends: librbd1 (>= ${binary:Version}),
- ${misc:Depends},
+
+Package: python3-rbd
+Architecture: linux-any
+Section: python
- Package: python3-rbd-dbg
- Architecture: linux-any
- Section: debug
- Priority: extra
- Depends: python3-rbd (= ${binary:Version}),
- python3-dbg,
- ${misc:Depends},
- Description: Python 3 libraries for the Ceph librbd library
- Ceph is a massively scalable, open-source, distributed
- storage system that runs on commodity hardware and delivers object,
- block and file system storage.
- .
- This package contains Python 3 libraries for interacting with Ceph's
- RBD block device library.
- .
- This package contains the debugging symbols for python-rbd.
-
++Depends: ${misc:Depends},
+ ${python3:Depends},
+ ${shlibs:Depends},
+Description: Python 3 libraries for the Ceph librbd library
+ Ceph is a massively scalable, open-source, distributed
+ storage system that runs on commodity hardware and delivers object,
+ block and file system storage.
+ .
+ This package contains Python 3 libraries for interacting with Ceph's
+ RBD block device library.
+
- Depends: librgw2 (>= ${binary:Version}),
- ${misc:Depends},
+Package: python-rgw
+Architecture: linux-any
+Section: python
- Replaces: python-ceph (<< 0.92-1223),
- Breaks: python-ceph (<< 0.92-1223),
- Description: Python 2 libraries for the Ceph librgw library
- Ceph is a massively scalable, open-source, distributed
- storage system that runs on commodity hardware and delivers object,
- block and file system storage.
- .
- This package contains Python 2 libraries for interacting with Ceph's
- RGW library.
-
- Package: python-rgw-dbg
- Architecture: linux-any
- Section: debug
- Priority: extra
- Depends: python-rgw (= ${binary:Version}),
- python-dbg,
- ${misc:Depends},
++Depends: ${misc:Depends},
+ ${python:Depends},
+ ${shlibs:Depends},
- .
- This package contains the debugging symbols for python-rgw.
+Description: Python 2 libraries for the Ceph librgw library
+ Ceph is a massively scalable, open-source, distributed
+ storage system that runs on commodity hardware and delivers object,
+ block and file system storage.
+ .
+ This package contains Python 2 libraries for interacting with Ceph's
+ RGW library.
- Depends: librgw2 (>= ${binary:Version}),
- ${misc:Depends},
+
+Package: python3-rgw
+Architecture: linux-any
+Section: python
- Package: python3-rgw-dbg
- Architecture: linux-any
- Section: debug
- Priority: extra
- Depends: python3-rgw (= ${binary:Version}),
- python3-dbg,
- ${misc:Depends},
- Description: Python 3 libraries for the Ceph librgw library
- Ceph is a massively scalable, open-source, distributed
- storage system that runs on commodity hardware and delivers object,
- block and file system storage.
- .
- This package contains Python 3 libraries for interacting with Ceph's
- RGW library.
- .
- This package contains the debugging symbols for python3-rgw.
-
++Depends: ${misc:Depends},
+ ${python3:Depends},
+ ${shlibs:Depends},
+Description: Python 3 libraries for the Ceph librgw library
+ Ceph is a massively scalable, open-source, distributed
+ storage system that runs on commodity hardware and delivers object,
+ block and file system storage.
+ .
+ This package contains Python 3 libraries for interacting with Ceph's
+ RGW library.
+
- Depends: libcephfs2 (= ${binary:Version}),
+Package: python-cephfs
+Architecture: linux-any
+Section: python
- Replaces: python-ceph (<< 0.92-1223),
- Breaks: python-ceph (<< 0.92-1223),
- Description: Python 2 libraries for the Ceph libcephfs library
- Ceph is a massively scalable, open-source, distributed
- storage system that runs on commodity hardware and delivers object,
- block and file system storage.
- .
- This package contains Python 2 libraries for interacting with Ceph's
- CephFS file system client library.
-
- Package: python-cephfs-dbg
- Architecture: linux-any
- Section: debug
- Priority: extra
- Depends: python-cephfs (= ${binary:Version}),
- python-dbg,
- ${misc:Depends},
- Description: Python 2 libraries for the Ceph libcephfs library
++Depends: ceph-common,
+ ${misc:Depends},
+ ${python:Depends},
+ ${shlibs:Depends},
- This package contains Python 2 libraries for interacting with Ceph's
++Replaces: python-ceph (<< 0.94.1-1~),
++Breaks: python-ceph (<< 0.94.1-1~),
++Description: Python libraries for the Ceph libcephfs library
+ Ceph is a massively scalable, open-source, distributed
+ storage system that runs on commodity hardware and delivers object,
+ block and file system storage.
+ .
- .
- This package contains the debugging symbols for python-cephfs.
++ This package contains Python libraries for interacting with Ceph's
+ CephFS file system client library.
- Depends: libcephfs2 (= ${binary:Version}),
+
+Package: python3-cephfs
+Architecture: linux-any
+Section: python
- Package: python3-cephfs-dbg
- Architecture: linux-any
- Section: debug
- Priority: extra
- Depends: python3-cephfs (= ${binary:Version}),
- python3-dbg,
- ${misc:Depends},
- Description: Python 3 libraries for the Ceph libcephfs library
- Ceph is a massively scalable, open-source, distributed
- storage system that runs on commodity hardware and delivers object,
- block and file system storage.
- .
- This package contains Python 3 libraries for interacting with Ceph's
- CephFS file system client library.
- .
- This package contains the debugging symbols for python3-cephfs.
-
- Package: python3-ceph-argparse
- Architecture: linux-any
- Section: python
- Depends: ${misc:Depends},
- ${python3:Depends},
- Description: Python 3 utility libraries for Ceph CLI
- Ceph is a massively scalable, open-source, distributed
- storage system that runs on commodity hardware and delivers object,
- block and file system storage.
- .
- This package contains types and routines for Python 3 used by the
- Ceph CLI as well as the RESTful interface.
-
++Depends: ceph-common (>= 12.2.8+dfsg1-1~),
+ ${misc:Depends},
+ ${python3:Depends},
+ ${shlibs:Depends},
+Description: Python 3 libraries for the Ceph libcephfs library
+ Ceph is a massively scalable, open-source, distributed
+ storage system that runs on commodity hardware and delivers object,
+ block and file system storage.
+ .
+ This package contains Python 3 libraries for interacting with Ceph's
+ CephFS file system client library.
+
- Section: java
+Package: libcephfs-java
- Depends: libcephfs-jni (= ${binary:Version}),
+Architecture: all
- Description: Java libraries for the Ceph File System
++Section: java
++Depends: libcephfs-jni (>= ${binary:Version}),
+ ${java:Depends},
+ ${misc:Depends},
- ${java:Depends},
++Description: Java library for the Ceph File System
++ Ceph is a distributed storage system designed to provide excellent
++ performance, reliability, and scalability.
++ .
++ This package contains the Java library for interacting with the Ceph
++ File System.
+
+Package: libcephfs-jni
+Architecture: linux-any
+Section: java
+Depends: libcephfs2 (= ${binary:Version}),
- Depends: librados-dev (= ${binary:Version}) ${misc:Depends},
+ ${misc:Depends},
+ ${shlibs:Depends},
+Description: Java Native Interface library for CephFS Java bindings
++ Ceph is a distributed storage system designed to provide excellent
++ performance, reliability, and scalability.
++ .
++ This package contains the Java Native Interface library for interacting
++ with the Ceph File System.
+
+Package: rados-objclass-dev
+Architecture: linux-any
+Section: libdevel
- This package contains development files needed for building RADOS object class plugins.
++Depends: librados-dev (= ${binary:Version}),
++ ${misc:Depends},
+Description: RADOS object class development kit.
+ .
++ This package contains development files needed for building RADOS object class
++ plugins.
--- /dev/null
- Format-Specification: http://anonscm.debian.org/viewvc/dep/web/deps/dep5/copyright-format.xml?revision=279&view=markup
- Name: ceph
- Maintainer: Sage Weil <sage@newdream.net>
++Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
++Upstream-Name: ceph
++Upstream-Contact: Sage Weil <sage@newdream.net>
+Source: http://ceph.com/
++Files-Excluded: src/rapidjson/bin/jsonchecker
++ src/boost/libs/beast/test/extern/zlib-1.2.11/contrib/dotzlib/DotZLib.chm
++Comment:
++ These Files fall under the JSON license which is not DFSG compliant or are missing
++ source and the source was not obtainable
+
+Files: *
- Copyright: (c) 2004-2010 by Sage Weil <sage@newdream.net>
- License: LGPL2.1 (see COPYING-LGPL2.1)
++Copyright: 2004-2014 Sage Weil <sage@newdream.net>
++ 2004-2014 Inktank <info@inktank.com>
++ Inktank, Inc
++ Inktank Storage, Inc.
++ 2012-2014 Red Hat <contact@redhat.com>
++ 2013-2014 Cloudwatt <libre.licensing@cloudwatt.com>
++ 2013 CohortFS, LLC
++ 2004-2011 Dreamhost
++ 2013 eNovance SAS <licensing@enovance.com>
++ 2014 Adam Crume <adamcrume@gmail.com>
++ 2012 Florian Haas, hastexo
++ 2010 Greg Farnum <gregf@hq.newdream.net>
++ 2014 John Spray <john.spray@inktank.com
++ 2004-2012 New Dream Network
++ 2014 Sebastien Ponce <sebastien.ponce@cern.ch>
++ 2011 Stanislav Sedov <stas@FreeBSD.org>
++ 2013-2014 UnitedStack <haomai@unitedstack.com>
++ 2011 Wido den Hollander <wido@widodh.nl>
++License: LGPL-2.1
+
- Files: cmake/modules/FindLTTngUST.cmake
- Copyright:
- Copyright 2016 Kitware, Inc.
- Copyright 2016 Philippe Proulx <pproulx@efficios.com>
- License: BSD 3-clause
++Files: src/erasure-code/jerasure/ErasureCode*
++ src/erasure-code/ErasureCode*
++ src/erasure-code/isa/*
++ src/include/str_map.h
++ src/test/common/test_str_map.cc
++ src/test/erasure-code/*
++ src/test/rgw/test_rgw_manifest.cc
++Copyright: 2014 CERN/Switzerland
++ 2013-2014 Cloudwatt <libre.licensing@cloudwatt.com>
++ 2014 Red Hat <contact@redhat.com>
++ 2013 eNovance SAS <licensing@enovance.com>
++License: LGPL-2.1+
+
- Files: doc/*
- Copyright: (c) 2010-2012 New Dream Network and contributors
- License: Creative Commons Attribution Share Alike 3.0 (CC-BY-SA-3.0)
++Files: src/erasure-code/isa/isa-l/erasure_code/*
++Copyright: 2011-2014 Intel Corporation
++License: BSD-3-clause
++
++Files: src/rocksdb/*
++Copyright: 2004-2013 Facebook, Inc.
++ 2011 The LevelDB Authors
++ 2009 Google Inc.
++License: BSD-3-clause
++Comment:
++ Additional Grant of Patent Rights
++ .
++ “Software” means the rocksdb software distributed by Facebook, Inc.
++ .
++ Facebook hereby grants you a perpetual, worldwide, royalty-free,
++ non-exclusive, irrevocable (subject to the termination provision below)
++ license under any rights in any patent claims owned by Facebook, to make,
++ have made, use, sell, offer to sell, import, and otherwise transfer the
++ Software. For avoidance of doubt, no license is granted under Facebook’s
++ rights in any patent claims that are infringed by (i) modifications to the
++ Software made by you or a third party, or (ii) the Software in combination
++ with any software or other technology provided by you or a third party.
++ .
++ The license granted hereunder will terminate, automatically and without
++ notice, for anyone that makes any claim (including by filing any lawsuit,
++ assertion or other action) alleging (a) direct, indirect, or contributory
++ infringement or inducement to infringe any patent: (i) by Facebook or any
++ of its subsidiaries or affiliates, whether or not such claim is related
++ to the Software, (ii) by any party if such claim arises in whole or in
++ part from any software, product or service of Facebook or any of its
++ subsidiaries or affiliates, whether or not such claim is related to the
++ Software, or (iii) by any party relating to the Software; or (b) that
++ any right in any patent claim of Facebook is invalid or unenforceable.
++
++Files: src/rocksdb/util/xxhash.*
++Copyright: 2012-2014, Yann Collet.
++License: BSD-2-clause
++ Redistribution and use in source and binary forms, with or without
++ modification, are permitted provided that the following conditions are
++ met:
++ .
++ * Redistributions of source code must retain the above copyright
++ notice, this list of conditions and the following disclaimer.
++ * Redistributions in binary form must reproduce the above
++ copyright notice, this list of conditions and the following disclaimer
++ in the documentation and/or other materials provided with the
++ distribution.
++ .
++ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Files: src/mount/canonicalize.c
- Copyright: Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
- License: LGPL2 or later (see COPYING-GPL2)
++ src/test/common/test_config.cc
++ src/test/crush/TestCrushWrapper.cc
++ src/test/common/Throttle.cc
++ src/test/objectstore/chain_xattr.cc
++ src/test/mon/mon-test-helpers.sh
++ src/test/objectstore/chain_xattr.cc
++ src/test/osd/osd-test-helpers.sh
++ src/ceph-disk
++ src/stop.sh
++Copyright: 1993 Rick Sladkey <jrs@world.std.com>
++ 2013 Inktank <info@inktank.com>
++ 2013-2014 Cloudwatt <libre.licensing@cloudwatt.com>
++License: LGPL-2+
+
+Files: src/os/btrfs_ioctl.h
- Copyright: Copyright (C) 2007 Oracle. All rights reserved.
- License: GPL2 (see COPYING-GPL2)
++ src/test/mon/PGMap.cc
++Copyright: 2007 Oracle. All rights reserved.
++ 2014 Inktank <info@inktank.com>
++License: GPL-2
+
- Files: src/include/ceph_hash.cc
- Copyright: None
- License: Public domain
++Files: src/common/ceph_hash.cc
++Copyright: 1995-1997 Robert J. Jenkins Jr.
++License: public-domain
++ This file uses Robert Jenkin's hash function as detailed at:
++ .
++ http://burtleburtle.net/bob/hash/evahash.html
++ .
++ This is in the public domain.
+
+Files: src/common/bloom_filter.hpp
- Copyright: Copyright (C) 2000 Arash Partow
- License: Boost Software License, Version 1.0
-
- Files: src/common/crc32c_intel*:
- Copyright:
- Copyright 2012-2013 Intel Corporation All Rights Reserved.
- License: BSD 3-clause
-
- Files: src/common/sctp_crc32.c:
- Copyright:
- Copyright (c) 2001-2007, by Cisco Systems, Inc. All rights reserved.
- Copyright (c) 2004-2006 Intel Corporation - All Rights Reserved
- License:
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
-
- a) Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
- b) Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the distribution.
-
- c) Neither the name of Cisco Systems, Inc. nor the names of its
- contributors may be used to endorse or promote products derived
- from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
- THE POSSIBILITY OF SUCH DAMAGE.
-
- Files: src/json_spirit
- Copyright:
- Copyright John W. Wilkinson 2007 - 2011
- License:
- The MIT License
-
- Copyright (c) 2007 - 2010 John W. Wilkinson
-
- Permission is hereby granted, free of charge, to any person
- obtaining a copy of this software and associated documentation
- files (the "Software"), to deal in the Software without
- restriction, including without limitation the rights to use,
- copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following
- conditions:
-
- The above copyright notice and this permission notice shall be
- included in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- OTHER DEALINGS IN THE SOFTWARE.
-
-
-
- Files: src/test/common/Throttle.cc src/test/filestore/chain_xattr.cc
- Copyright: Copyright (C) 2013 Cloudwatt <libre.licensing@cloudwatt.com>
- License: LGPL2 or later
-
- Files: src/osd/ErasureCodePluginJerasure/*.{c,h}
- Copyright: Copyright (c) 2011, James S. Plank <plank@cs.utk.edu>
- License:
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- - Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- - Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
-
- - Neither the name of the University of Tennessee nor the names of its
- contributors may be used to endorse or promote products derived
- from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
- WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- POSSIBILITY OF SUCH DAMAGE.
-
- Packaging:
- Copyright (C) 2004-2009 by Sage Weil <sage@newdream.net>
- Copyright (C) 2010 Canonical, Ltd.
- Licensed under LGPL-2.1
-
- Files: src/test/perf_local.cc
- Copyright:
- (c) 2011-2014 Stanford University
- (c) 2011 Facebook
- License:
- The MIT License
++Copyright: 2000 Arash Partow
++License: Boost-Software-License-1.0
++ Permission is hereby granted, free of charge, to any person or organization
++ obtaining a copy of the software and accompanying documentation covered by
++ this license (the "Software") to use, reproduce, display, distribute,
++ execute, and transmit the Software, and to prepare derivative works of the
++ Software, and to permit third-parties to whom the Software is furnished to
++ do so, all subject to the following:
++ .
++ The copyright notices in the Software and this entire statement,
++ including the above license grant, this restriction and the following
++ disclaimer, must be included in all copies of the Software, in whole or
++ in part, and all derivative works of the Software, unless such copies
++ or derivative works are solely in the form of machine-executable object
++ code generated by a source language processor.
++ .
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
++ SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
++ FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
++ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ DEALINGS IN THE SOFTWARE.
++
++Files: src/common/crc32c_intel*
++Copyright: 2012-2013 Intel Corporation All Rights Reserved.
++License: BSD-3-clause
++
++Files: src/common/sctp_crc32.c
++Copyright: 2001-2007, by Cisco Systems, Inc. All rights reserved,
++ 2004-2006 Intel Corporation - All Rights Reserved
++License: BSD-3-clause
++
++Files: src/erasure-code/jerasure/gf-complete/*/*
++Copyright: 2013 James S. Plank
++ Ethan L. Miller
++ Kevin M. Greenan
++ Benjamin A. Arnold
++ John A. Burnum
++ Adam W. Disney
++ Allen C. McBride
++License: BSD-3-clause
++Comment:
++ https://bitbucket.org/jimplank/gf-complete
++
++Files: src/erasure-code/jerasure/jerasure/*/*
++Copyright: 2011-2013 James S. Plank <plank@cs.utk.edu>
++ 2013 Kevin Greenan
++License: BSD-3-clause
++
++Files: src/gtest/*
++Copyright: 2008, Google Inc.
++License: BSD-3-clause
++
++Files: src/civetweb/*
++Copyright: 2004-2013 Sergey Lyubka
++ 2013-2014 the Civetweb developers
++License: Expat
++
++Files: src/json_spirit/*
++Copyright: 2007-2011, John W. Wilkinson
++License: Expat
++
++Files: src/java/native/ScopedLocalRef.h
++ src/java/native/JniConstants.*
++Copyright: 2010 The Android Open Source Project
++License: Apache-2.0
++ Licensed under the Apache License, Version 2.0 (the "License");
++ you may not use this file except in compliance with the License.
++ You may obtain a copy of the License at
++ .
++ http://www.apache.org/licenses/LICENSE-2.0
++ .
++ Unless required by applicable law or agreed to in writing, software
++ distributed under the License is distributed on an "AS IS" BASIS,
++ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ See the License for the specific language governing permissions and
++ limitations under the License.
++ .
++ The complete text of the Apache License, Version 2.0
++ can be found in "/usr/share/common-licenses/Apache-2.0".
++
++Files: src/libs3/*
++Copyright: 2008 Bryan Ischo <bryan@ischo.com>
++License: GPL-3/OpenSSL
++ libs3 is free software: you can redistribute it and/or modify it under the
++ terms of the GNU General Public License as published by the Free Software
++ Foundation, version 3 of the License.
++ .
++ In addition, as a special exception, the copyright holders give
++ permission to link the code of this library and its programs with the
++ OpenSSL library, and distribute linked combinations including the two.
++ .
++ libs3 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 General Public License for more
++ details.
++ .
++ The complete text of the GNU General Public License version 3
++ can be found in "/usr/share/common-licenses/GPL-3' file.
++
++Files: src/mount/mtab.c
++Copyright: util-linux-ng AUTHORS
++License: GPL-2+
++Comment:
++ "mount/fstab.c" from line 559:
++ https://git.kernel.org/cgit/utils/util-linux/util-linux.git/tree/mount-deprecated/fstab.c?h=v2.22#n559
++ https://git.kernel.org/cgit/utils/util-linux/util-linux.git/tree/README.licensing
++
++Files: src/test/librbd/fsx.c
++Copyright: 1991, NeXT Computer, Inc.
++License: APSL-2.0
++ The contents of this file constitute Original Code as defined in and
++ are subject to the Apple Public Source License Version 2.0 (the
++ "License"). You may not use this file except in compliance with the
++ License. Please obtain a copy of the License at
++ http://www.opensource.apple.com/apsl/ and read it before using this file.
++ .
++ This Original Code and all software distributed under the License are
++ distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
++ EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
++ INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
++ License for the specific language governing rights and limitations
++ under the License.
++Comment:
++ http://codemonkey.org.uk/projects/fsx/
++ http://codemonkey.org.uk/projects/fsx/fsx-macosforge/fsx.c
++
++Files: man/*
++ debian/man/*
++Copyright: 2010-2014, Inktank Storage, Inc. and contributors.
++License: CC-BY-SA-3.0
++
++Files: debian/*
++Copyright: 2010 Sage Weil <sage@newdream.net>
++ 2010 Canonical, Ltd.
++ 2011-2013 László Böszörményi (GCS) <gcs@debian.org>
++ 2013-2014 James Page <james.page@ubuntu.com>
++ 2014 Dmitry Smirnov <onlyjob@debian.org>
++License: LGPL-2.1
++
++License: GPL-2
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 2 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 General Public License for more details.
++ .
++ You should have received a copy of the GNU General Public License along
++ with this program; if not, write to the Free Software Foundation, Inc.,
++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++ .
++ On Debian systems, the complete text of the GNU General Public License
++ version 2 can be found in `/usr/share/common-licenses/GPL-2' file.
++
++License: GPL-2+
++ This program is free software: you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation, either version 2 of the License, or
++ (at your option) any later version.
++ .
++ This package 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 General Public License for more details.
++ .
++ You should have received a copy of the GNU General Public License
++ along with this program. If not, see <http://www.gnu.org/licenses/>.
++ .
++ On Debian systems, the complete text of the GNU General Public License
++ version 2 can be found in `/usr/share/common-licenses/GPL-2'.
++
++License: LGPL-2.1
++ This library is free software; you can redistribute it and/or
++ modify it under the terms of the GNU Lesser General Public
++ License version 2.1 as published by the Free Software Foundation.
++ .
++ This library 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 library; if not, write to the Free Software
++ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++ .
++ On Debian systems, the complete text of the GNU Lesser General
++ Public License can be found in `/usr/share/common-licenses/LGPL-2.1'.
++
++License: LGPL-2.1+
++ This library 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 library 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 library; if not, write to the Free Software
++ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++ .
++ On Debian systems, the complete text of the GNU Lesser General
++ Public License can be found in `/usr/share/common-licenses/LGPL-2.1'.
++
++License: LGPL-2+
++ This library is free software; you can redistribute it and/or
++ modify it under the terms of the GNU Lesser General Public
++ License version 2 (or later) as published by the Free Software Foundation.
++ .
++ This library 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 library; if not, write to the Free Software
++ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++ .
++ On Debian systems, the complete text of the GNU Lesser General
++ Public License 2 can be found in `/usr/share/common-licenses/LGPL-2'.
++
++License: Expat
++ Permission is hereby granted, free of charge, to any person obtaining a copy
++ of this software and associated documentation files (the "Software"), to deal
++ in the Software without restriction, including without limitation the rights
++ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ copies of the Software, and to permit persons to whom the Software is
++ furnished to do so, subject to the following conditions:
++ .
++ The above copyright notice and this permission notice shall be included in
++ all copies or substantial portions of the Software.
++ .
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ THE SOFTWARE.
++Comment:
++ This license also known as "MIT" however FSF consider "MIT" labelling
++ ambiguous and copyright-format specification recommend to label such license
++ as "Expat".
++
++License: CC-BY-SA-3.0
++ Creative Commons Attribution-ShareAlike 3.0 Unported
++ ․
++ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
++ LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN
++ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION
++ ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE
++ INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
++ ITS USE.
++ ․
++ License
++ ․
++ THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
++ COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
++ COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
++ AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
++ ․
++ BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
++ TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY
++ BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS
++ CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
++ CONDITIONS.
++ ․
++ 1. Definitions
++ ․
++ a. "Adaptation" means a work based upon the Work, or upon the Work and
++ other pre-existing works, such as a translation, adaptation, derivative
++ work, arrangement of music or other alterations of a literary or
++ artistic work, or phonogram or performance and includes cinematographic
++ adaptations or any other form in which the Work may be recast,
++ transformed, or adapted including in any form recognizably derived from
++ the original, except that a work that constitutes a Collection will not
++ be considered an Adaptation for the purpose of this License. For the
++ avoidance of doubt, where the Work is a musical work, performance or
++ phonogram, the synchronization of the Work in timed-relation with a
++ moving image ("synching") will be considered an Adaptation for the
++ purpose of this License.
++ ․
++ b. "Collection" means a collection of literary or artistic works, such
++ as encyclopedias and anthologies, or performances, phonograms or
++ broadcasts, or other works or subject matter other than works listed in
++ Section 1(f) below, which, by reason of the selection and arrangement of
++ their contents, constitute intellectual creations, in which the Work is
++ included in its entirety in unmodified form along with one or more other
++ contributions, each constituting separate and independent works in
++ themselves, which together are assembled into a collective whole. A work
++ that constitutes a Collection will not be considered an Adaptation (as
++ defined below) for the purposes of this License.
++ ․
++ c. "Creative Commons Compatible License" means a license that is listed
++ at http://creativecommons.org/compatiblelicenses that has been approved
++ by Creative Commons as being essentially equivalent to this License,
++ including, at a minimum, because that license: (i) contains terms that
++ have the same purpose, meaning and effect as the License Elements of
++ this License; and, (ii) explicitly permits the relicensing of
++ adaptations of works made available under that license under this
++ License or a Creative Commons jurisdiction license with the same License
++ Elements as this License.
++ ․
++ d. "Distribute" means to make available to the public the original and
++ copies of the Work or Adaptation, as appropriate, through sale or other
++ transfer of ownership.
++ ․
++ e. "License Elements" means the following high-level license attributes
++ as selected by Licensor and indicated in the title of this License:
++ Attribution, ShareAlike.
++ ․
++ f. "Licensor" means the individual, individuals, entity or entities that
++ offer(s) the Work under the terms of this License.
++ ․
++ g. "Original Author" means, in the case of a literary or artistic work,
++ the individual, individuals, entity or entities who created the Work or
++ if no individual or entity can be identified, the publisher; and in
++ addition (i) in the case of a performance the actors, singers,
++ musicians, dancers, and other persons who act, sing, deliver, declaim,
++ play in, interpret or otherwise perform literary or artistic works or
++ expressions of folklore; (ii) in the case of a phonogram the producer
++ being the person or legal entity who first fixes the sounds of a
++ performance or other sounds; and, (iii) in the case of broadcasts, the
++ organization that transmits the broadcast.
++ ․
++ h. "Work" means the literary and/or artistic work offered under the
++ terms of this License including without limitation any production in the
++ literary, scientific and artistic domain, whatever may be the mode or
++ form of its expression including digital form, such as a book, pamphlet
++ and other writing; a lecture, address, sermon or other work of the same
++ nature; a dramatic or dramatico-musical work; a choreographic work or
++ entertainment in dumb show; a musical composition with or without words;
++ a cinematographic work to which are assimilated works expressed by a
++ process analogous to cinematography; a work of drawing, painting,
++ architecture, sculpture, engraving or lithography; a photographic work
++ to which are assimilated works expressed by a process analogous to
++ photography; a work of applied art; an illustration, map, plan, sketch
++ or three-dimensional work relative to geography, topography,
++ architecture or science; a performance; a broadcast; a phonogram; a
++ compilation of data to the extent it is protected as a copyrightable
++ work; or a work performed by a variety or circus performer to the extent
++ it is not otherwise considered a literary or artistic work.
++ ․
++ i. "You" means an individual or entity exercising rights under this
++ License who has not previously violated the terms of this License with
++ respect to the Work, or who has received express permission from the
++ Licensor to exercise rights under this License despite a previous
++ violation.
++ ․
++ j. "Publicly Perform" means to perform public recitations of the Work
++ and to communicate to the public those public recitations, by any means
++ or process, including by wire or wireless means or public digital
++ performances; to make available to the public Works in such a way that
++ members of the public may access these Works from a place and at a place
++ individually chosen by them; to perform the Work to the public by any
++ means or process and the communication to the public of the performances
++ of the Work, including by public digital performance; to broadcast and
++ rebroadcast the Work by any means including signs, sounds or images.
++ ․
++ k. "Reproduce" means to make copies of the Work by any means including
++ without limitation by sound or visual recordings and the right of
++ fixation and reproducing fixations of the Work, including storage of a
++ protected performance or phonogram in digital form or other electronic
++ medium.
++ ․
++ 2. Fair Dealing Rights. Nothing in this License is intended to reduce,
++ limit, or restrict any uses free from copyright or rights arising from
++ limitations or exceptions that are provided for in connection with the
++ copyright protection under copyright law or other applicable laws.
++ ․
++ 3. License Grant. Subject to the terms and conditions of this License,
++ Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
++ perpetual (for the duration of the applicable copyright) license to
++ exercise the rights in the Work as stated below:
++ ․
++ a. to Reproduce the Work, to incorporate the Work into one or more
++ Collections, and to Reproduce the Work as incorporated in the
++ Collections;
++ ․
++ b. to create and Reproduce Adaptations provided that any such
++ Adaptation, including any translation in any medium, takes reasonable
++ steps to clearly label, demarcate or otherwise identify that changes
++ were made to the original Work. For example, a translation could be
++ marked "The original work was translated from English to Spanish," or a
++ modification could indicate "The original work has been modified.";
++ ․
++ c. to Distribute and Publicly Perform the Work including as incorporated
++ in Collections; and,
++ ․
++ d. to Distribute and Publicly Perform Adaptations.
++ ․
++ e. For the avoidance of doubt:
++ ․
++ i. Non-waivable Compulsory License Schemes. In those jurisdictions in
++ which the right to collect royalties through any statutory or compulsory
++ licensing scheme cannot be waived, the Licensor reserves the exclusive
++ right to collect such royalties for any exercise by You of the rights
++ granted under this License;
++ ․
++ ii. Waivable Compulsory License Schemes. In those jurisdictions in which
++ the right to collect royalties through any statutory or compulsory
++ licensing scheme can be waived, the Licensor waives the exclusive right
++ to collect such royalties for any exercise by You of the rights granted
++ under this License; and,
++ ․
++ iii. Voluntary License Schemes. The Licensor waives the right to collect
++ royalties, whether individually or, in the event that the Licensor is a
++ member of a collecting society that administers voluntary licensing
++ schemes, via that society, from any exercise by You of the rights
++ granted under this License.
++ ․
++ The above rights may be exercised in all media and formats whether now
++ known or hereafter devised. The above rights include the right to make
++ such modifications as are technically necessary to exercise the rights
++ in other media and formats. Subject to Section 8(f), all rights not
++ expressly granted by Licensor are hereby reserved.
++ ․
++ 4. Restrictions. The license granted in Section 3 above is expressly
++ made subject to and limited by the following restrictions:
++ ․
++ a. You may Distribute or Publicly Perform the Work only under the terms
++ of this License. You must include a copy of, or the Uniform Resource
++ Identifier (URI) for, this License with every copy of the Work You
++ Distribute or Publicly Perform. You may not offer or impose any terms on
++ the Work that restrict the terms of this License or the ability of the
++ recipient of the Work to exercise the rights granted to that recipient
++ under the terms of the License. You may not sublicense the Work. You
++ must keep intact all notices that refer to this License and to the
++ disclaimer of warranties with every copy of the Work You Distribute or
++ Publicly Perform. When You Distribute or Publicly Perform the Work, You
++ may not impose any effective technological measures on the Work that
++ restrict the ability of a recipient of the Work from You to exercise the
++ rights granted to that recipient under the terms of the License. This
++ Section 4(a) applies to the Work as incorporated in a Collection, but
++ this does not require the Collection apart from the Work itself to be
++ made subject to the terms of this License. If You create a Collection,
++ upon notice from any Licensor You must, to the extent practicable,
++ remove from the Collection any credit as required by Section 4(c), as
++ requested. If You create an Adaptation, upon notice from any Licensor
++ You must, to the extent practicable, remove from the Adaptation any
++ credit as required by Section 4(c), as requested.
++ ․
++ b. You may Distribute or Publicly Perform an Adaptation only under the
++ terms of: (i) this License; (ii) a later version of this License with
++ the same License Elements as this License; (iii) a Creative Commons
++ jurisdiction license (either this or a later license version) that
++ contains the same License Elements as this License (e.g.,
++ Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons Compatible
++ License. If you license the Adaptation under one of the licenses
++ mentioned in (iv), you must comply with the terms of that license. If
++ you license the Adaptation under the terms of any of the licenses
++ mentioned in (i), (ii) or (iii) (the "Applicable License"), you must
++ comply with the terms of the Applicable License generally and the
++ following provisions: (I) You must include a copy of, or the URI for,
++ the Applicable License with every copy of each Adaptation You Distribute
++ or Publicly Perform; (II) You may not offer or impose any terms on the
++ Adaptation that restrict the terms of the Applicable License or the
++ ability of the recipient of the Adaptation to exercise the rights
++ granted to that recipient under the terms of the Applicable License;
++ (III) You must keep intact all notices that refer to the Applicable
++ License and to the disclaimer of warranties with every copy of the Work
++ as included in the Adaptation You Distribute or Publicly Perform; (IV)
++ when You Distribute or Publicly Perform the Adaptation, You may not
++ impose any effective technological measures on the Adaptation that
++ restrict the ability of a recipient of the Adaptation from You to
++ exercise the rights granted to that recipient under the terms of the
++ Applicable License. This Section 4(b) applies to the Adaptation as
++ incorporated in a Collection, but this does not require the Collection
++ apart from the Adaptation itself to be made subject to the terms of the
++ Applicable License.
++ ․
++ c. If You Distribute, or Publicly Perform the Work or any Adaptations or
++ Collections, You must, unless a request has been made pursuant to
++ Section 4(a), keep intact all copyright notices for the Work and
++ provide, reasonable to the medium or means You are utilizing: (i) the
++ name of the Original Author (or pseudonym, if applicable) if supplied,
++ and/or if the Original Author and/or Licensor designate another party or
++ parties (e.g., a sponsor institute, publishing entity, journal) for
++ attribution ("Attribution Parties") in Licensor's copyright notice,
++ terms of service or by other reasonable means, the name of such party or
++ parties; (ii) the title of the Work if supplied; (iii) to the extent
++ reasonably practicable, the URI, if any, that Licensor specifies to be
++ associated with the Work, unless such URI does not refer to the
++ copyright notice or licensing information for the Work; and (iv) ,
++ consistent with Ssection 3(b), in the case of an Adaptation, a credit
++ identifying the use of the Work in the Adaptation (e.g., "French
++ translation of the Work by Original Author," or "Screenplay based on
++ original Work by Original Author"). The credit required by this Section
++ 4(c) may be implemented in any reasonable manner; provided, however,
++ that in the case of a Adaptation or Collection, at a minimum such credit
++ will appear, if a credit for all contributing authors of the Adaptation
++ or Collection appears, then as part of these credits and in a manner at
++ least as prominent as the credits for the other contributing authors.
++ For the avoidance of doubt, You may only use the credit required by this
++ Section for the purpose of attribution in the manner set out above and,
++ by exercising Your rights under this License, You may not implicitly or
++ explicitly assert or imply any connection with, sponsorship or
++ endorsement by the Original Author, Licensor and/or Attribution Parties,
++ as appropriate, of You or Your use of the Work, without the separate,
++ express prior written permission of the Original Author, Licensor and/or
++ Attribution Parties.
++ ․
++ d. Except as otherwise agreed in writing by the Licensor or as may be
++ otherwise permitted by applicable law, if You Reproduce, Distribute or
++ Publicly Perform the Work either by itself or as part of any Adaptations
++ or Collections, You must not distort, mutilate, modify or take other
++ derogatory action in relation to the Work which would be prejudicial to
++ the Original Author's honor or reputation. Licensor agrees that in those
++ jurisdictions (e.g. Japan), in which any exercise of the right granted
++ in Section 3(b) of this License (the right to make Adaptations) would be
++ deemed to be a distortion, mutilation, modification or other derogatory
++ action prejudicial to the Original Author's honor and reputation, the
++ Licensor will waive or not assert, as appropriate, this Section, to the
++ fullest extent permitted by the applicable national law, to enable You
++ to reasonably exercise Your right under Section 3(b) of this License
++ (right to make Adaptations) but not otherwise.
++ ․
++ 5. Representations, Warranties and Disclaimer
++ ․
++ UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR
++ OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY
++ KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE,
++ INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY,
++ FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF
++ LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS,
++ WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE
++ EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
++ ․
++ 6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE
++ LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR
++ ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES
++ ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS
++ BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
++ ․
++ 7. Termination
++ ․
++ a. This License and the rights granted hereunder will terminate
++ automatically upon any breach by You of the terms of this License.
++ Individuals or entities who have received Adaptations or Collections
++ from You under this License, however, will not have their licenses
++ terminated provided such individuals or entities remain in full
++ compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will
++ survive any termination of this License.
++ ․
++ b. Subject to the above terms and conditions, the license granted here
++ is perpetual (for the duration of the applicable copyright in the Work).
++ Notwithstanding the above, Licensor reserves the right to release the
++ Work under different license terms or to stop distributing the Work at
++ any time; provided, however that any such election will not serve to
++ withdraw this License (or any other license that has been, or is
++ required to be, granted under the terms of this License), and this
++ License will continue in full force and effect unless terminated as
++ stated above.
++ ․
++ 8. Miscellaneous
++ ․
++ a. Each time You Distribute or Publicly Perform the Work or a
++ Collection, the Licensor offers to the recipient a license to the Work
++ on the same terms and conditions as the license granted to You under
++ this License.
++ ․
++ b. Each time You Distribute or Publicly Perform an Adaptation, Licensor
++ offers to the recipient a license to the original Work on the same terms
++ and conditions as the license granted to You under this License.
++ ․
++ c. If any provision of this License is invalid or unenforceable under
++ applicable law, it shall not affect the validity or enforceability of
++ the remainder of the terms of this License, and without further action
++ by the parties to this agreement, such provision shall be reformed to
++ the minimum extent necessary to make such provision valid and
++ enforceable.
++ ․
++ d. No term or provision of this License shall be deemed waived and no
++ breach consented to unless such waiver or consent shall be in writing
++ and signed by the party to be charged with such waiver or consent.
++ ․
++ e. This License constitutes the entire agreement between the parties
++ with respect to the Work licensed here. There are no understandings,
++ agreements or representations with respect to the Work not specified
++ here. Licensor shall not be bound by any additional provisions that may
++ appear in any communication from You. This License may not be modified
++ without the mutual written agreement of the Licensor and You.
++ ․
++ f. The rights granted under, and the subject matter referenced, in this
++ License were drafted utilizing the terminology of the Berne Convention
++ for the Protection of Literary and Artistic Works (as amended on
++ September 28, 1979), the Rome Convention of 1961, the WIPO Copyright
++ Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and
++ the Universal Copyright Convention (as revised on July 24, 1971). These
++ rights and subject matter take effect in the relevant jurisdiction in
++ which the License terms are sought to be enforced according to the
++ corresponding provisions of the implementation of those treaty
++ provisions in the applicable national law. If the standard suite of
++ rights granted under applicable copyright law includes additional rights
++ not granted under this License, such additional rights are deemed to be
++ included in the License; this License is not intended to restrict the
++ license of any rights under applicable law.
++ ․
++ ․
++ Creative Commons Notice
++ ․
++ Creative Commons is not a party to this License, and makes no warranty
++ whatsoever in connection with the Work. Creative Commons will not be
++ liable to You or any party on any legal theory for any damages
++ whatsoever, including without limitation any general, special,
++ incidental or consequential damages arising in connection to this
++ license. Notwithstanding the foregoing two (2) sentences, if Creative
++ Commons has expressly identified itself as the Licensor hereunder, it
++ shall have all rights and obligations of Licensor.
++ ․
++ Except for the limited purpose of indicating to the public that the Work
++ is licensed under the CCPL, Creative Commons does not authorize the use
++ by either party of the trademark "Creative Commons" or any related
++ trademark or logo of Creative Commons without the prior written consent
++ of Creative Commons. Any permitted use will be in compliance with
++ Creative Commons' then-current trademark usage guidelines, as may be
++ published on its website or otherwise made available upon request from
++ time to time. For the avoidance of doubt, this trademark restriction
++ does not form part of the License.
++ ․
++ Creative Commons may be contacted at http://creativecommons.org/.
++
++License: BSD-3-clause
++ Redistribution and use in source and binary forms, with or without
++ modification, are permitted provided that the following conditions
++ are met:
++ .
++ 1. Redistributions of source code must retain the above
++ copyright notice, this list of conditions and the following
++ disclaimer.
++ .
++ 2. Redistributions in binary form must reproduce the above
++ copyright notice, this list of conditions and the following
++ disclaimer in the documentation and/or other materials
++ provided with the distribution.
++ .
++ 3. Neither the name of the copyright holder nor the names of
++ its contributors may be used to endorse or promote products
++ derived from this software without specific prior written
++ permission.
++ .
++ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
++ FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
++ COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
++ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
++ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
++ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
++ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
++ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
++ OF THE POSSIBILITY OF SUCH DAMAGE.
--- /dev/null
--- /dev/null
++## /etc/default/ceph -- sourced by ceph-osd@.service (systemd)
++
++#CLUSTER=ceph
++#CONFIG=/etc/ceph/ceph.conf
++
++## create journal (if not exist) on OSD start; flush journal after OSD stop.
++## (useful for journals on tmpfs).
++#osd_prestart_sh="[ -r \"/var/lib/ceph/osd/${CLUSTER}-$1/journal\" ] || /usr/bin/ceph-osd --id $1 --mkjournal"
++#osd_poststop_sh="/usr/bin/ceph-osd --id $1 --flush-journal"
--- /dev/null
--- /dev/null
++#!/bin/sh
++
++##/etc/pm/sleep.d/25-ceph
++
++case $1 in
++suspend|hibernate)
++ service ceph stop
++;;
++resume|thaw)
++ service ceph start
++;;
++esac
--- /dev/null
--- /dev/null
++[DEFAULT]
++debian-branch = luminous/unstable
++pristine-tar = True
++
++[import-orig]
++filter = debian/*
--- /dev/null
--- /dev/null
++#!/bin/sh
++
++#/lib/systemd/system-sleep/ceph
++
++case $1 in
++pre)
++ /bin/systemctl stop ceph
++;;
++post)
++ /bin/systemctl start ceph
++;;
++esac
--- /dev/null
--- /dev/null
++[Unit]
++Description=Create Ceph client.admin key when possible
++PartOf=ceph-mon.service
++
++[Service]
++Environment=CLUSTER=ceph
++Environment=CONFIG=/etc/ceph/ceph.conf
++EnvironmentFile=-/etc/default/ceph
++ExecStart=/usr/sbin/ceph-create-keys --cluster ${CLUSTER} --id %H
--- /dev/null
--- /dev/null
++[Unit]
++Description=Ceph metadata server daemon (MDS)
++Documentation=man:ceph-mds
++After=network-online.target nss-lookup.target
++Wants=network-online.target nss-lookup.target
++PartOf=ceph.target
++
++[Service]
++LimitNOFILE=1048576
++LimitNPROC=1048576
++EnvironmentFile=-/etc/default/ceph
++Environment=CLUSTER=ceph
++ExecStart=/usr/bin/ceph-mds -f --cluster ${CLUSTER} --id %H --setuser ceph --setgroup ceph
++
++[Install]
++WantedBy=multi-user.target
--- /dev/null
--- /dev/null
++[Unit]
++Description=Ceph cluster monitor daemon
++Documentation=man:ceph-mon
++
++After=network-online.target local-fs.target ceph-create-keys.service
++Wants=network-online.target local-fs.target ceph-create-keys.service
++
++PartOf=ceph.target
++
++[Service]
++LimitNOFILE=1048576
++LimitNPROC=1048576
++EnvironmentFile=-/etc/default/ceph
++Environment=CLUSTER=ceph
++ExecStart=/usr/bin/ceph-mon -f --cluster ${CLUSTER} --id %H --setuser ceph --setgroup ceph
++ExecReload=/bin/kill -HUP $MAINPID
++Restart=on-failure
++RestartSec=30
++
++[Install]
++WantedBy=multi-user.target
--- /dev/null
--- /dev/null
++[Unit]
++Description=Ceph object storage daemon (OSD)
++Documentation=man:ceph-osd
++After=network-online.target
++Wants=network-online.target
++PartOf=ceph.service
++RequiresMountsFor=/var/lib/ceph/osd/ceph-%i
++
++[Service]
++Environment=CLUSTER=ceph
++Environment=CONFIG=/etc/ceph/ceph.conf
++EnvironmentFile=-/etc/default/ceph
++ExecStartPre=-/bin/sh -c '${osd_prestart_sh}' -- %i
++ExecStartPre=/usr/lib/ceph/ceph-osd-prestart.sh --id %i --cluster ${CLUSTER}
++ExecStart=/usr/bin/ceph-osd --id %i --foreground --cluster ${CLUSTER} -c ${CONFIG}
++ExecStopPost=-/bin/sh -c '${osd_poststop_sh}' -- %i
++LimitNOFILE=327680
++Restart=on-failure
++RestartSec=30
++
++[Install]
++WantedBy=multi-user.target
--- /dev/null
- usr/lib/libcephfs.so
+usr/include/cephfs/ceph_statx.h
+usr/include/cephfs/libcephfs.h
++usr/lib/*/libcephfs.so
--- /dev/null
- usr/lib/libcephfs_jni.so* usr/lib/jni
++usr/lib/*/libcephfs_jni.so* usr/lib/jni
--- /dev/null
- usr/lib/libcephfs.so.*
++usr/lib/*/libcephfs.so.*
--- /dev/null
--- /dev/null
++libcephfs.so.2 libcephfs2 #MINVER#
++ (regex|c++)"^_.*" 12.2.8
++ ceph_buffer_free@Base 12.2.8
++ ceph_chdir@Base 12.2.8
++ ceph_chmod@Base 12.2.8
++ ceph_chown@Base 12.2.8
++ ceph_close@Base 12.2.8
++ ceph_closedir@Base 12.2.8
++ ceph_conf_get@Base 12.2.8
++ ceph_conf_parse_argv@Base 12.2.8
++ ceph_conf_parse_env@Base 12.2.8
++ ceph_conf_read_file@Base 12.2.8
++ ceph_conf_set@Base 12.2.8
++ ceph_create@Base 12.2.8
++ ceph_create_from_rados@Base 12.2.8
++ ceph_create_with_context@Base 12.2.8
++ ceph_debug_get_fd_caps@Base 12.2.8
++ ceph_debug_get_file_caps@Base 12.2.8
++ ceph_fallocate@Base 12.2.8
++ ceph_fchmod@Base 12.2.8
++ ceph_fchown@Base 12.2.8
++ ceph_fgetxattr@Base 12.2.8
++ ceph_flistxattr@Base 12.2.8
++ ceph_flock@Base 12.2.8
++ ceph_fremovexattr@Base 12.2.8
++ ceph_fsetattrx@Base 12.2.8
++ ceph_fsetxattr@Base 12.2.8
++ ceph_fstatx@Base 12.2.8
++ ceph_fsync@Base 12.2.8
++ ceph_ftruncate@Base 12.2.8
++ ceph_get_cap_return_timeout@Base 12.2.8
++ ceph_get_default_data_pool_name@Base 12.2.8
++ ceph_get_file_extent_osds@Base 12.2.8
++ ceph_get_file_layout@Base 12.2.8
++ ceph_get_file_object_size@Base 12.2.8
++ ceph_get_file_pool@Base 12.2.8
++ ceph_get_file_pool_name@Base 12.2.8
++ ceph_get_file_replication@Base 12.2.8
++ ceph_get_file_stripe_address@Base 12.2.8
++ ceph_get_file_stripe_count@Base 12.2.8
++ ceph_get_file_stripe_unit@Base 12.2.8
++ ceph_get_local_osd@Base 12.2.8
++ ceph_get_mount_context@Base 12.2.8
++ ceph_get_osd_addr@Base 12.2.8
++ ceph_get_osd_crush_location@Base 12.2.8
++ ceph_get_path_layout@Base 12.2.8
++ ceph_get_path_object_size@Base 12.2.8
++ ceph_get_path_pool@Base 12.2.8
++ ceph_get_path_pool_name@Base 12.2.8
++ ceph_get_path_replication@Base 12.2.8
++ ceph_get_path_stripe_count@Base 12.2.8
++ ceph_get_path_stripe_unit@Base 12.2.8
++ ceph_get_pool_id@Base 12.2.8
++ ceph_get_pool_name@Base 12.2.8
++ ceph_get_pool_replication@Base 12.2.8
++ ceph_get_stripe_unit_granularity@Base 12.2.8
++ ceph_getcwd@Base 12.2.8
++ ceph_getdents@Base 12.2.8
++ ceph_getdnames@Base 12.2.8
++ ceph_getxattr@Base 12.2.8
++ ceph_init@Base 12.2.8
++ ceph_is_mounted@Base 12.2.8
++ ceph_lchown@Base 12.2.8
++ ceph_lgetxattr@Base 12.2.8
++ ceph_link@Base 12.2.8
++ ceph_listxattr@Base 12.2.8
++ ceph_ll_close@Base 12.2.8
++ ceph_ll_commit_blocks@Base 12.2.8
++ ceph_ll_create@Base 12.2.8
++ ceph_ll_delegation@Base 12.2.8
++ ceph_ll_file_layout@Base 12.2.8
++ ceph_ll_forget@Base 12.2.8
++ ceph_ll_fsync@Base 12.2.8
++ ceph_ll_get_inode@Base 12.2.8
++ ceph_ll_get_internal_offset@Base 12.2.8
++ ceph_ll_get_stripe_osd@Base 12.2.8
++ ceph_ll_getattr@Base 12.2.8
++ ceph_ll_getlk@Base 12.2.8
++ ceph_ll_getxattr@Base 12.2.8
++ ceph_ll_link@Base 12.2.8
++ ceph_ll_listxattr@Base 12.2.8
++ ceph_ll_lookup@Base 12.2.8
++ ceph_ll_lookup_inode@Base 12.2.8
++ ceph_ll_lookup_root@Base 12.2.8
++ ceph_ll_lseek@Base 12.2.8
++ ceph_ll_mkdir@Base 12.2.8
++ ceph_ll_mknod@Base 12.2.8
++ ceph_ll_num_osds@Base 12.2.8
++ ceph_ll_open@Base 12.2.8
++ ceph_ll_opendir@Base 12.2.8
++ ceph_ll_osdaddr@Base 12.2.8
++ ceph_ll_put@Base 12.2.8
++ ceph_ll_read@Base 12.2.8
++ ceph_ll_read_block@Base 12.2.8
++ ceph_ll_readlink@Base 12.2.8
++ ceph_ll_readv@Base 12.2.8
++ ceph_ll_releasedir@Base 12.2.8
++ ceph_ll_removexattr@Base 12.2.8
++ ceph_ll_rename@Base 12.2.8
++ ceph_ll_rmdir@Base 12.2.8
++ ceph_ll_setattr@Base 12.2.8
++ ceph_ll_setlk@Base 12.2.8
++ ceph_ll_setxattr@Base 12.2.8
++ ceph_ll_snap_seq@Base 12.2.8
++ ceph_ll_statfs@Base 12.2.8
++ ceph_ll_stripe_unit@Base 12.2.8
++ ceph_ll_symlink@Base 12.2.8
++ ceph_ll_sync_inode@Base 12.2.8
++ ceph_ll_unlink@Base 12.2.8
++ ceph_ll_walk@Base 12.2.8
++ ceph_ll_write@Base 12.2.8
++ ceph_ll_write_block@Base 12.2.8
++ ceph_ll_writev@Base 12.2.8
++ ceph_llistxattr@Base 12.2.8
++ ceph_localize_reads@Base 12.2.8
++ ceph_lremovexattr@Base 12.2.8
++ ceph_lseek@Base 12.2.8
++ ceph_lsetxattr@Base 12.2.8
++ ceph_mds_command@Base 12.2.8
++ ceph_mkdir@Base 12.2.8
++ ceph_mkdirs@Base 12.2.8
++ ceph_mknod@Base 12.2.8
++ ceph_mount@Base 12.2.8
++ ceph_mount_perms@Base 12.2.8
++ ceph_open@Base 12.2.8
++ ceph_open_layout@Base 12.2.8
++ ceph_opendir@Base 12.2.8
++ ceph_preadv@Base 12.2.8
++ ceph_pwritev@Base 12.2.8
++ ceph_read@Base 12.2.8
++ ceph_readdir@Base 12.2.8
++ ceph_readdir_r@Base 12.2.8
++ ceph_readdirplus_r@Base 12.2.8
++ ceph_readlink@Base 12.2.8
++ ceph_release@Base 12.2.8
++ ceph_removexattr@Base 12.2.8
++ ceph_rename@Base 12.2.8
++ ceph_rewinddir@Base 12.2.8
++ ceph_rmdir@Base 12.2.8
++ ceph_seekdir@Base 12.2.8
++ ceph_set_default_file_replication@Base 12.2.8
++ ceph_set_default_file_stripe_count@Base 12.2.8
++ ceph_set_default_file_stripe_unit@Base 12.2.8
++ ceph_set_default_object_size@Base 12.2.8
++ ceph_set_default_preferred_pg@Base 12.2.8
++ ceph_set_deleg_timeout@Base 12.2.8
++ ceph_setattrx@Base 12.2.8
++ ceph_setxattr@Base 12.2.8
++ ceph_shutdown@Base 12.2.8
++ ceph_statfs@Base 12.2.8
++ ceph_statx@Base 12.2.8
++ ceph_symlink@Base 12.2.8
++ ceph_sync_fs@Base 12.2.8
++ ceph_telldir@Base 12.2.8
++ ceph_truncate@Base 12.2.8
++ ceph_unlink@Base 12.2.8
++ ceph_unmount@Base 12.2.8
++ ceph_userperm_destroy@Base 12.2.8
++ ceph_userperm_new@Base 12.2.8
++ ceph_utime@Base 12.2.8
++ ceph_version@Base 12.2.8
++ ceph_write@Base 12.2.8
--- /dev/null
- usr/lib/librados.so
- usr/lib/librados_tp.so
+usr/bin/librados-config
+usr/include/rados/buffer.h
+usr/include/rados/buffer_fwd.h
+usr/include/rados/crc32c.h
+usr/include/rados/inline_memory.h
+usr/include/rados/librados.h
+usr/include/rados/librados.hpp
+usr/include/rados/memory.h
+usr/include/rados/page.h
+usr/include/rados/rados_types.h
+usr/include/rados/rados_types.hpp
++usr/lib/*/ceph/libceph-common.so
++usr/lib/*/librados.so
+usr/share/man/man8/librados-config.8
--- /dev/null
- usr/lib/ceph/libceph-common.so*
- usr/lib/librados.so.*
- usr/lib/librados_tp.so.*
++usr/lib/*/ceph/libceph-common.so.*
++usr/lib/*/librados.so.*
--- /dev/null
--- /dev/null
++libceph-common.so.0 librados2 #MINVER#
++ (regex|c++)"^_.*" 12.2.8
++ CEPH_CONF_FILE_DEFAULT@Base 12.2.8
++ MDS_GID_NONE@Base 12.2.8
++ MDS_RANK_NONE@Base 12.2.8
++ XXH32@Base 12.2.8
++ XXH32_canonicalFromHash@Base 12.2.8
++ XXH32_createState@Base 12.2.8
++ XXH32_digest@Base 12.2.8
++ XXH32_freeState@Base 12.2.8
++ XXH32_hashFromCanonical@Base 12.2.8
++ XXH32_reset@Base 12.2.8
++ XXH32_update@Base 12.2.8
++ XXH64@Base 12.2.8
++ XXH64_canonicalFromHash@Base 12.2.8
++ XXH64_createState@Base 12.2.8
++ XXH64_digest@Base 12.2.8
++ XXH64_freeState@Base 12.2.8
++ XXH64_hashFromCanonical@Base 12.2.8
++ XXH64_reset@Base 12.2.8
++ XXH64_update@Base 12.2.8
++ XXH_versionNumber@Base 12.2.8
++ boost_asio_detail_posix_thread_function@Base 12.2.8
++ (arch=arm64 armhf)ceph_arch_aarch64_crc32@Base 12.2.8
++ (arch=arm64 armhf)ceph_arch_aarch64_pmull@Base 12.2.8
++ (arch=arm64 armhf)ceph_arch_arm_probe@Base 12.2.8
++ (arch=i386 amd64)ceph_arch_intel_aesni@Base 12.2.8
++ (arch=i386 amd64)ceph_arch_intel_pclmul@Base 12.2.8
++ (arch=i386 amd64)ceph_arch_intel_probe@Base 12.2.8
++ (arch=i386 amd64)ceph_arch_intel_sse2@Base 12.2.8
++ (arch=i386 amd64)ceph_arch_intel_sse3@Base 12.2.8
++ (arch=i386 amd64)ceph_arch_intel_sse41@Base 12.2.8
++ (arch=i386 amd64)ceph_arch_intel_sse42@Base 12.2.8
++ (arch=i386 amd64)ceph_arch_intel_ssse3@Base 12.2.8
++ (arch=arm64 armhf)ceph_arch_neon@Base 12.2.8
++ (arch=ppc64el)ceph_arch_ppc_crc32@Base 12.2.8
++ (arch=ppc64el)ceph_arch_ppc_probe@Base 12.2.8
++ ceph_arch_probe@Base 12.2.8
++ ceph_arch_probed@Base 12.2.8
++ ceph_armor@Base 12.2.8
++ ceph_armor_line_break@Base 12.2.8
++ ceph_choose_crc32@Base 12.2.8
++ (arch=arm64)ceph_crc32c_aarch64@Base 12.2.8
++ ceph_crc32c_func@Base 12.2.8
++ ceph_crc32c_intel_baseline@Base 12.2.8
++ (arch=i386 amd64)ceph_crc32c_intel_fast@Base 12.2.8
++ (arch=i386 amd64)ceph_crc32c_intel_fast_exists@Base 12.2.8
++ (arch=ppc64el)ceph_crc32c_ppc@Base 12.2.8
++ ceph_crc32c_sctp@Base 12.2.8
++ ceph_crc32c_zeros@Base 12.2.8
++ ceph_options@Base 12.2.8
++ ceph_os_fgetxattr@Base 12.2.8
++ ceph_os_flistxattr@Base 12.2.8
++ ceph_os_fremovexattr@Base 12.2.8
++ ceph_os_fsetxattr@Base 12.2.8
++ ceph_os_getxattr@Base 12.2.8
++ ceph_os_listxattr@Base 12.2.8
++ ceph_os_removexattr@Base 12.2.8
++ ceph_os_setxattr@Base 12.2.8
++ ceph_unarmor@Base 12.2.8
++ ceph_ver__ae699615bac534ea496ee965ac6192cb7e0e07c0@Base 12.2.8
++ check_for_control_characters@Base 12.2.8
++ check_for_control_characters_cstr@Base 12.2.8
++ check_utf8@Base 12.2.8
++ check_utf8_cstr@Base 12.2.8
++ code_environment_to_str@Base 12.2.8
++ (arch=amd64)crc32_iscsi_00@Base 12.2.8
++ (arch=amd64)crc32_iscsi_00_slver@Base 12.2.8
++ (arch=amd64)crc32_iscsi_00_slver_00020014@Base 12.2.8
++ (arch=amd64)crc32_iscsi_zero_00@Base 12.2.8
++ (arch=amd64)crc32_iscsi_zero_00_slver@Base 12.2.8
++ (arch=amd64)crc32_iscsi_zero_00_slver_00020014@Base 12.2.8
++ crc32_table_iscsi_base@Base 12.2.8
++ (arch=ppc64el)crc_zero@Base 12.2.8
++ crush_add_bucket@Base 12.2.8
++ crush_add_list_bucket_item@Base 12.2.8
++ crush_add_rule@Base 12.2.8
++ crush_add_straw2_bucket_item@Base 12.2.8
++ crush_add_straw_bucket_item@Base 12.2.8
++ crush_add_tree_bucket_item@Base 12.2.8
++ crush_add_uniform_bucket_item@Base 12.2.8
++ crush_addition_is_unsafe@Base 12.2.8
++ crush_adjust_list_bucket_item_weight@Base 12.2.8
++ crush_adjust_straw2_bucket_item_weight@Base 12.2.8
++ crush_adjust_straw_bucket_item_weight@Base 12.2.8
++ crush_adjust_tree_bucket_item_weight@Base 12.2.8
++ crush_adjust_uniform_bucket_item_weight@Base 12.2.8
++ crush_bucket_add_item@Base 12.2.8
++ crush_bucket_adjust_item_weight@Base 12.2.8
++ crush_bucket_alg_name@Base 12.2.8
++ crush_bucket_remove_item@Base 12.2.8
++ crush_calc_straw@Base 12.2.8
++ crush_create@Base 12.2.8
++ crush_destroy@Base 12.2.8
++ crush_destroy_bucket@Base 12.2.8
++ crush_destroy_bucket_list@Base 12.2.8
++ crush_destroy_bucket_straw2@Base 12.2.8
++ crush_destroy_bucket_straw@Base 12.2.8
++ crush_destroy_bucket_tree@Base 12.2.8
++ crush_destroy_bucket_uniform@Base 12.2.8
++ crush_destroy_choose_args@Base 12.2.8
++ crush_destroy_rule@Base 12.2.8
++ crush_do_rule@Base 12.2.8
++ crush_finalize@Base 12.2.8
++ crush_find_rule@Base 12.2.8
++ crush_get_bucket_item_weight@Base 12.2.8
++ crush_get_next_bucket_id@Base 12.2.8
++ crush_hash32@Base 12.2.8
++ crush_hash32_2@Base 12.2.8
++ crush_hash32_3@Base 12.2.8
++ crush_hash32_4@Base 12.2.8
++ crush_hash32_5@Base 12.2.8
++ crush_hash_name@Base 12.2.8
++ crush_init_workspace@Base 12.2.8
++ crush_make_bucket@Base 12.2.8
++ crush_make_choose_args@Base 12.2.8
++ crush_make_list_bucket@Base 12.2.8
++ crush_make_rule@Base 12.2.8
++ crush_make_straw2_bucket@Base 12.2.8
++ crush_make_straw_bucket@Base 12.2.8
++ crush_make_tree_bucket@Base 12.2.8
++ crush_make_uniform_bucket@Base 12.2.8
++ crush_multiplication_is_unsafe@Base 12.2.8
++ crush_remove_bucket@Base 12.2.8
++ crush_remove_list_bucket_item@Base 12.2.8
++ crush_remove_straw2_bucket_item@Base 12.2.8
++ crush_remove_straw_bucket_item@Base 12.2.8
++ crush_remove_tree_bucket_item@Base 12.2.8
++ crush_remove_uniform_bucket_item@Base 12.2.8
++ crush_reweight_bucket@Base 12.2.8
++ crush_rule_set_step@Base 12.2.8
++ current_maxid@Base 12.2.8
++ decode_utf8@Base 12.2.8
++ encode_utf8@Base 12.2.8
++ escape_json_attr@Base 12.2.8
++ escape_json_attr_len@Base 12.2.8
++ escape_xml_attr@Base 12.2.8
++ escape_xml_attr_len@Base 12.2.8
++ g_ceph_context@Base 12.2.8
++ g_code_env@Base 12.2.8
++ g_conf@Base 12.2.8
++ g_lockdep@Base 12.2.8
++ get_linux_version@Base 12.2.8
++ get_process_name@Base 12.2.8
++ is_control_character@Base 12.2.8
++ last_freed_id@Base 12.2.8
++ mime_decode_from_qp@Base 12.2.8
++ mime_encode_as_qp@Base 12.2.8
++ module_has_param@Base 12.2.8
++ module_load@Base 12.2.8
++ pem_key@Base 12.2.8
++ pipe_cloexec@Base 12.2.8
++ resolve_addrs@Base 12.2.8
++ reverse_bits@Base 12.2.8
++ reverse_nibbles@Base 12.2.8
++ safe_cat@Base 12.2.8
++ safe_pread@Base 12.2.8
++ safe_pread_exact@Base 12.2.8
++ safe_pwrite@Base 12.2.8
++ safe_read@Base 12.2.8
++ safe_read_exact@Base 12.2.8
++ safe_read_file@Base 12.2.8
++ safe_splice@Base 12.2.8
++ safe_splice_exact@Base 12.2.8
++ safe_write@Base 12.2.8
++ safe_write_file@Base 12.2.8
++ sctp_crc_c@Base 12.2.8
++ sctp_crc_tableil8_o32@Base 12.2.8
++ sctp_crc_tableil8_o40@Base 12.2.8
++ sctp_crc_tableil8_o48@Base 12.2.8
++ sctp_crc_tableil8_o56@Base 12.2.8
++ sctp_crc_tableil8_o64@Base 12.2.8
++ sctp_crc_tableil8_o72@Base 12.2.8
++ sctp_crc_tableil8_o80@Base 12.2.8
++ sctp_crc_tableil8_o88@Base 12.2.8
++ set_legacy_crush_map@Base 12.2.8
++ set_optimal_crush_map@Base 12.2.8
++librados.so.2 librados2 #MINVER#
++ (regex|c++)"^_.*" 0.87
++ rados_aio_append@Base 0.72.2
++ rados_aio_cancel@Base 0.87
++ rados_aio_cmpext@Base 12.2.8
++ rados_aio_create_completion@Base 0.72.2
++ rados_aio_exec@Base 12.2.8
++ rados_aio_flush@Base 0.72.2
++ rados_aio_flush_async@Base 0.72.2
++ rados_aio_get_return_value@Base 0.72.2
++ rados_aio_get_version@Base 10.2.5
++ rados_aio_getxattr@Base 12.2.8
++ rados_aio_getxattrs@Base 12.2.8
++ rados_aio_ioctx_selfmanaged_snap_create@Base 12.2.8
++ rados_aio_ioctx_selfmanaged_snap_remove@Base 12.2.8
++ rados_aio_is_complete@Base 0.72.2
++ rados_aio_is_complete_and_cb@Base 0.72.2
++ rados_aio_is_safe@Base 0.72.2
++ rados_aio_is_safe_and_cb@Base 0.72.2
++ rados_aio_notify@Base 10.1.0
++ rados_aio_read@Base 0.72.2
++ rados_aio_read_op_operate@Base 0.79
++ rados_aio_release@Base 0.72.2
++ rados_aio_remove@Base 0.72.2
++ rados_aio_rmxattr@Base 12.2.8
++ rados_aio_setxattr@Base 12.2.8
++ rados_aio_stat@Base 0.72.2
++ rados_aio_unlock@Base 12.2.8
++ rados_aio_unwatch@Base 10.1.0
++ rados_aio_wait_for_complete@Base 0.72.2
++ rados_aio_wait_for_complete_and_cb@Base 0.72.2
++ rados_aio_wait_for_safe@Base 0.72.2
++ rados_aio_wait_for_safe_and_cb@Base 0.72.2
++ rados_aio_watch2@Base 12.2.8
++ rados_aio_watch@Base 10.1.0
++ rados_aio_watch_flush@Base 10.1.0
++ rados_aio_write@Base 0.72.2
++ rados_aio_write_full@Base 0.72.2
++ rados_aio_write_op_operate@Base 0.79
++ rados_aio_writesame@Base 12.2.8
++ rados_append@Base 0.72.2
++ rados_application_enable@Base 12.2.8
++ rados_application_list@Base 12.2.8
++ rados_application_metadata_get@Base 12.2.8
++ rados_application_metadata_list@Base 12.2.8
++ rados_application_metadata_remove@Base 12.2.8
++ rados_application_metadata_set@Base 12.2.8
++ rados_blacklist_add@Base 0.93
++ rados_break_lock@Base 0.72.2
++ rados_buffer_free@Base 0.72.2
++ rados_cache_pin@Base 10.1.0
++ rados_cache_unpin@Base 10.1.0
++ rados_cct@Base 0.72.2
++ rados_checksum@Base 12.2.8
++#MISSING: 12.2.8# rados_clone_range@Base 0.72.2
++ rados_cluster_fsid@Base 0.72.2
++ rados_cluster_stat@Base 0.72.2
++ rados_cmpext@Base 12.2.8
++ rados_conf_get@Base 0.72.2
++ rados_conf_parse_argv@Base 0.72.2
++ rados_conf_parse_argv_remainder@Base 0.72.2
++ rados_conf_parse_env@Base 0.72.2
++ rados_conf_read_file@Base 0.72.2
++ rados_conf_set@Base 0.72.2
++ rados_connect@Base 0.72.2
++ rados_create2@Base 0.72.2
++ rados_create@Base 0.72.2
++ rados_create_read_op@Base 0.79
++ rados_create_with_context@Base 0.72.2
++ rados_create_write_op@Base 0.79
++ rados_exec@Base 0.72.2
++ rados_get_instance_id@Base 0.72.2
++ rados_get_last_version@Base 0.72.2
++ rados_getxattr@Base 0.72.2
++ rados_getxattrs@Base 0.72.2
++ rados_getxattrs_end@Base 0.72.2
++ rados_getxattrs_next@Base 0.72.2
++ rados_inconsistent_pg_list@Base 10.1.0
++ rados_ioctx_cct@Base 0.72.2
++ rados_ioctx_create2@Base 0.93
++ rados_ioctx_create@Base 0.72.2
++ rados_ioctx_destroy@Base 0.72.2
++ rados_ioctx_get_cluster@Base 0.72.2
++ rados_ioctx_get_id@Base 0.72.2
++ rados_ioctx_get_pool_name@Base 0.72.2
++ rados_ioctx_locator_set_key@Base 0.72.2
++ rados_ioctx_pool_get_auid@Base 0.72.2
++ rados_ioctx_pool_required_alignment2@Base 10.1.0
++ rados_ioctx_pool_required_alignment@Base 0.79
++ rados_ioctx_pool_requires_alignment2@Base 10.1.0
++ rados_ioctx_pool_requires_alignment@Base 0.79
++ rados_ioctx_pool_set_auid@Base 0.72.2
++ rados_ioctx_pool_stat@Base 0.72.2
++ rados_ioctx_selfmanaged_snap_create@Base 0.72.2
++ rados_ioctx_selfmanaged_snap_remove@Base 0.72.2
++ rados_ioctx_selfmanaged_snap_rollback@Base 0.72.2
++ rados_ioctx_selfmanaged_snap_set_write_ctx@Base 0.72.2
++ rados_ioctx_set_namespace@Base 0.72.2
++ rados_ioctx_snap_create@Base 0.72.2
++ rados_ioctx_snap_get_name@Base 0.72.2
++ rados_ioctx_snap_get_stamp@Base 0.72.2
++ rados_ioctx_snap_list@Base 0.72.2
++ rados_ioctx_snap_lookup@Base 0.72.2
++ rados_ioctx_snap_remove@Base 0.72.2
++ rados_ioctx_snap_rollback@Base 0.80~rc1
++ rados_ioctx_snap_set_read@Base 0.72.2
++ rados_list_lockers@Base 0.72.2
++ rados_lock_exclusive@Base 0.72.2
++ rados_lock_shared@Base 0.72.2
++ rados_mgr_command@Base 12.2.8
++ rados_mon_command@Base 0.72.2
++ rados_mon_command_target@Base 0.72.2
++ rados_monitor_log2@Base 12.2.8
++ rados_monitor_log@Base 0.72.2
++ rados_nobjects_list_close@Base 0.93
++ rados_nobjects_list_get_cursor@Base 12.2.8
++ rados_nobjects_list_get_pg_hash_position@Base 0.93
++ rados_nobjects_list_next@Base 0.93
++ rados_nobjects_list_open@Base 0.93
++ rados_nobjects_list_seek@Base 0.93
++ rados_nobjects_list_seek_cursor@Base 12.2.8
++ rados_notify2@Base 0.93
++ rados_notify@Base 0.72.2
++ rados_notify_ack@Base 0.93
++ rados_object_list@Base 10.1.0
++ rados_object_list_begin@Base 10.1.0
++ rados_object_list_cursor_cmp@Base 10.1.0
++ rados_object_list_cursor_free@Base 10.1.0
++ rados_object_list_end@Base 10.1.0
++ rados_object_list_free@Base 10.1.0
++ rados_object_list_is_end@Base 10.1.0
++ rados_object_list_slice@Base 10.1.0
++ rados_objects_list_close@Base 0.72.2
++ rados_objects_list_get_pg_hash_position@Base 0.79
++ rados_objects_list_next@Base 0.72.2
++ rados_objects_list_open@Base 0.72.2
++ rados_objects_list_seek@Base 0.79
++ rados_omap_get_end@Base 0.79
++ rados_omap_get_next@Base 0.79
++ rados_osd_command@Base 0.72.2
++ rados_pg_command@Base 0.72.2
++ rados_ping_monitor@Base 0.72.2
++ rados_pool_create@Base 0.72.2
++ rados_pool_create_with_all@Base 0.72.2
++ rados_pool_create_with_auid@Base 0.72.2
++ rados_pool_create_with_crush_rule@Base 0.72.2
++ rados_pool_delete@Base 0.72.2
++ rados_pool_get_base_tier@Base 0.93
++ rados_pool_list@Base 0.72.2
++ rados_pool_lookup@Base 0.72.2
++ rados_pool_reverse_lookup@Base 0.72.2
++ rados_read@Base 0.72.2
++ rados_read_op_assert_exists@Base 0.79
++ rados_read_op_assert_version@Base 0.93
++ rados_read_op_checksum@Base 12.2.8
++ rados_read_op_cmpext@Base 12.2.8
++ rados_read_op_cmpxattr@Base 0.79
++ rados_read_op_exec@Base 0.79
++ rados_read_op_exec_user_buf@Base 0.79
++ rados_read_op_getxattrs@Base 0.79
++ rados_read_op_omap_cmp@Base 0.79
++ rados_read_op_omap_get_keys2@Base 12.2.8
++ rados_read_op_omap_get_keys@Base 0.79
++ rados_read_op_omap_get_vals2@Base 12.2.8
++ rados_read_op_omap_get_vals@Base 0.79
++ rados_read_op_omap_get_vals_by_keys@Base 0.79
++ rados_read_op_operate@Base 0.79
++ rados_read_op_read@Base 0.79
++ rados_read_op_set_flags@Base 0.79
++ rados_read_op_stat@Base 0.79
++ rados_release_read_op@Base 0.79
++ rados_release_write_op@Base 0.79
++ rados_remove@Base 0.72.2
++ rados_rmxattr@Base 0.72.2
++ rados_rollback@Base 0.72.2
++ rados_service_register@Base 12.2.8
++ rados_service_update_status@Base 12.2.8
++ rados_set_alloc_hint2@Base 12.2.8
++ rados_set_alloc_hint@Base 0.79
++ rados_setxattr@Base 0.72.2
++ rados_shutdown@Base 0.72.2
++ rados_stat@Base 0.72.2
++ rados_tmap_get@Base 0.72.2
++ rados_tmap_put@Base 0.72.2
++ rados_tmap_to_omap@Base 12.2.8
++ rados_tmap_update@Base 0.72.2
++ rados_trunc@Base 0.72.2
++ rados_unlock@Base 0.72.2
++ rados_unwatch2@Base 0.93
++ rados_unwatch@Base 0.72.2
++ rados_version@Base 0.72.2
++ rados_wait_for_latest_osdmap@Base 0.79
++ rados_watch2@Base 0.93
++ rados_watch3@Base 12.2.8
++ rados_watch@Base 0.72.2
++ rados_watch_check@Base 0.93
++ rados_watch_flush@Base 0.93
++ rados_write@Base 0.72.2
++ rados_write_full@Base 0.72.2
++ rados_write_op_append@Base 0.79
++ rados_write_op_assert_exists@Base 0.79
++ rados_write_op_assert_version@Base 0.93
++ rados_write_op_cmpext@Base 12.2.8
++ rados_write_op_cmpxattr@Base 0.79
++ rados_write_op_create@Base 0.79
++ rados_write_op_exec@Base 0.79
++ rados_write_op_omap_clear@Base 0.79
++ rados_write_op_omap_cmp@Base 0.79
++ rados_write_op_omap_rm_keys@Base 0.79
++ rados_write_op_omap_set@Base 0.79
++ rados_write_op_operate2@Base 10.1.0
++ rados_write_op_operate@Base 0.79
++ rados_write_op_remove@Base 0.79
++ rados_write_op_rmxattr@Base 0.79
++ rados_write_op_set_alloc_hint2@Base 12.2.8
++ rados_write_op_set_alloc_hint@Base 0.79
++ rados_write_op_set_flags@Base 0.79
++ rados_write_op_setxattr@Base 0.79
++ rados_write_op_truncate@Base 0.79
++ rados_write_op_write@Base 0.79
++ rados_write_op_write_full@Base 0.79
++ rados_write_op_writesame@Base 12.2.8
++ rados_write_op_zero@Base 0.79
++ rados_writesame@Base 12.2.8
--- /dev/null
- usr/lib/libradosstriper.so
+usr/include/radosstriper/libradosstriper.h
+usr/include/radosstriper/libradosstriper.hpp
++usr/lib/*/libradosstriper.so
--- /dev/null
- usr/lib/libradosstriper.so.*
++usr/lib/*/libradosstriper.so.*
--- /dev/null
--- /dev/null
++libradosstriper.so.1 libradosstriper1 #MINVER#
++ (regex|c++)"^_.*" 0.87
++ default_file_layout@Base 10.1.0
++ rados_striper_aio_append@Base 0.87
++ rados_striper_aio_flush@Base 0.87
++ rados_striper_aio_read@Base 0.87
++ rados_striper_aio_remove@Base 12.2.8
++ rados_striper_aio_stat@Base 12.2.8
++ rados_striper_aio_write@Base 0.87
++ rados_striper_aio_write_full@Base 0.87
++ rados_striper_append@Base 0.87
++ rados_striper_create@Base 0.87
++ rados_striper_destroy@Base 0.87
++ rados_striper_getxattr@Base 0.87
++ rados_striper_getxattrs@Base 0.87
++ rados_striper_getxattrs_end@Base 0.87
++ rados_striper_getxattrs_next@Base 0.87
++ rados_striper_multi_aio_create_completion@Base 0.87
++ rados_striper_multi_aio_get_return_value@Base 0.87
++ rados_striper_multi_aio_is_complete@Base 0.87
++ rados_striper_multi_aio_is_complete_and_cb@Base 0.87
++ rados_striper_multi_aio_is_safe@Base 0.87
++ rados_striper_multi_aio_is_safe_and_cb@Base 0.87
++ rados_striper_multi_aio_release@Base 0.87
++ rados_striper_multi_aio_wait_for_complete@Base 0.87
++ rados_striper_multi_aio_wait_for_complete_and_cb@Base 0.87
++ rados_striper_multi_aio_wait_for_safe@Base 0.87
++ rados_striper_multi_aio_wait_for_safe_and_cb@Base 0.87
++ rados_striper_read@Base 0.87
++ rados_striper_remove@Base 0.87
++ rados_striper_rmxattr@Base 0.87
++ rados_striper_set_object_layout_object_size@Base 0.87
++ rados_striper_set_object_layout_stripe_count@Base 0.87
++ rados_striper_set_object_layout_stripe_unit@Base 0.87
++ rados_striper_setxattr@Base 0.87
++ rados_striper_stat@Base 0.87
++ rados_striper_trunc@Base 0.87
++ rados_striper_write@Base 0.87
++ rados_striper_write_full@Base 0.87
--- /dev/null
- usr/lib/librbd.so
- usr/lib/librbd_tp.so
+usr/include/rbd/features.h
+usr/include/rbd/librbd.h
+usr/include/rbd/librbd.hpp
++usr/lib/*/librbd.so
--- /dev/null
- usr/lib/librbd.so.*
- usr/lib/librbd_tp.so.*
++usr/lib/*/librbd.so.*
--- /dev/null
--- /dev/null
++librbd.so.1 librbd1 #MINVER#
++ (regex|c++)"^_.*" 0.87
++ rbd_aio_close@Base 10.1.0
++ rbd_aio_compare_and_write@Base 12.2.8
++ rbd_aio_create_completion@Base 0.72.2
++ rbd_aio_discard@Base 0.72.2
++ rbd_aio_flush@Base 0.72.2
++ rbd_aio_get_arg@Base 10.1.0
++ rbd_aio_get_return_value@Base 0.72.2
++ rbd_aio_is_complete@Base 0.72.2
++ rbd_aio_mirror_image_demote@Base 12.2.8
++ rbd_aio_mirror_image_get_info@Base 12.2.8
++ rbd_aio_mirror_image_get_status@Base 12.2.8
++ rbd_aio_mirror_image_promote@Base 12.2.8
++ rbd_aio_open@Base 10.1.0
++ rbd_aio_open_by_id@Base 12.2.8
++ rbd_aio_open_by_id_read_only@Base 12.2.8
++ rbd_aio_open_read_only@Base 10.1.0
++ rbd_aio_read2@Base 0.93
++ rbd_aio_read@Base 0.72.2
++ rbd_aio_readv@Base 12.2.8
++ rbd_aio_release@Base 0.72.2
++ rbd_aio_wait_for_complete@Base 0.72.2
++ rbd_aio_write2@Base 0.93
++ rbd_aio_write@Base 0.72.2
++ rbd_aio_writesame@Base 12.2.8
++ rbd_aio_writev@Base 12.2.8
++ rbd_break_lock@Base 0.72.2
++ rbd_clone2@Base 0.72.2
++ rbd_clone3@Base 10.1.0
++ rbd_clone@Base 0.72.2
++ rbd_close@Base 0.72.2
++ rbd_compare_and_write@Base 12.2.8
++ rbd_copy2@Base 0.72.2
++ rbd_copy3@Base 10.1.0
++ rbd_copy4@Base 12.2.8
++ rbd_copy@Base 0.72.2
++ rbd_copy_with_progress2@Base 0.72.2
++ rbd_copy_with_progress3@Base 10.2.5
++ rbd_copy_with_progress4@Base 12.2.8
++ rbd_copy_with_progress@Base 0.72.2
++ rbd_create2@Base 0.72.2
++ rbd_create3@Base 0.72.2
++ rbd_create4@Base 10.1.0
++ rbd_create@Base 0.72.2
++ rbd_diff_iterate2@Base 9.2.0
++ rbd_diff_iterate@Base 0.72.2
++ rbd_discard@Base 0.72.2
++ rbd_flatten@Base 0.72.2
++ rbd_flatten_with_progress@Base 12.2.8
++ rbd_flush@Base 0.72.2
++ rbd_get_block_name_prefix@Base 12.2.8
++ rbd_get_create_timestamp@Base 12.2.8
++ rbd_get_data_pool_id@Base 12.2.8
++ rbd_get_features@Base 0.72.2
++ rbd_get_flags@Base 0.93
++ rbd_get_id@Base 12.2.8
++ rbd_get_old_format@Base 0.72.2
++ rbd_get_overlap@Base 0.72.2
++ rbd_get_parent_info2@Base 12.2.8
++ rbd_get_parent_info@Base 0.72.2
++ rbd_get_size@Base 0.72.2
++ rbd_get_stripe_count@Base 0.72.2
++ rbd_get_stripe_unit@Base 0.72.2
++ rbd_image_options_clear@Base 10.1.0
++ rbd_image_options_create@Base 10.1.0
++ rbd_image_options_destroy@Base 10.1.0
++ rbd_image_options_get_string@Base 10.1.0
++ rbd_image_options_get_uint64@Base 10.1.0
++ rbd_image_options_is_empty@Base 10.1.0
++ rbd_image_options_is_set@Base 10.2.5
++ rbd_image_options_set_string@Base 10.1.0
++ rbd_image_options_set_uint64@Base 10.1.0
++ rbd_image_options_unset@Base 10.1.0
++ rbd_invalidate_cache@Base 0.80.5-2~
++ rbd_is_exclusive_lock_owner@Base 0.93
++ rbd_list@Base 0.72.2
++ rbd_list_children@Base 0.72.2
++ rbd_list_lockers@Base 0.72.2
++ rbd_lock_acquire@Base 12.2.8
++ rbd_lock_break@Base 12.2.8
++ rbd_lock_exclusive@Base 0.72.2
++ rbd_lock_get_owners@Base 12.2.8
++ rbd_lock_get_owners_cleanup@Base 12.2.8
++ rbd_lock_release@Base 12.2.8
++ rbd_lock_shared@Base 0.72.2
++ rbd_metadata_get@Base 9.2.0
++ rbd_metadata_list@Base 9.2.0
++ rbd_metadata_remove@Base 9.2.0
++ rbd_metadata_set@Base 9.2.0
++ rbd_mirror_image_demote@Base 10.1.0
++ rbd_mirror_image_disable@Base 10.1.0
++ rbd_mirror_image_enable@Base 10.1.0
++ rbd_mirror_image_get_info@Base 10.1.0
++ rbd_mirror_image_get_status@Base 10.2.5
++ rbd_mirror_image_promote@Base 10.1.0
++ rbd_mirror_image_resync@Base 10.1.0
++ rbd_mirror_image_status_list@Base 10.2.5
++ rbd_mirror_image_status_list_cleanup@Base 10.2.5
++ rbd_mirror_image_status_summary@Base 10.2.5
++ rbd_mirror_mode_get@Base 10.1.0
++ rbd_mirror_mode_set@Base 10.1.0
++ rbd_mirror_peer_add@Base 10.1.0
++ rbd_mirror_peer_list@Base 10.1.0
++ rbd_mirror_peer_list_cleanup@Base 10.1.0
++ rbd_mirror_peer_remove@Base 10.1.0
++ rbd_mirror_peer_set_client@Base 10.1.0
++ rbd_mirror_peer_set_cluster@Base 10.1.0
++ rbd_open@Base 0.72.2
++ rbd_open_by_id@Base 12.2.8
++ rbd_open_by_id_read_only@Base 12.2.8
++ rbd_open_read_only@Base 0.72.2
++ rbd_poll_io_events@Base 10.1.0
++ rbd_read2@Base 0.93
++ rbd_read@Base 0.72.2
++ rbd_read_iterate2@Base 0.72.2
++ rbd_read_iterate@Base 0.72.2
++ rbd_rebuild_object_map@Base 9.2.0
++ rbd_remove@Base 0.72.2
++ rbd_remove_with_progress@Base 0.72.2
++ rbd_rename@Base 0.72.2
++ rbd_resize2@Base 12.2.8
++ rbd_resize@Base 0.72.2
++ rbd_resize_with_progress@Base 0.72.2
++ rbd_set_image_notification@Base 10.1.0
++ rbd_snap_create@Base 0.72.2
++ rbd_snap_get_limit@Base 12.2.8
++ rbd_snap_get_timestamp@Base 12.2.8
++ rbd_snap_is_protected@Base 0.72.2
++ rbd_snap_list@Base 0.72.2
++ rbd_snap_list_end@Base 0.72.2
++ rbd_snap_protect@Base 0.72.2
++ rbd_snap_remove2@Base 12.2.8
++ rbd_snap_remove@Base 0.72.2
++ rbd_snap_rename@Base 10.1.0
++ rbd_snap_rollback@Base 0.72.2
++ rbd_snap_rollback_with_progress@Base 0.72.2
++ rbd_snap_set@Base 0.72.2
++ rbd_snap_set_limit@Base 12.2.8
++ rbd_snap_unprotect@Base 0.72.2
++ rbd_stat@Base 0.72.2
++ rbd_trash_get@Base 12.2.8
++ rbd_trash_get_cleanup@Base 12.2.8
++ rbd_trash_list@Base 12.2.8
++ rbd_trash_list_cleanup@Base 12.2.8
++ rbd_trash_move@Base 12.2.8
++ rbd_trash_remove@Base 12.2.8
++ rbd_trash_remove_with_progress@Base 12.2.8
++ rbd_trash_restore@Base 12.2.8
++ rbd_unlock@Base 0.72.2
++ rbd_update_features@Base 9.2.0
++ rbd_update_unwatch@Base 10.2.5
++ rbd_update_watch@Base 10.2.5
++ rbd_version@Base 0.72.2
++ rbd_write2@Base 0.93
++ rbd_write@Base 0.72.2
++ rbd_writesame@Base 12.2.8
--- /dev/null
- usr/lib/librgw.so
+usr/include/rados/librgw.h
+usr/include/rados/rgw_file.h
++usr/lib/*/librgw.so
--- /dev/null
- usr/lib/librgw.so.*
++usr/lib/*/librgw.so.*
--- /dev/null
--- /dev/null
++.TH ceph-crush-location "1" "April 2014" "ceph-crush-location" "User Commands"
++.SH NAME
++ceph-crush-location \- get CRUSH location
++.SH DESCRIPTION
++Generate a CRUSH location for the given entity
++
++The CRUSH location consists of a list of key=value pairs, separated
++by spaces, all on a single line. This describes where in CRUSH
++hierarhcy this entity should be placed.
++
++.SH OPTIONS
++.TP 4
++\fB\-\-cluster\fR <clustername>
++name of the cluster (see /etc/ceph/$cluster.conf)
++.TP 4
++\fB\-\-type\fR <osd|mds|client>
++daemon/entity type
++.TP 4
++\fB\-\-id\fR <id>
++id (osd number, mds name, client name)
++
++.SH SEE ALSO
++.TP
++\fBceph-conf\fP(8)
--- /dev/null
--- /dev/null
++.TH mount.fuse.ceph "8" "March 2014" "ceph-fuse" "User Commands"
++.SH NAME
++mount.fuse.ceph \- wrapper around ceph-fuse
++.SH DESCRIPTION
++Helper to mount ceph-fuse from /etc/fstab. To use, add an entry like:
++
++.nf
++# DEVICE PATH TYPE OPTIONS
++ mount.fuse.ceph#conf=/etc/ceph/ceph.conf,id=admin /mnt/ceph fuse _netdev,noatime,allow_other 0 0
++ mount.fuse.ceph#conf=/etc/ceph/foo.conf,id=myuser /mnt/ceph2 fuse _netdev,noatime,allow_other 0 0
++.fi
++
++where the device field is a comma-separated list of options to pass on
++the command line. The examples above, for example, specify that
++ceph-fuse will authenticated as client.admin and client.myuser
++(respectively), and the second example also sets the "conf" option to
++"/etc/ceph/foo.conf" via the ceph-fuse command line. Any valid
++ceph-fuse option can be passed in this way.
++
++.SH OPTIONS
++.TP 4
++\fB\-\-conf\fR
++path to ceph cponfiguration file, usually "/etc/ceph/ceph.conf"
++.TP 4
++\fB\-\-id\fR
++user name
++
++.SH SEE ALSO
++.TP
++\fBceph-fuse\fP(8)
--- /dev/null
--- /dev/null
++/* Plugin for jQuery for working with colors.
++ *
++ * Version 1.1.
++ *
++ * Inspiration from jQuery color animation plugin by John Resig.
++ *
++ * Released under the MIT license by Ole Laursen, October 2009.
++ *
++ * Examples:
++ *
++ * $.color.parse("#fff").scale('rgb', 0.25).add('a', -0.5).toString()
++ * var c = $.color.extract($("#mydiv"), 'background-color');
++ * console.log(c.r, c.g, c.b, c.a);
++ * $.color.make(100, 50, 25, 0.4).toString() // returns "rgba(100,50,25,0.4)"
++ *
++ * Note that .scale() and .add() return the same modified object
++ * instead of making a new one.
++ *
++ * V. 1.1: Fix error handling so e.g. parsing an empty string does
++ * produce a color rather than just crashing.
++ */
++
++(function($) {
++ $.color = {};
++
++ // construct color object with some convenient chainable helpers
++ $.color.make = function (r, g, b, a) {
++ var o = {};
++ o.r = r || 0;
++ o.g = g || 0;
++ o.b = b || 0;
++ o.a = a != null ? a : 1;
++
++ o.add = function (c, d) {
++ for (var i = 0; i < c.length; ++i)
++ o[c.charAt(i)] += d;
++ return o.normalize();
++ };
++
++ o.scale = function (c, f) {
++ for (var i = 0; i < c.length; ++i)
++ o[c.charAt(i)] *= f;
++ return o.normalize();
++ };
++
++ o.toString = function () {
++ if (o.a >= 1.0) {
++ return "rgb("+[o.r, o.g, o.b].join(",")+")";
++ } else {
++ return "rgba("+[o.r, o.g, o.b, o.a].join(",")+")";
++ }
++ };
++
++ o.normalize = function () {
++ function clamp(min, value, max) {
++ return value < min ? min: (value > max ? max: value);
++ }
++
++ o.r = clamp(0, parseInt(o.r), 255);
++ o.g = clamp(0, parseInt(o.g), 255);
++ o.b = clamp(0, parseInt(o.b), 255);
++ o.a = clamp(0, o.a, 1);
++ return o;
++ };
++
++ o.clone = function () {
++ return $.color.make(o.r, o.b, o.g, o.a);
++ };
++
++ return o.normalize();
++ }
++
++ // extract CSS color property from element, going up in the DOM
++ // if it's "transparent"
++ $.color.extract = function (elem, css) {
++ var c;
++ do {
++ c = elem.css(css).toLowerCase();
++ // keep going until we find an element that has color, or
++ // we hit the body
++ if (c != '' && c != 'transparent')
++ break;
++ elem = elem.parent();
++ } while (!$.nodeName(elem.get(0), "body"));
++
++ // catch Safari's way of signalling transparent
++ if (c == "rgba(0, 0, 0, 0)")
++ c = "transparent";
++
++ return $.color.parse(c);
++ }
++
++ // parse CSS color string (like "rgb(10, 32, 43)" or "#fff"),
++ // returns color object, if parsing failed, you get black (0, 0,
++ // 0) out
++ $.color.parse = function (str) {
++ var res, m = $.color.make;
++
++ // Look for rgb(num,num,num)
++ if (res = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(str))
++ return m(parseInt(res[1], 10), parseInt(res[2], 10), parseInt(res[3], 10));
++
++ // Look for rgba(num,num,num,num)
++ if (res = /rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))
++ return m(parseInt(res[1], 10), parseInt(res[2], 10), parseInt(res[3], 10), parseFloat(res[4]));
++
++ // Look for rgb(num%,num%,num%)
++ if (res = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(str))
++ return m(parseFloat(res[1])*2.55, parseFloat(res[2])*2.55, parseFloat(res[3])*2.55);
++
++ // Look for rgba(num%,num%,num%,num)
++ if (res = /rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))
++ return m(parseFloat(res[1])*2.55, parseFloat(res[2])*2.55, parseFloat(res[3])*2.55, parseFloat(res[4]));
++
++ // Look for #a0b1c2
++ if (res = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str))
++ return m(parseInt(res[1], 16), parseInt(res[2], 16), parseInt(res[3], 16));
++
++ // Look for #fff
++ if (res = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(str))
++ return m(parseInt(res[1]+res[1], 16), parseInt(res[2]+res[2], 16), parseInt(res[3]+res[3], 16));
++
++ // Otherwise, we're most likely dealing with a named color
++ var name = $.trim(str).toLowerCase();
++ if (name == "transparent")
++ return m(255, 255, 255, 0);
++ else {
++ // default to black
++ res = lookupColors[name] || [0, 0, 0];
++ return m(res[0], res[1], res[2]);
++ }
++ }
++
++ var lookupColors = {
++ aqua:[0,255,255],
++ azure:[240,255,255],
++ beige:[245,245,220],
++ black:[0,0,0],
++ blue:[0,0,255],
++ brown:[165,42,42],
++ cyan:[0,255,255],
++ darkblue:[0,0,139],
++ darkcyan:[0,139,139],
++ darkgrey:[169,169,169],
++ darkgreen:[0,100,0],
++ darkkhaki:[189,183,107],
++ darkmagenta:[139,0,139],
++ darkolivegreen:[85,107,47],
++ darkorange:[255,140,0],
++ darkorchid:[153,50,204],
++ darkred:[139,0,0],
++ darksalmon:[233,150,122],
++ darkviolet:[148,0,211],
++ fuchsia:[255,0,255],
++ gold:[255,215,0],
++ green:[0,128,0],
++ indigo:[75,0,130],
++ khaki:[240,230,140],
++ lightblue:[173,216,230],
++ lightcyan:[224,255,255],
++ lightgreen:[144,238,144],
++ lightgrey:[211,211,211],
++ lightpink:[255,182,193],
++ lightyellow:[255,255,224],
++ lime:[0,255,0],
++ magenta:[255,0,255],
++ maroon:[128,0,0],
++ navy:[0,0,128],
++ olive:[128,128,0],
++ orange:[255,165,0],
++ pink:[255,192,203],
++ purple:[128,0,128],
++ violet:[128,0,128],
++ red:[255,0,0],
++ silver:[192,192,192],
++ white:[255,255,255],
++ yellow:[255,255,0]
++ };
++})(jQuery);
--- /dev/null
--- /dev/null
++/*!
++ * jQuery JavaScript Library v1.4.2
++ * http://jquery.com/
++ *
++ * Copyright 2010, John Resig
++ * Dual licensed under the MIT or GPL Version 2 licenses.
++ * http://jquery.org/license
++ *
++ * Includes Sizzle.js
++ * http://sizzlejs.com/
++ * Copyright 2010, The Dojo Foundation
++ * Released under the MIT, BSD, and GPL Licenses.
++ *
++ * Date: Sat Feb 13 22:33:48 2010 -0500
++ */
++(function( window, undefined ) {
++
++// Define a local copy of jQuery
++var jQuery = function( selector, context ) {
++ // The jQuery object is actually just the init constructor 'enhanced'
++ return new jQuery.fn.init( selector, context );
++ },
++
++ // Map over jQuery in case of overwrite
++ _jQuery = window.jQuery,
++
++ // Map over the $ in case of overwrite
++ _$ = window.$,
++
++ // Use the correct document accordingly with window argument (sandbox)
++ document = window.document,
++
++ // A central reference to the root jQuery(document)
++ rootjQuery,
++
++ // A simple way to check for HTML strings or ID strings
++ // (both of which we optimize for)
++ quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,
++
++ // Is it a simple selector
++ isSimple = /^.[^:#\[\.,]*$/,
++
++ // Check if a string has a non-whitespace character in it
++ rnotwhite = /\S/,
++
++ // Used for trimming whitespace
++ rtrim = /^(\s|\u00A0)+|(\s|\u00A0)+$/g,
++
++ // Match a standalone tag
++ rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,
++
++ // Keep a UserAgent string for use with jQuery.browser
++ userAgent = navigator.userAgent,
++
++ // For matching the engine and version of the browser
++ browserMatch,
++
++ // Has the ready events already been bound?
++ readyBound = false,
++
++ // The functions to execute on DOM ready
++ readyList = [],
++
++ // The ready event handler
++ DOMContentLoaded,
++
++ // Save a reference to some core methods
++ toString = Object.prototype.toString,
++ hasOwnProperty = Object.prototype.hasOwnProperty,
++ push = Array.prototype.push,
++ slice = Array.prototype.slice,
++ indexOf = Array.prototype.indexOf;
++
++jQuery.fn = jQuery.prototype = {
++ init: function( selector, context ) {
++ var match, elem, ret, doc;
++
++ // Handle $(""), $(null), or $(undefined)
++ if ( !selector ) {
++ return this;
++ }
++
++ // Handle $(DOMElement)
++ if ( selector.nodeType ) {
++ this.context = this[0] = selector;
++ this.length = 1;
++ return this;
++ }
++
++ // The body element only exists once, optimize finding it
++ if ( selector === "body" && !context ) {
++ this.context = document;
++ this[0] = document.body;
++ this.selector = "body";
++ this.length = 1;
++ return this;
++ }
++
++ // Handle HTML strings
++ if ( typeof selector === "string" ) {
++ // Are we dealing with HTML string or an ID?
++ match = quickExpr.exec( selector );
++
++ // Verify a match, and that no context was specified for #id
++ if ( match && (match[1] || !context) ) {
++
++ // HANDLE: $(html) -> $(array)
++ if ( match[1] ) {
++ doc = (context ? context.ownerDocument || context : document);
++
++ // If a single string is passed in and it's a single tag
++ // just do a createElement and skip the rest
++ ret = rsingleTag.exec( selector );
++
++ if ( ret ) {
++ if ( jQuery.isPlainObject( context ) ) {
++ selector = [ document.createElement( ret[1] ) ];
++ jQuery.fn.attr.call( selector, context, true );
++
++ } else {
++ selector = [ doc.createElement( ret[1] ) ];
++ }
++
++ } else {
++ ret = buildFragment( [ match[1] ], [ doc ] );
++ selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes;
++ }
++
++ return jQuery.merge( this, selector );
++
++ // HANDLE: $("#id")
++ } else {
++ elem = document.getElementById( match[2] );
++
++ if ( elem ) {
++ // Handle the case where IE and Opera return items
++ // by name instead of ID
++ if ( elem.id !== match[2] ) {
++ return rootjQuery.find( selector );
++ }
++
++ // Otherwise, we inject the element directly into the jQuery object
++ this.length = 1;
++ this[0] = elem;
++ }
++
++ this.context = document;
++ this.selector = selector;
++ return this;
++ }
++
++ // HANDLE: $("TAG")
++ } else if ( !context && /^\w+$/.test( selector ) ) {
++ this.selector = selector;
++ this.context = document;
++ selector = document.getElementsByTagName( selector );
++ return jQuery.merge( this, selector );
++
++ // HANDLE: $(expr, $(...))
++ } else if ( !context || context.jquery ) {
++ return (context || rootjQuery).find( selector );
++
++ // HANDLE: $(expr, context)
++ // (which is just equivalent to: $(context).find(expr)
++ } else {
++ return jQuery( context ).find( selector );
++ }
++
++ // HANDLE: $(function)
++ // Shortcut for document ready
++ } else if ( jQuery.isFunction( selector ) ) {
++ return rootjQuery.ready( selector );
++ }
++
++ if (selector.selector !== undefined) {
++ this.selector = selector.selector;
++ this.context = selector.context;
++ }
++
++ return jQuery.makeArray( selector, this );
++ },
++
++ // Start with an empty selector
++ selector: "",
++
++ // The current version of jQuery being used
++ jquery: "1.4.2",
++
++ // The default length of a jQuery object is 0
++ length: 0,
++
++ // The number of elements contained in the matched element set
++ size: function() {
++ return this.length;
++ },
++
++ toArray: function() {
++ return slice.call( this, 0 );
++ },
++
++ // Get the Nth element in the matched element set OR
++ // Get the whole matched element set as a clean array
++ get: function( num ) {
++ return num == null ?
++
++ // Return a 'clean' array
++ this.toArray() :
++
++ // Return just the object
++ ( num < 0 ? this.slice(num)[ 0 ] : this[ num ] );
++ },
++
++ // Take an array of elements and push it onto the stack
++ // (returning the new matched element set)
++ pushStack: function( elems, name, selector ) {
++ // Build a new jQuery matched element set
++ var ret = jQuery();
++
++ if ( jQuery.isArray( elems ) ) {
++ push.apply( ret, elems );
++
++ } else {
++ jQuery.merge( ret, elems );
++ }
++
++ // Add the old object onto the stack (as a reference)
++ ret.prevObject = this;
++
++ ret.context = this.context;
++
++ if ( name === "find" ) {
++ ret.selector = this.selector + (this.selector ? " " : "") + selector;
++ } else if ( name ) {
++ ret.selector = this.selector + "." + name + "(" + selector + ")";
++ }
++
++ // Return the newly-formed element set
++ return ret;
++ },
++
++ // Execute a callback for every element in the matched set.
++ // (You can seed the arguments with an array of args, but this is
++ // only used internally.)
++ each: function( callback, args ) {
++ return jQuery.each( this, callback, args );
++ },
++
++ ready: function( fn ) {
++ // Attach the listeners
++ jQuery.bindReady();
++
++ // If the DOM is already ready
++ if ( jQuery.isReady ) {
++ // Execute the function immediately
++ fn.call( document, jQuery );
++
++ // Otherwise, remember the function for later
++ } else if ( readyList ) {
++ // Add the function to the wait list
++ readyList.push( fn );
++ }
++
++ return this;
++ },
++
++ eq: function( i ) {
++ return i === -1 ?
++ this.slice( i ) :
++ this.slice( i, +i + 1 );
++ },
++
++ first: function() {
++ return this.eq( 0 );
++ },
++
++ last: function() {
++ return this.eq( -1 );
++ },
++
++ slice: function() {
++ return this.pushStack( slice.apply( this, arguments ),
++ "slice", slice.call(arguments).join(",") );
++ },
++
++ map: function( callback ) {
++ return this.pushStack( jQuery.map(this, function( elem, i ) {
++ return callback.call( elem, i, elem );
++ }));
++ },
++
++ end: function() {
++ return this.prevObject || jQuery(null);
++ },
++
++ // For internal use only.
++ // Behaves like an Array's method, not like a jQuery method.
++ push: push,
++ sort: [].sort,
++ splice: [].splice
++};
++
++// Give the init function the jQuery prototype for later instantiation
++jQuery.fn.init.prototype = jQuery.fn;
++
++jQuery.extend = jQuery.fn.extend = function() {
++ // copy reference to target object
++ var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy;
++
++ // Handle a deep copy situation
++ if ( typeof target === "boolean" ) {
++ deep = target;
++ target = arguments[1] || {};
++ // skip the boolean and the target
++ i = 2;
++ }
++
++ // Handle case when target is a string or something (possible in deep copy)
++ if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
++ target = {};
++ }
++
++ // extend jQuery itself if only one argument is passed
++ if ( length === i ) {
++ target = this;
++ --i;
++ }
++
++ for ( ; i < length; i++ ) {
++ // Only deal with non-null/undefined values
++ if ( (options = arguments[ i ]) != null ) {
++ // Extend the base object
++ for ( name in options ) {
++ src = target[ name ];
++ copy = options[ name ];
++
++ // Prevent never-ending loop
++ if ( target === copy ) {
++ continue;
++ }
++
++ // Recurse if we're merging object literal values or arrays
++ if ( deep && copy && ( jQuery.isPlainObject(copy) || jQuery.isArray(copy) ) ) {
++ var clone = src && ( jQuery.isPlainObject(src) || jQuery.isArray(src) ) ? src
++ : jQuery.isArray(copy) ? [] : {};
++
++ // Never move original objects, clone them
++ target[ name ] = jQuery.extend( deep, clone, copy );
++
++ // Don't bring in undefined values
++ } else if ( copy !== undefined ) {
++ target[ name ] = copy;
++ }
++ }
++ }
++ }
++
++ // Return the modified object
++ return target;
++};
++
++jQuery.extend({
++ noConflict: function( deep ) {
++ window.$ = _$;
++
++ if ( deep ) {
++ window.jQuery = _jQuery;
++ }
++
++ return jQuery;
++ },
++
++ // Is the DOM ready to be used? Set to true once it occurs.
++ isReady: false,
++
++ // Handle when the DOM is ready
++ ready: function() {
++ // Make sure that the DOM is not already loaded
++ if ( !jQuery.isReady ) {
++ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
++ if ( !document.body ) {
++ return setTimeout( jQuery.ready, 13 );
++ }
++
++ // Remember that the DOM is ready
++ jQuery.isReady = true;
++
++ // If there are functions bound, to execute
++ if ( readyList ) {
++ // Execute all of them
++ var fn, i = 0;
++ while ( (fn = readyList[ i++ ]) ) {
++ fn.call( document, jQuery );
++ }
++
++ // Reset the list of functions
++ readyList = null;
++ }
++
++ // Trigger any bound ready events
++ if ( jQuery.fn.triggerHandler ) {
++ jQuery( document ).triggerHandler( "ready" );
++ }
++ }
++ },
++
++ bindReady: function() {
++ if ( readyBound ) {
++ return;
++ }
++
++ readyBound = true;
++
++ // Catch cases where $(document).ready() is called after the
++ // browser event has already occurred.
++ if ( document.readyState === "complete" ) {
++ return jQuery.ready();
++ }
++
++ // Mozilla, Opera and webkit nightlies currently support this event
++ if ( document.addEventListener ) {
++ // Use the handy event callback
++ document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
++
++ // A fallback to window.onload, that will always work
++ window.addEventListener( "load", jQuery.ready, false );
++
++ // If IE event model is used
++ } else if ( document.attachEvent ) {
++ // ensure firing before onload,
++ // maybe late but safe also for iframes
++ document.attachEvent("onreadystatechange", DOMContentLoaded);
++
++ // A fallback to window.onload, that will always work
++ window.attachEvent( "onload", jQuery.ready );
++
++ // If IE and not a frame
++ // continually check to see if the document is ready
++ var toplevel = false;
++
++ try {
++ toplevel = window.frameElement == null;
++ } catch(e) {}
++
++ if ( document.documentElement.doScroll && toplevel ) {
++ doScrollCheck();
++ }
++ }
++ },
++
++ // See test/unit/core.js for details concerning isFunction.
++ // Since version 1.3, DOM methods and functions like alert
++ // aren't supported. They return false on IE (#2968).
++ isFunction: function( obj ) {
++ return toString.call(obj) === "[object Function]";
++ },
++
++ isArray: function( obj ) {
++ return toString.call(obj) === "[object Array]";
++ },
++
++ isPlainObject: function( obj ) {
++ // Must be an Object.
++ // Because of IE, we also have to check the presence of the constructor property.
++ // Make sure that DOM nodes and window objects don't pass through, as well
++ if ( !obj || toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval ) {
++ return false;
++ }
++
++ // Not own constructor property must be Object
++ if ( obj.constructor
++ && !hasOwnProperty.call(obj, "constructor")
++ && !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf") ) {
++ return false;
++ }
++
++ // Own properties are enumerated firstly, so to speed up,
++ // if last one is own, then all properties are own.
++
++ var key;
++ for ( key in obj ) {}
++
++ return key === undefined || hasOwnProperty.call( obj, key );
++ },
++
++ isEmptyObject: function( obj ) {
++ for ( var name in obj ) {
++ return false;
++ }
++ return true;
++ },
++
++ error: function( msg ) {
++ throw msg;
++ },
++
++ parseJSON: function( data ) {
++ if ( typeof data !== "string" || !data ) {
++ return null;
++ }
++
++ // Make sure leading/trailing whitespace is removed (IE can't handle it)
++ data = jQuery.trim( data );
++
++ // Make sure the incoming data is actual JSON
++ // Logic borrowed from http://json.org/json2.js
++ if ( /^[\],:{}\s]*$/.test(data.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@")
++ .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]")
++ .replace(/(?:^|:|,)(?:\s*\[)+/g, "")) ) {
++
++ // Try to use the native JSON parser first
++ return window.JSON && window.JSON.parse ?
++ window.JSON.parse( data ) :
++ (new Function("return " + data))();
++
++ } else {
++ jQuery.error( "Invalid JSON: " + data );
++ }
++ },
++
++ noop: function() {},
++
++ // Evalulates a script in a global context
++ globalEval: function( data ) {
++ if ( data && rnotwhite.test(data) ) {
++ // Inspired by code by Andrea Giammarchi
++ // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html
++ var head = document.getElementsByTagName("head")[0] || document.documentElement,
++ script = document.createElement("script");
++
++ script.type = "text/javascript";
++
++ if ( jQuery.support.scriptEval ) {
++ script.appendChild( document.createTextNode( data ) );
++ } else {
++ script.text = data;
++ }
++
++ // Use insertBefore instead of appendChild to circumvent an IE6 bug.
++ // This arises when a base node is used (#2709).
++ head.insertBefore( script, head.firstChild );
++ head.removeChild( script );
++ }
++ },
++
++ nodeName: function( elem, name ) {
++ return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
++ },
++
++ // args is for internal usage only
++ each: function( object, callback, args ) {
++ var name, i = 0,
++ length = object.length,
++ isObj = length === undefined || jQuery.isFunction(object);
++
++ if ( args ) {
++ if ( isObj ) {
++ for ( name in object ) {
++ if ( callback.apply( object[ name ], args ) === false ) {
++ break;
++ }
++ }
++ } else {
++ for ( ; i < length; ) {
++ if ( callback.apply( object[ i++ ], args ) === false ) {
++ break;
++ }
++ }
++ }
++
++ // A special, fast, case for the most common use of each
++ } else {
++ if ( isObj ) {
++ for ( name in object ) {
++ if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
++ break;
++ }
++ }
++ } else {
++ for ( var value = object[0];
++ i < length && callback.call( value, i, value ) !== false; value = object[++i] ) {}
++ }
++ }
++
++ return object;
++ },
++
++ trim: function( text ) {
++ return (text || "").replace( rtrim, "" );
++ },
++
++ // results is for internal usage only
++ makeArray: function( array, results ) {
++ var ret = results || [];
++
++ if ( array != null ) {
++ // The window, strings (and functions) also have 'length'
++ // The extra typeof function check is to prevent crashes
++ // in Safari 2 (See: #3039)
++ if ( array.length == null || typeof array === "string" || jQuery.isFunction(array) || (typeof array !== "function" && array.setInterval) ) {
++ push.call( ret, array );
++ } else {
++ jQuery.merge( ret, array );
++ }
++ }
++
++ return ret;
++ },
++
++ inArray: function( elem, array ) {
++ if ( array.indexOf ) {
++ return array.indexOf( elem );
++ }
++
++ for ( var i = 0, length = array.length; i < length; i++ ) {
++ if ( array[ i ] === elem ) {
++ return i;
++ }
++ }
++
++ return -1;
++ },
++
++ merge: function( first, second ) {
++ var i = first.length, j = 0;
++
++ if ( typeof second.length === "number" ) {
++ for ( var l = second.length; j < l; j++ ) {
++ first[ i++ ] = second[ j ];
++ }
++
++ } else {
++ while ( second[j] !== undefined ) {
++ first[ i++ ] = second[ j++ ];
++ }
++ }
++
++ first.length = i;
++
++ return first;
++ },
++
++ grep: function( elems, callback, inv ) {
++ var ret = [];
++
++ // Go through the array, only saving the items
++ // that pass the validator function
++ for ( var i = 0, length = elems.length; i < length; i++ ) {
++ if ( !inv !== !callback( elems[ i ], i ) ) {
++ ret.push( elems[ i ] );
++ }
++ }
++
++ return ret;
++ },
++
++ // arg is for internal usage only
++ map: function( elems, callback, arg ) {
++ var ret = [], value;
++
++ // Go through the array, translating each of the items to their
++ // new value (or values).
++ for ( var i = 0, length = elems.length; i < length; i++ ) {
++ value = callback( elems[ i ], i, arg );
++
++ if ( value != null ) {
++ ret[ ret.length ] = value;
++ }
++ }
++
++ return ret.concat.apply( [], ret );
++ },
++
++ // A global GUID counter for objects
++ guid: 1,
++
++ proxy: function( fn, proxy, thisObject ) {
++ if ( arguments.length === 2 ) {
++ if ( typeof proxy === "string" ) {
++ thisObject = fn;
++ fn = thisObject[ proxy ];
++ proxy = undefined;
++
++ } else if ( proxy && !jQuery.isFunction( proxy ) ) {
++ thisObject = proxy;
++ proxy = undefined;
++ }
++ }
++
++ if ( !proxy && fn ) {
++ proxy = function() {
++ return fn.apply( thisObject || this, arguments );
++ };
++ }
++
++ // Set the guid of unique handler to the same of original handler, so it can be removed
++ if ( fn ) {
++ proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
++ }
++
++ // So proxy can be declared as an argument
++ return proxy;
++ },
++
++ // Use of jQuery.browser is frowned upon.
++ // More details: http://docs.jquery.com/Utilities/jQuery.browser
++ uaMatch: function( ua ) {
++ ua = ua.toLowerCase();
++
++ var match = /(webkit)[ \/]([\w.]+)/.exec( ua ) ||
++ /(opera)(?:.*version)?[ \/]([\w.]+)/.exec( ua ) ||
++ /(msie) ([\w.]+)/.exec( ua ) ||
++ !/compatible/.test( ua ) && /(mozilla)(?:.*? rv:([\w.]+))?/.exec( ua ) ||
++ [];
++
++ return { browser: match[1] || "", version: match[2] || "0" };
++ },
++
++ browser: {}
++});
++
++browserMatch = jQuery.uaMatch( userAgent );
++if ( browserMatch.browser ) {
++ jQuery.browser[ browserMatch.browser ] = true;
++ jQuery.browser.version = browserMatch.version;
++}
++
++// Deprecated, use jQuery.browser.webkit instead
++if ( jQuery.browser.webkit ) {
++ jQuery.browser.safari = true;
++}
++
++if ( indexOf ) {
++ jQuery.inArray = function( elem, array ) {
++ return indexOf.call( array, elem );
++ };
++}
++
++// All jQuery objects should point back to these
++rootjQuery = jQuery(document);
++
++// Cleanup functions for the document ready method
++if ( document.addEventListener ) {
++ DOMContentLoaded = function() {
++ document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
++ jQuery.ready();
++ };
++
++} else if ( document.attachEvent ) {
++ DOMContentLoaded = function() {
++ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
++ if ( document.readyState === "complete" ) {
++ document.detachEvent( "onreadystatechange", DOMContentLoaded );
++ jQuery.ready();
++ }
++ };
++}
++
++// The DOM ready check for Internet Explorer
++function doScrollCheck() {
++ if ( jQuery.isReady ) {
++ return;
++ }
++
++ try {
++ // If IE is used, use the trick by Diego Perini
++ // http://javascript.nwbox.com/IEContentLoaded/
++ document.documentElement.doScroll("left");
++ } catch( error ) {
++ setTimeout( doScrollCheck, 1 );
++ return;
++ }
++
++ // and execute any waiting functions
++ jQuery.ready();
++}
++
++function evalScript( i, elem ) {
++ if ( elem.src ) {
++ jQuery.ajax({
++ url: elem.src,
++ async: false,
++ dataType: "script"
++ });
++ } else {
++ jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" );
++ }
++
++ if ( elem.parentNode ) {
++ elem.parentNode.removeChild( elem );
++ }
++}
++
++// Mutifunctional method to get and set values to a collection
++// The value/s can be optionally by executed if its a function
++function access( elems, key, value, exec, fn, pass ) {
++ var length = elems.length;
++
++ // Setting many attributes
++ if ( typeof key === "object" ) {
++ for ( var k in key ) {
++ access( elems, k, key[k], exec, fn, value );
++ }
++ return elems;
++ }
++
++ // Setting one attribute
++ if ( value !== undefined ) {
++ // Optionally, function values get executed if exec is true
++ exec = !pass && exec && jQuery.isFunction(value);
++
++ for ( var i = 0; i < length; i++ ) {
++ fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
++ }
++
++ return elems;
++ }
++
++ // Getting an attribute
++ return length ? fn( elems[0], key ) : undefined;
++}
++
++function now() {
++ return (new Date).getTime();
++}
++(function() {
++
++ jQuery.support = {};
++
++ var root = document.documentElement,
++ script = document.createElement("script"),
++ div = document.createElement("div"),
++ id = "script" + now();
++
++ div.style.display = "none";
++ div.innerHTML = " <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
++
++ var all = div.getElementsByTagName("*"),
++ a = div.getElementsByTagName("a")[0];
++
++ // Can't get basic test support
++ if ( !all || !all.length || !a ) {
++ return;
++ }
++
++ jQuery.support = {
++ // IE strips leading whitespace when .innerHTML is used
++ leadingWhitespace: div.firstChild.nodeType === 3,
++
++ // Make sure that tbody elements aren't automatically inserted
++ // IE will insert them into empty tables
++ tbody: !div.getElementsByTagName("tbody").length,
++
++ // Make sure that link elements get serialized correctly by innerHTML
++ // This requires a wrapper element in IE
++ htmlSerialize: !!div.getElementsByTagName("link").length,
++
++ // Get the style information from getAttribute
++ // (IE uses .cssText insted)
++ style: /red/.test( a.getAttribute("style") ),
++
++ // Make sure that URLs aren't manipulated
++ // (IE normalizes it by default)
++ hrefNormalized: a.getAttribute("href") === "/a",
++
++ // Make sure that element opacity exists
++ // (IE uses filter instead)
++ // Use a regex to work around a WebKit issue. See #5145
++ opacity: /^0.55$/.test( a.style.opacity ),
++
++ // Verify style float existence
++ // (IE uses styleFloat instead of cssFloat)
++ cssFloat: !!a.style.cssFloat,
++
++ // Make sure that if no value is specified for a checkbox
++ // that it defaults to "on".
++ // (WebKit defaults to "" instead)
++ checkOn: div.getElementsByTagName("input")[0].value === "on",
++
++ // Make sure that a selected-by-default option has a working selected property.
++ // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
++ optSelected: document.createElement("select").appendChild( document.createElement("option") ).selected,
++
++ parentNode: div.removeChild( div.appendChild( document.createElement("div") ) ).parentNode === null,
++
++ // Will be defined later
++ deleteExpando: true,
++ checkClone: false,
++ scriptEval: false,
++ noCloneEvent: true,
++ boxModel: null
++ };
++
++ script.type = "text/javascript";
++ try {
++ script.appendChild( document.createTextNode( "window." + id + "=1;" ) );
++ } catch(e) {}
++
++ root.insertBefore( script, root.firstChild );
++
++ // Make sure that the execution of code works by injecting a script
++ // tag with appendChild/createTextNode
++ // (IE doesn't support this, fails, and uses .text instead)
++ if ( window[ id ] ) {
++ jQuery.support.scriptEval = true;
++ delete window[ id ];
++ }
++
++ // Test to see if it's possible to delete an expando from an element
++ // Fails in Internet Explorer
++ try {
++ delete script.test;
++
++ } catch(e) {
++ jQuery.support.deleteExpando = false;
++ }
++
++ root.removeChild( script );
++
++ if ( div.attachEvent && div.fireEvent ) {
++ div.attachEvent("onclick", function click() {
++ // Cloning a node shouldn't copy over any
++ // bound event handlers (IE does this)
++ jQuery.support.noCloneEvent = false;
++ div.detachEvent("onclick", click);
++ });
++ div.cloneNode(true).fireEvent("onclick");
++ }
++
++ div = document.createElement("div");
++ div.innerHTML = "<input type='radio' name='radiotest' checked='checked'/>";
++
++ var fragment = document.createDocumentFragment();
++ fragment.appendChild( div.firstChild );
++
++ // WebKit doesn't clone checked state correctly in fragments
++ jQuery.support.checkClone = fragment.cloneNode(true).cloneNode(true).lastChild.checked;
++
++ // Figure out if the W3C box model works as expected
++ // document.body must exist before we can do this
++ jQuery(function() {
++ var div = document.createElement("div");
++ div.style.width = div.style.paddingLeft = "1px";
++
++ document.body.appendChild( div );
++ jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2;
++ document.body.removeChild( div ).style.display = 'none';
++
++ div = null;
++ });
++
++ // Technique from Juriy Zaytsev
++ // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
++ var eventSupported = function( eventName ) {
++ var el = document.createElement("div");
++ eventName = "on" + eventName;
++
++ var isSupported = (eventName in el);
++ if ( !isSupported ) {
++ el.setAttribute(eventName, "return;");
++ isSupported = typeof el[eventName] === "function";
++ }
++ el = null;
++
++ return isSupported;
++ };
++
++ jQuery.support.submitBubbles = eventSupported("submit");
++ jQuery.support.changeBubbles = eventSupported("change");
++
++ // release memory in IE
++ root = script = div = all = a = null;
++})();
++
++jQuery.props = {
++ "for": "htmlFor",
++ "class": "className",
++ readonly: "readOnly",
++ maxlength: "maxLength",
++ cellspacing: "cellSpacing",
++ rowspan: "rowSpan",
++ colspan: "colSpan",
++ tabindex: "tabIndex",
++ usemap: "useMap",
++ frameborder: "frameBorder"
++};
++var expando = "jQuery" + now(), uuid = 0, windowData = {};
++
++jQuery.extend({
++ cache: {},
++
++ expando:expando,
++
++ // The following elements throw uncatchable exceptions if you
++ // attempt to add expando properties to them.
++ noData: {
++ "embed": true,
++ "object": true,
++ "applet": true
++ },
++
++ data: function( elem, name, data ) {
++ if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
++ return;
++ }
++
++ elem = elem == window ?
++ windowData :
++ elem;
++
++ var id = elem[ expando ], cache = jQuery.cache, thisCache;
++
++ if ( !id && typeof name === "string" && data === undefined ) {
++ return null;
++ }
++
++ // Compute a unique ID for the element
++ if ( !id ) {
++ id = ++uuid;
++ }
++
++ // Avoid generating a new cache unless none exists and we
++ // want to manipulate it.
++ if ( typeof name === "object" ) {
++ elem[ expando ] = id;
++ thisCache = cache[ id ] = jQuery.extend(true, {}, name);
++
++ } else if ( !cache[ id ] ) {
++ elem[ expando ] = id;
++ cache[ id ] = {};
++ }
++
++ thisCache = cache[ id ];
++
++ // Prevent overriding the named cache with undefined values
++ if ( data !== undefined ) {
++ thisCache[ name ] = data;
++ }
++
++ return typeof name === "string" ? thisCache[ name ] : thisCache;
++ },
++
++ removeData: function( elem, name ) {
++ if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
++ return;
++ }
++
++ elem = elem == window ?
++ windowData :
++ elem;
++
++ var id = elem[ expando ], cache = jQuery.cache, thisCache = cache[ id ];
++
++ // If we want to remove a specific section of the element's data
++ if ( name ) {
++ if ( thisCache ) {
++ // Remove the section of cache data
++ delete thisCache[ name ];
++
++ // If we've removed all the data, remove the element's cache
++ if ( jQuery.isEmptyObject(thisCache) ) {
++ jQuery.removeData( elem );
++ }
++ }
++
++ // Otherwise, we want to remove all of the element's data
++ } else {
++ if ( jQuery.support.deleteExpando ) {
++ delete elem[ jQuery.expando ];
++
++ } else if ( elem.removeAttribute ) {
++ elem.removeAttribute( jQuery.expando );
++ }
++
++ // Completely remove the data cache
++ delete cache[ id ];
++ }
++ }
++});
++
++jQuery.fn.extend({
++ data: function( key, value ) {
++ if ( typeof key === "undefined" && this.length ) {
++ return jQuery.data( this[0] );
++
++ } else if ( typeof key === "object" ) {
++ return this.each(function() {
++ jQuery.data( this, key );
++ });
++ }
++
++ var parts = key.split(".");
++ parts[1] = parts[1] ? "." + parts[1] : "";
++
++ if ( value === undefined ) {
++ var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
++
++ if ( data === undefined && this.length ) {
++ data = jQuery.data( this[0], key );
++ }
++ return data === undefined && parts[1] ?
++ this.data( parts[0] ) :
++ data;
++ } else {
++ return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function() {
++ jQuery.data( this, key, value );
++ });
++ }
++ },
++
++ removeData: function( key ) {
++ return this.each(function() {
++ jQuery.removeData( this, key );
++ });
++ }
++});
++jQuery.extend({
++ queue: function( elem, type, data ) {
++ if ( !elem ) {
++ return;
++ }
++
++ type = (type || "fx") + "queue";
++ var q = jQuery.data( elem, type );
++
++ // Speed up dequeue by getting out quickly if this is just a lookup
++ if ( !data ) {
++ return q || [];
++ }
++
++ if ( !q || jQuery.isArray(data) ) {
++ q = jQuery.data( elem, type, jQuery.makeArray(data) );
++
++ } else {
++ q.push( data );
++ }
++
++ return q;
++ },
++
++ dequeue: function( elem, type ) {
++ type = type || "fx";
++
++ var queue = jQuery.queue( elem, type ), fn = queue.shift();
++
++ // If the fx queue is dequeued, always remove the progress sentinel
++ if ( fn === "inprogress" ) {
++ fn = queue.shift();
++ }
++
++ if ( fn ) {
++ // Add a progress sentinel to prevent the fx queue from being
++ // automatically dequeued
++ if ( type === "fx" ) {
++ queue.unshift("inprogress");
++ }
++
++ fn.call(elem, function() {
++ jQuery.dequeue(elem, type);
++ });
++ }
++ }
++});
++
++jQuery.fn.extend({
++ queue: function( type, data ) {
++ if ( typeof type !== "string" ) {
++ data = type;
++ type = "fx";
++ }
++
++ if ( data === undefined ) {
++ return jQuery.queue( this[0], type );
++ }
++ return this.each(function( i, elem ) {
++ var queue = jQuery.queue( this, type, data );
++
++ if ( type === "fx" && queue[0] !== "inprogress" ) {
++ jQuery.dequeue( this, type );
++ }
++ });
++ },
++ dequeue: function( type ) {
++ return this.each(function() {
++ jQuery.dequeue( this, type );
++ });
++ },
++
++ // Based off of the plugin by Clint Helfers, with permission.
++ // http://blindsignals.com/index.php/2009/07/jquery-delay/
++ delay: function( time, type ) {
++ time = jQuery.fx ? jQuery.fx.speeds[time] || time : time;
++ type = type || "fx";
++
++ return this.queue( type, function() {
++ var elem = this;
++ setTimeout(function() {
++ jQuery.dequeue( elem, type );
++ }, time );
++ });
++ },
++
++ clearQueue: function( type ) {
++ return this.queue( type || "fx", [] );
++ }
++});
++var rclass = /[\n\t]/g,
++ rspace = /\s+/,
++ rreturn = /\r/g,
++ rspecialurl = /href|src|style/,
++ rtype = /(button|input)/i,
++ rfocusable = /(button|input|object|select|textarea)/i,
++ rclickable = /^(a|area)$/i,
++ rradiocheck = /radio|checkbox/;
++
++jQuery.fn.extend({
++ attr: function( name, value ) {
++ return access( this, name, value, true, jQuery.attr );
++ },
++
++ removeAttr: function( name, fn ) {
++ return this.each(function(){
++ jQuery.attr( this, name, "" );
++ if ( this.nodeType === 1 ) {
++ this.removeAttribute( name );
++ }
++ });
++ },
++
++ addClass: function( value ) {
++ if ( jQuery.isFunction(value) ) {
++ return this.each(function(i) {
++ var self = jQuery(this);
++ self.addClass( value.call(this, i, self.attr("class")) );
++ });
++ }
++
++ if ( value && typeof value === "string" ) {
++ var classNames = (value || "").split( rspace );
++
++ for ( var i = 0, l = this.length; i < l; i++ ) {
++ var elem = this[i];
++
++ if ( elem.nodeType === 1 ) {
++ if ( !elem.className ) {
++ elem.className = value;
++
++ } else {
++ var className = " " + elem.className + " ", setClass = elem.className;
++ for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
++ if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) {
++ setClass += " " + classNames[c];
++ }
++ }
++ elem.className = jQuery.trim( setClass );
++ }
++ }
++ }
++ }
++
++ return this;
++ },
++
++ removeClass: function( value ) {
++ if ( jQuery.isFunction(value) ) {
++ return this.each(function(i) {
++ var self = jQuery(this);
++ self.removeClass( value.call(this, i, self.attr("class")) );
++ });
++ }
++
++ if ( (value && typeof value === "string") || value === undefined ) {
++ var classNames = (value || "").split(rspace);
++
++ for ( var i = 0, l = this.length; i < l; i++ ) {
++ var elem = this[i];
++
++ if ( elem.nodeType === 1 && elem.className ) {
++ if ( value ) {
++ var className = (" " + elem.className + " ").replace(rclass, " ");
++ for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
++ className = className.replace(" " + classNames[c] + " ", " ");
++ }
++ elem.className = jQuery.trim( className );
++
++ } else {
++ elem.className = "";
++ }
++ }
++ }
++ }
++
++ return this;
++ },
++
++ toggleClass: function( value, stateVal ) {
++ var type = typeof value, isBool = typeof stateVal === "boolean";
++
++ if ( jQuery.isFunction( value ) ) {
++ return this.each(function(i) {
++ var self = jQuery(this);
++ self.toggleClass( value.call(this, i, self.attr("class"), stateVal), stateVal );
++ });
++ }
++
++ return this.each(function() {
++ if ( type === "string" ) {
++ // toggle individual class names
++ var className, i = 0, self = jQuery(this),
++ state = stateVal,
++ classNames = value.split( rspace );
++
++ while ( (className = classNames[ i++ ]) ) {
++ // check each className given, space seperated list
++ state = isBool ? state : !self.hasClass( className );
++ self[ state ? "addClass" : "removeClass" ]( className );
++ }
++
++ } else if ( type === "undefined" || type === "boolean" ) {
++ if ( this.className ) {
++ // store className if set
++ jQuery.data( this, "__className__", this.className );
++ }
++
++ // toggle whole className
++ this.className = this.className || value === false ? "" : jQuery.data( this, "__className__" ) || "";
++ }
++ });
++ },
++
++ hasClass: function( selector ) {
++ var className = " " + selector + " ";
++ for ( var i = 0, l = this.length; i < l; i++ ) {
++ if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
++ return true;
++ }
++ }
++
++ return false;
++ },
++
++ val: function( value ) {
++ if ( value === undefined ) {
++ var elem = this[0];
++
++ if ( elem ) {
++ if ( jQuery.nodeName( elem, "option" ) ) {
++ return (elem.attributes.value || {}).specified ? elem.value : elem.text;
++ }
++
++ // We need to handle select boxes special
++ if ( jQuery.nodeName( elem, "select" ) ) {
++ var index = elem.selectedIndex,
++ values = [],
++ options = elem.options,
++ one = elem.type === "select-one";
++
++ // Nothing was selected
++ if ( index < 0 ) {
++ return null;
++ }
++
++ // Loop through all the selected options
++ for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
++ var option = options[ i ];
++
++ if ( option.selected ) {
++ // Get the specifc value for the option
++ value = jQuery(option).val();
++
++ // We don't need an array for one selects
++ if ( one ) {
++ return value;
++ }
++
++ // Multi-Selects return an array
++ values.push( value );
++ }
++ }
++
++ return values;
++ }
++
++ // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
++ if ( rradiocheck.test( elem.type ) && !jQuery.support.checkOn ) {
++ return elem.getAttribute("value") === null ? "on" : elem.value;
++ }
++
++
++ // Everything else, we just grab the value
++ return (elem.value || "").replace(rreturn, "");
++
++ }
++
++ return undefined;
++ }
++
++ var isFunction = jQuery.isFunction(value);
++
++ return this.each(function(i) {
++ var self = jQuery(this), val = value;
++
++ if ( this.nodeType !== 1 ) {
++ return;
++ }
++
++ if ( isFunction ) {
++ val = value.call(this, i, self.val());
++ }
++
++ // Typecast each time if the value is a Function and the appended
++ // value is therefore different each time.
++ if ( typeof val === "number" ) {
++ val += "";
++ }
++
++ if ( jQuery.isArray(val) && rradiocheck.test( this.type ) ) {
++ this.checked = jQuery.inArray( self.val(), val ) >= 0;
++
++ } else if ( jQuery.nodeName( this, "select" ) ) {
++ var values = jQuery.makeArray(val);
++
++ jQuery( "option", this ).each(function() {
++ this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
++ });
++
++ if ( !values.length ) {
++ this.selectedIndex = -1;
++ }
++
++ } else {
++ this.value = val;
++ }
++ });
++ }
++});
++
++jQuery.extend({
++ attrFn: {
++ val: true,
++ css: true,
++ html: true,
++ text: true,
++ data: true,
++ width: true,
++ height: true,
++ offset: true
++ },
++
++ attr: function( elem, name, value, pass ) {
++ // don't set attributes on text and comment nodes
++ if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
++ return undefined;
++ }
++
++ if ( pass && name in jQuery.attrFn ) {
++ return jQuery(elem)[name](value);
++ }
++
++ var notxml = elem.nodeType !== 1 || !jQuery.isXMLDoc( elem ),
++ // Whether we are setting (or getting)
++ set = value !== undefined;
++
++ // Try to normalize/fix the name
++ name = notxml && jQuery.props[ name ] || name;
++
++ // Only do all the following if this is a node (faster for style)
++ if ( elem.nodeType === 1 ) {
++ // These attributes require special treatment
++ var special = rspecialurl.test( name );
++
++ // Safari mis-reports the default selected property of an option
++ // Accessing the parent's selectedIndex property fixes it
++ if ( name === "selected" && !jQuery.support.optSelected ) {
++ var parent = elem.parentNode;
++ if ( parent ) {
++ parent.selectedIndex;
++
++ // Make sure that it also works with optgroups, see #5701
++ if ( parent.parentNode ) {
++ parent.parentNode.selectedIndex;
++ }
++ }
++ }
++
++ // If applicable, access the attribute via the DOM 0 way
++ if ( name in elem && notxml && !special ) {
++ if ( set ) {
++ // We can't allow the type property to be changed (since it causes problems in IE)
++ if ( name === "type" && rtype.test( elem.nodeName ) && elem.parentNode ) {
++ jQuery.error( "type property can't be changed" );
++ }
++
++ elem[ name ] = value;
++ }
++
++ // browsers index elements by id/name on forms, give priority to attributes.
++ if ( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) ) {
++ return elem.getAttributeNode( name ).nodeValue;
++ }
++
++ // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
++ // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
++ if ( name === "tabIndex" ) {
++ var attributeNode = elem.getAttributeNode( "tabIndex" );
++
++ return attributeNode && attributeNode.specified ?
++ attributeNode.value :
++ rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
++ 0 :
++ undefined;
++ }
++
++ return elem[ name ];
++ }
++
++ if ( !jQuery.support.style && notxml && name === "style" ) {
++ if ( set ) {
++ elem.style.cssText = "" + value;
++ }
++
++ return elem.style.cssText;
++ }
++
++ if ( set ) {
++ // convert the value to a string (all browsers do this but IE) see #1070
++ elem.setAttribute( name, "" + value );
++ }
++
++ var attr = !jQuery.support.hrefNormalized && notxml && special ?
++ // Some attributes require a special call on IE
++ elem.getAttribute( name, 2 ) :
++ elem.getAttribute( name );
++
++ // Non-existent attributes return null, we normalize to undefined
++ return attr === null ? undefined : attr;
++ }
++
++ // elem is actually elem.style ... set the style
++ // Using attr for specific style information is now deprecated. Use style instead.
++ return jQuery.style( elem, name, value );
++ }
++});
++var rnamespaces = /\.(.*)$/,
++ fcleanup = function( nm ) {
++ return nm.replace(/[^\w\s\.\|`]/g, function( ch ) {
++ return "\\" + ch;
++ });
++ };
++
++/*
++ * A number of helper functions used for managing events.
++ * Many of the ideas behind this code originated from
++ * Dean Edwards' addEvent library.
++ */
++jQuery.event = {
++
++ // Bind an event to an element
++ // Original by Dean Edwards
++ add: function( elem, types, handler, data ) {
++ if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
++ return;
++ }
++
++ // For whatever reason, IE has trouble passing the window object
++ // around, causing it to be cloned in the process
++ if ( elem.setInterval && ( elem !== window && !elem.frameElement ) ) {
++ elem = window;
++ }
++
++ var handleObjIn, handleObj;
++
++ if ( handler.handler ) {
++ handleObjIn = handler;
++ handler = handleObjIn.handler;
++ }
++
++ // Make sure that the function being executed has a unique ID
++ if ( !handler.guid ) {
++ handler.guid = jQuery.guid++;
++ }
++
++ // Init the element's event structure
++ var elemData = jQuery.data( elem );
++
++ // If no elemData is found then we must be trying to bind to one of the
++ // banned noData elements
++ if ( !elemData ) {
++ return;
++ }
++
++ var events = elemData.events = elemData.events || {},
++ eventHandle = elemData.handle, eventHandle;
++
++ if ( !eventHandle ) {
++ elemData.handle = eventHandle = function() {
++ // Handle the second event of a trigger and when
++ // an event is called after a page has unloaded
++ return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
++ jQuery.event.handle.apply( eventHandle.elem, arguments ) :
++ undefined;
++ };
++ }
++
++ // Add elem as a property of the handle function
++ // This is to prevent a memory leak with non-native events in IE.
++ eventHandle.elem = elem;
++
++ // Handle multiple events separated by a space
++ // jQuery(...).bind("mouseover mouseout", fn);
++ types = types.split(" ");
++
++ var type, i = 0, namespaces;
++
++ while ( (type = types[ i++ ]) ) {
++ handleObj = handleObjIn ?
++ jQuery.extend({}, handleObjIn) :
++ { handler: handler, data: data };
++
++ // Namespaced event handlers
++ if ( type.indexOf(".") > -1 ) {
++ namespaces = type.split(".");
++ type = namespaces.shift();
++ handleObj.namespace = namespaces.slice(0).sort().join(".");
++
++ } else {
++ namespaces = [];
++ handleObj.namespace = "";
++ }
++
++ handleObj.type = type;
++ handleObj.guid = handler.guid;
++
++ // Get the current list of functions bound to this event
++ var handlers = events[ type ],
++ special = jQuery.event.special[ type ] || {};
++
++ // Init the event handler queue
++ if ( !handlers ) {
++ handlers = events[ type ] = [];
++
++ // Check for a special event handler
++ // Only use addEventListener/attachEvent if the special
++ // events handler returns false
++ if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
++ // Bind the global event handler to the element
++ if ( elem.addEventListener ) {
++ elem.addEventListener( type, eventHandle, false );
++
++ } else if ( elem.attachEvent ) {
++ elem.attachEvent( "on" + type, eventHandle );
++ }
++ }
++ }
++
++ if ( special.add ) {
++ special.add.call( elem, handleObj );
++
++ if ( !handleObj.handler.guid ) {
++ handleObj.handler.guid = handler.guid;
++ }
++ }
++
++ // Add the function to the element's handler list
++ handlers.push( handleObj );
++
++ // Keep track of which events have been used, for global triggering
++ jQuery.event.global[ type ] = true;
++ }
++
++ // Nullify elem to prevent memory leaks in IE
++ elem = null;
++ },
++
++ global: {},
++
++ // Detach an event or set of events from an element
++ remove: function( elem, types, handler, pos ) {
++ // don't do events on text and comment nodes
++ if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
++ return;
++ }
++
++ var ret, type, fn, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
++ elemData = jQuery.data( elem ),
++ events = elemData && elemData.events;
++
++ if ( !elemData || !events ) {
++ return;
++ }
++
++ // types is actually an event object here
++ if ( types && types.type ) {
++ handler = types.handler;
++ types = types.type;
++ }
++
++ // Unbind all events for the element
++ if ( !types || typeof types === "string" && types.charAt(0) === "." ) {
++ types = types || "";
++
++ for ( type in events ) {
++ jQuery.event.remove( elem, type + types );
++ }
++
++ return;
++ }
++
++ // Handle multiple events separated by a space
++ // jQuery(...).unbind("mouseover mouseout", fn);
++ types = types.split(" ");
++
++ while ( (type = types[ i++ ]) ) {
++ origType = type;
++ handleObj = null;
++ all = type.indexOf(".") < 0;
++ namespaces = [];
++
++ if ( !all ) {
++ // Namespaced event handlers
++ namespaces = type.split(".");
++ type = namespaces.shift();
++
++ namespace = new RegExp("(^|\\.)" +
++ jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)")
++ }
++
++ eventType = events[ type ];
++
++ if ( !eventType ) {
++ continue;
++ }
++
++ if ( !handler ) {
++ for ( var j = 0; j < eventType.length; j++ ) {
++ handleObj = eventType[ j ];
++
++ if ( all || namespace.test( handleObj.namespace ) ) {
++ jQuery.event.remove( elem, origType, handleObj.handler, j );
++ eventType.splice( j--, 1 );
++ }
++ }
++
++ continue;
++ }
++
++ special = jQuery.event.special[ type ] || {};
++
++ for ( var j = pos || 0; j < eventType.length; j++ ) {
++ handleObj = eventType[ j ];
++
++ if ( handler.guid === handleObj.guid ) {
++ // remove the given handler for the given type
++ if ( all || namespace.test( handleObj.namespace ) ) {
++ if ( pos == null ) {
++ eventType.splice( j--, 1 );
++ }
++
++ if ( special.remove ) {
++ special.remove.call( elem, handleObj );
++ }
++ }
++
++ if ( pos != null ) {
++ break;
++ }
++ }
++ }
++
++ // remove generic event handler if no more handlers exist
++ if ( eventType.length === 0 || pos != null && eventType.length === 1 ) {
++ if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
++ removeEvent( elem, type, elemData.handle );
++ }
++
++ ret = null;
++ delete events[ type ];
++ }
++ }
++
++ // Remove the expando if it's no longer used
++ if ( jQuery.isEmptyObject( events ) ) {
++ var handle = elemData.handle;
++ if ( handle ) {
++ handle.elem = null;
++ }
++
++ delete elemData.events;
++ delete elemData.handle;
++
++ if ( jQuery.isEmptyObject( elemData ) ) {
++ jQuery.removeData( elem );
++ }
++ }
++ },
++
++ // bubbling is internal
++ trigger: function( event, data, elem /*, bubbling */ ) {
++ // Event object or event type
++ var type = event.type || event,
++ bubbling = arguments[3];
++
++ if ( !bubbling ) {
++ event = typeof event === "object" ?
++ // jQuery.Event object
++ event[expando] ? event :
++ // Object literal
++ jQuery.extend( jQuery.Event(type), event ) :
++ // Just the event type (string)
++ jQuery.Event(type);
++
++ if ( type.indexOf("!") >= 0 ) {
++ event.type = type = type.slice(0, -1);
++ event.exclusive = true;
++ }
++
++ // Handle a global trigger
++ if ( !elem ) {
++ // Don't bubble custom events when global (to avoid too much overhead)
++ event.stopPropagation();
++
++ // Only trigger if we've ever bound an event for it
++ if ( jQuery.event.global[ type ] ) {
++ jQuery.each( jQuery.cache, function() {
++ if ( this.events && this.events[type] ) {
++ jQuery.event.trigger( event, data, this.handle.elem );
++ }
++ });
++ }
++ }
++
++ // Handle triggering a single element
++
++ // don't do events on text and comment nodes
++ if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
++ return undefined;
++ }
++
++ // Clean up in case it is reused
++ event.result = undefined;
++ event.target = elem;
++
++ // Clone the incoming data, if any
++ data = jQuery.makeArray( data );
++ data.unshift( event );
++ }
++
++ event.currentTarget = elem;
++
++ // Trigger the event, it is assumed that "handle" is a function
++ var handle = jQuery.data( elem, "handle" );
++ if ( handle ) {
++ handle.apply( elem, data );
++ }
++
++ var parent = elem.parentNode || elem.ownerDocument;
++
++ // Trigger an inline bound script
++ try {
++ if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) {
++ if ( elem[ "on" + type ] && elem[ "on" + type ].apply( elem, data ) === false ) {
++ event.result = false;
++ }
++ }
++
++ // prevent IE from throwing an error for some elements with some event types, see #3533
++ } catch (e) {}
++
++ if ( !event.isPropagationStopped() && parent ) {
++ jQuery.event.trigger( event, data, parent, true );
++
++ } else if ( !event.isDefaultPrevented() ) {
++ var target = event.target, old,
++ isClick = jQuery.nodeName(target, "a") && type === "click",
++ special = jQuery.event.special[ type ] || {};
++
++ if ( (!special._default || special._default.call( elem, event ) === false) &&
++ !isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) {
++
++ try {
++ if ( target[ type ] ) {
++ // Make sure that we don't accidentally re-trigger the onFOO events
++ old = target[ "on" + type ];
++
++ if ( old ) {
++ target[ "on" + type ] = null;
++ }
++
++ jQuery.event.triggered = true;
++ target[ type ]();
++ }
++
++ // prevent IE from throwing an error for some elements with some event types, see #3533
++ } catch (e) {}
++
++ if ( old ) {
++ target[ "on" + type ] = old;
++ }
++
++ jQuery.event.triggered = false;
++ }
++ }
++ },
++
++ handle: function( event ) {
++ var all, handlers, namespaces, namespace, events;
++
++ event = arguments[0] = jQuery.event.fix( event || window.event );
++ event.currentTarget = this;
++
++ // Namespaced event handlers
++ all = event.type.indexOf(".") < 0 && !event.exclusive;
++
++ if ( !all ) {
++ namespaces = event.type.split(".");
++ event.type = namespaces.shift();
++ namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join("\\.(?:.*\\.)?") + "(\\.|$)");
++ }
++
++ var events = jQuery.data(this, "events"), handlers = events[ event.type ];
++
++ if ( events && handlers ) {
++ // Clone the handlers to prevent manipulation
++ handlers = handlers.slice(0);
++
++ for ( var j = 0, l = handlers.length; j < l; j++ ) {
++ var handleObj = handlers[ j ];
++
++ // Filter the functions by class
++ if ( all || namespace.test( handleObj.namespace ) ) {
++ // Pass in a reference to the handler function itself
++ // So that we can later remove it
++ event.handler = handleObj.handler;
++ event.data = handleObj.data;
++ event.handleObj = handleObj;
++
++ var ret = handleObj.handler.apply( this, arguments );
++
++ if ( ret !== undefined ) {
++ event.result = ret;
++ if ( ret === false ) {
++ event.preventDefault();
++ event.stopPropagation();
++ }
++ }
++
++ if ( event.isImmediatePropagationStopped() ) {
++ break;
++ }
++ }
++ }
++ }
++
++ return event.result;
++ },
++
++ props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
++
++ fix: function( event ) {
++ if ( event[ expando ] ) {
++ return event;
++ }
++
++ // store a copy of the original event object
++ // and "clone" to set read-only properties
++ var originalEvent = event;
++ event = jQuery.Event( originalEvent );
++
++ for ( var i = this.props.length, prop; i; ) {
++ prop = this.props[ --i ];
++ event[ prop ] = originalEvent[ prop ];
++ }
++
++ // Fix target property, if necessary
++ if ( !event.target ) {
++ event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
++ }
++
++ // check if target is a textnode (safari)
++ if ( event.target.nodeType === 3 ) {
++ event.target = event.target.parentNode;
++ }
++
++ // Add relatedTarget, if necessary
++ if ( !event.relatedTarget && event.fromElement ) {
++ event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
++ }
++
++ // Calculate pageX/Y if missing and clientX/Y available
++ if ( event.pageX == null && event.clientX != null ) {
++ var doc = document.documentElement, body = document.body;
++ event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
++ event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
++ }
++
++ // Add which for key events
++ if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) {
++ event.which = event.charCode || event.keyCode;
++ }
++
++ // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
++ if ( !event.metaKey && event.ctrlKey ) {
++ event.metaKey = event.ctrlKey;
++ }
++
++ // Add which for click: 1 === left; 2 === middle; 3 === right
++ // Note: button is not normalized, so don't use it
++ if ( !event.which && event.button !== undefined ) {
++ event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
++ }
++
++ return event;
++ },
++
++ // Deprecated, use jQuery.guid instead
++ guid: 1E8,
++
++ // Deprecated, use jQuery.proxy instead
++ proxy: jQuery.proxy,
++
++ special: {
++ ready: {
++ // Make sure the ready event is setup
++ setup: jQuery.bindReady,
++ teardown: jQuery.noop
++ },
++
++ live: {
++ add: function( handleObj ) {
++ jQuery.event.add( this, handleObj.origType, jQuery.extend({}, handleObj, {handler: liveHandler}) );
++ },
++
++ remove: function( handleObj ) {
++ var remove = true,
++ type = handleObj.origType.replace(rnamespaces, "");
++
++ jQuery.each( jQuery.data(this, "events").live || [], function() {
++ if ( type === this.origType.replace(rnamespaces, "") ) {
++ remove = false;
++ return false;
++ }
++ });
++
++ if ( remove ) {
++ jQuery.event.remove( this, handleObj.origType, liveHandler );
++ }
++ }
++
++ },
++
++ beforeunload: {
++ setup: function( data, namespaces, eventHandle ) {
++ // We only want to do this special case on windows
++ if ( this.setInterval ) {
++ this.onbeforeunload = eventHandle;
++ }
++
++ return false;
++ },
++ teardown: function( namespaces, eventHandle ) {
++ if ( this.onbeforeunload === eventHandle ) {
++ this.onbeforeunload = null;
++ }
++ }
++ }
++ }
++};
++
++var removeEvent = document.removeEventListener ?
++ function( elem, type, handle ) {
++ elem.removeEventListener( type, handle, false );
++ } :
++ function( elem, type, handle ) {
++ elem.detachEvent( "on" + type, handle );
++ };
++
++jQuery.Event = function( src ) {
++ // Allow instantiation without the 'new' keyword
++ if ( !this.preventDefault ) {
++ return new jQuery.Event( src );
++ }
++
++ // Event object
++ if ( src && src.type ) {
++ this.originalEvent = src;
++ this.type = src.type;
++ // Event type
++ } else {
++ this.type = src;
++ }
++
++ // timeStamp is buggy for some events on Firefox(#3843)
++ // So we won't rely on the native value
++ this.timeStamp = now();
++
++ // Mark it as fixed
++ this[ expando ] = true;
++};
++
++function returnFalse() {
++ return false;
++}
++function returnTrue() {
++ return true;
++}
++
++// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
++// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
++jQuery.Event.prototype = {
++ preventDefault: function() {
++ this.isDefaultPrevented = returnTrue;
++
++ var e = this.originalEvent;
++ if ( !e ) {
++ return;
++ }
++
++ // if preventDefault exists run it on the original event
++ if ( e.preventDefault ) {
++ e.preventDefault();
++ }
++ // otherwise set the returnValue property of the original event to false (IE)
++ e.returnValue = false;
++ },
++ stopPropagation: function() {
++ this.isPropagationStopped = returnTrue;
++
++ var e = this.originalEvent;
++ if ( !e ) {
++ return;
++ }
++ // if stopPropagation exists run it on the original event
++ if ( e.stopPropagation ) {
++ e.stopPropagation();
++ }
++ // otherwise set the cancelBubble property of the original event to true (IE)
++ e.cancelBubble = true;
++ },
++ stopImmediatePropagation: function() {
++ this.isImmediatePropagationStopped = returnTrue;
++ this.stopPropagation();
++ },
++ isDefaultPrevented: returnFalse,
++ isPropagationStopped: returnFalse,
++ isImmediatePropagationStopped: returnFalse
++};
++
++// Checks if an event happened on an element within another element
++// Used in jQuery.event.special.mouseenter and mouseleave handlers
++var withinElement = function( event ) {
++ // Check if mouse(over|out) are still within the same parent element
++ var parent = event.relatedTarget;
++
++ // Firefox sometimes assigns relatedTarget a XUL element
++ // which we cannot access the parentNode property of
++ try {
++ // Traverse up the tree
++ while ( parent && parent !== this ) {
++ parent = parent.parentNode;
++ }
++
++ if ( parent !== this ) {
++ // set the correct event type
++ event.type = event.data;
++
++ // handle event if we actually just moused on to a non sub-element
++ jQuery.event.handle.apply( this, arguments );
++ }
++
++ // assuming we've left the element since we most likely mousedover a xul element
++ } catch(e) { }
++},
++
++// In case of event delegation, we only need to rename the event.type,
++// liveHandler will take care of the rest.
++delegate = function( event ) {
++ event.type = event.data;
++ jQuery.event.handle.apply( this, arguments );
++};
++
++// Create mouseenter and mouseleave events
++jQuery.each({
++ mouseenter: "mouseover",
++ mouseleave: "mouseout"
++}, function( orig, fix ) {
++ jQuery.event.special[ orig ] = {
++ setup: function( data ) {
++ jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
++ },
++ teardown: function( data ) {
++ jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
++ }
++ };
++});
++
++// submit delegation
++if ( !jQuery.support.submitBubbles ) {
++
++ jQuery.event.special.submit = {
++ setup: function( data, namespaces ) {
++ if ( this.nodeName.toLowerCase() !== "form" ) {
++ jQuery.event.add(this, "click.specialSubmit", function( e ) {
++ var elem = e.target, type = elem.type;
++
++ if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
++ return trigger( "submit", this, arguments );
++ }
++ });
++
++ jQuery.event.add(this, "keypress.specialSubmit", function( e ) {
++ var elem = e.target, type = elem.type;
++
++ if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
++ return trigger( "submit", this, arguments );
++ }
++ });
++
++ } else {
++ return false;
++ }
++ },
++
++ teardown: function( namespaces ) {
++ jQuery.event.remove( this, ".specialSubmit" );
++ }
++ };
++
++}
++
++// change delegation, happens here so we have bind.
++if ( !jQuery.support.changeBubbles ) {
++
++ var formElems = /textarea|input|select/i,
++
++ changeFilters,
++
++ getVal = function( elem ) {
++ var type = elem.type, val = elem.value;
++
++ if ( type === "radio" || type === "checkbox" ) {
++ val = elem.checked;
++
++ } else if ( type === "select-multiple" ) {
++ val = elem.selectedIndex > -1 ?
++ jQuery.map( elem.options, function( elem ) {
++ return elem.selected;
++ }).join("-") :
++ "";
++
++ } else if ( elem.nodeName.toLowerCase() === "select" ) {
++ val = elem.selectedIndex;
++ }
++
++ return val;
++ },
++
++ testChange = function testChange( e ) {
++ var elem = e.target, data, val;
++
++ if ( !formElems.test( elem.nodeName ) || elem.readOnly ) {
++ return;
++ }
++
++ data = jQuery.data( elem, "_change_data" );
++ val = getVal(elem);
++
++ // the current data will be also retrieved by beforeactivate
++ if ( e.type !== "focusout" || elem.type !== "radio" ) {
++ jQuery.data( elem, "_change_data", val );
++ }
++
++ if ( data === undefined || val === data ) {
++ return;
++ }
++
++ if ( data != null || val ) {
++ e.type = "change";
++ return jQuery.event.trigger( e, arguments[1], elem );
++ }
++ };
++
++ jQuery.event.special.change = {
++ filters: {
++ focusout: testChange,
++
++ click: function( e ) {
++ var elem = e.target, type = elem.type;
++
++ if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
++ return testChange.call( this, e );
++ }
++ },
++
++ // Change has to be called before submit
++ // Keydown will be called before keypress, which is used in submit-event delegation
++ keydown: function( e ) {
++ var elem = e.target, type = elem.type;
++
++ if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") ||
++ (e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
++ type === "select-multiple" ) {
++ return testChange.call( this, e );
++ }
++ },
++
++ // Beforeactivate happens also before the previous element is blurred
++ // with this event you can't trigger a change event, but you can store
++ // information/focus[in] is not needed anymore
++ beforeactivate: function( e ) {
++ var elem = e.target;
++ jQuery.data( elem, "_change_data", getVal(elem) );
++ }
++ },
++
++ setup: function( data, namespaces ) {
++ if ( this.type === "file" ) {
++ return false;
++ }
++
++ for ( var type in changeFilters ) {
++ jQuery.event.add( this, type + ".specialChange", changeFilters[type] );
++ }
++
++ return formElems.test( this.nodeName );
++ },
++
++ teardown: function( namespaces ) {
++ jQuery.event.remove( this, ".specialChange" );
++
++ return formElems.test( this.nodeName );
++ }
++ };
++
++ changeFilters = jQuery.event.special.change.filters;
++}
++
++function trigger( type, elem, args ) {
++ args[0].type = type;
++ return jQuery.event.handle.apply( elem, args );
++}
++
++// Create "bubbling" focus and blur events
++if ( document.addEventListener ) {
++ jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
++ jQuery.event.special[ fix ] = {
++ setup: function() {
++ this.addEventListener( orig, handler, true );
++ },
++ teardown: function() {
++ this.removeEventListener( orig, handler, true );
++ }
++ };
++
++ function handler( e ) {
++ e = jQuery.event.fix( e );
++ e.type = fix;
++ return jQuery.event.handle.call( this, e );
++ }
++ });
++}
++
++jQuery.each(["bind", "one"], function( i, name ) {
++ jQuery.fn[ name ] = function( type, data, fn ) {
++ // Handle object literals
++ if ( typeof type === "object" ) {
++ for ( var key in type ) {
++ this[ name ](key, data, type[key], fn);
++ }
++ return this;
++ }
++
++ if ( jQuery.isFunction( data ) ) {
++ fn = data;
++ data = undefined;
++ }
++
++ var handler = name === "one" ? jQuery.proxy( fn, function( event ) {
++ jQuery( this ).unbind( event, handler );
++ return fn.apply( this, arguments );
++ }) : fn;
++
++ if ( type === "unload" && name !== "one" ) {
++ this.one( type, data, fn );
++
++ } else {
++ for ( var i = 0, l = this.length; i < l; i++ ) {
++ jQuery.event.add( this[i], type, handler, data );
++ }
++ }
++
++ return this;
++ };
++});
++
++jQuery.fn.extend({
++ unbind: function( type, fn ) {
++ // Handle object literals
++ if ( typeof type === "object" && !type.preventDefault ) {
++ for ( var key in type ) {
++ this.unbind(key, type[key]);
++ }
++
++ } else {
++ for ( var i = 0, l = this.length; i < l; i++ ) {
++ jQuery.event.remove( this[i], type, fn );
++ }
++ }
++
++ return this;
++ },
++
++ delegate: function( selector, types, data, fn ) {
++ return this.live( types, data, fn, selector );
++ },
++
++ undelegate: function( selector, types, fn ) {
++ if ( arguments.length === 0 ) {
++ return this.unbind( "live" );
++
++ } else {
++ return this.die( types, null, fn, selector );
++ }
++ },
++
++ trigger: function( type, data ) {
++ return this.each(function() {
++ jQuery.event.trigger( type, data, this );
++ });
++ },
++
++ triggerHandler: function( type, data ) {
++ if ( this[0] ) {
++ var event = jQuery.Event( type );
++ event.preventDefault();
++ event.stopPropagation();
++ jQuery.event.trigger( event, data, this[0] );
++ return event.result;
++ }
++ },
++
++ toggle: function( fn ) {
++ // Save reference to arguments for access in closure
++ var args = arguments, i = 1;
++
++ // link all the functions, so any of them can unbind this click handler
++ while ( i < args.length ) {
++ jQuery.proxy( fn, args[ i++ ] );
++ }
++
++ return this.click( jQuery.proxy( fn, function( event ) {
++ // Figure out which function to execute
++ var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i;
++ jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 );
++
++ // Make sure that clicks stop
++ event.preventDefault();
++
++ // and execute the function
++ return args[ lastToggle ].apply( this, arguments ) || false;
++ }));
++ },
++
++ hover: function( fnOver, fnOut ) {
++ return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
++ }
++});
++
++var liveMap = {
++ focus: "focusin",
++ blur: "focusout",
++ mouseenter: "mouseover",
++ mouseleave: "mouseout"
++};
++
++jQuery.each(["live", "die"], function( i, name ) {
++ jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) {
++ var type, i = 0, match, namespaces, preType,
++ selector = origSelector || this.selector,
++ context = origSelector ? this : jQuery( this.context );
++
++ if ( jQuery.isFunction( data ) ) {
++ fn = data;
++ data = undefined;
++ }
++
++ types = (types || "").split(" ");
++
++ while ( (type = types[ i++ ]) != null ) {
++ match = rnamespaces.exec( type );
++ namespaces = "";
++
++ if ( match ) {
++ namespaces = match[0];
++ type = type.replace( rnamespaces, "" );
++ }
++
++ if ( type === "hover" ) {
++ types.push( "mouseenter" + namespaces, "mouseleave" + namespaces );
++ continue;
++ }
++
++ preType = type;
++
++ if ( type === "focus" || type === "blur" ) {
++ types.push( liveMap[ type ] + namespaces );
++ type = type + namespaces;
++
++ } else {
++ type = (liveMap[ type ] || type) + namespaces;
++ }
++
++ if ( name === "live" ) {
++ // bind live handler
++ context.each(function(){
++ jQuery.event.add( this, liveConvert( type, selector ),
++ { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } );
++ });
++
++ } else {
++ // unbind live handler
++ context.unbind( liveConvert( type, selector ), fn );
++ }
++ }
++
++ return this;
++ }
++});
++
++function liveHandler( event ) {
++ var stop, elems = [], selectors = [], args = arguments,
++ related, match, handleObj, elem, j, i, l, data,
++ events = jQuery.data( this, "events" );
++
++ // Make sure we avoid non-left-click bubbling in Firefox (#3861)
++ if ( event.liveFired === this || !events || !events.live || event.button && event.type === "click" ) {
++ return;
++ }
++
++ event.liveFired = this;
++
++ var live = events.live.slice(0);
++
++ for ( j = 0; j < live.length; j++ ) {
++ handleObj = live[j];
++
++ if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) {
++ selectors.push( handleObj.selector );
++
++ } else {
++ live.splice( j--, 1 );
++ }
++ }
++
++ match = jQuery( event.target ).closest( selectors, event.currentTarget );
++
++ for ( i = 0, l = match.length; i < l; i++ ) {
++ for ( j = 0; j < live.length; j++ ) {
++ handleObj = live[j];
++
++ if ( match[i].selector === handleObj.selector ) {
++ elem = match[i].elem;
++ related = null;
++
++ // Those two events require additional checking
++ if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) {
++ related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0];
++ }
++
++ if ( !related || related !== elem ) {
++ elems.push({ elem: elem, handleObj: handleObj });
++ }
++ }
++ }
++ }
++
++ for ( i = 0, l = elems.length; i < l; i++ ) {
++ match = elems[i];
++ event.currentTarget = match.elem;
++ event.data = match.handleObj.data;
++ event.handleObj = match.handleObj;
++
++ if ( match.handleObj.origHandler.apply( match.elem, args ) === false ) {
++ stop = false;
++ break;
++ }
++ }
++
++ return stop;
++}
++
++function liveConvert( type, selector ) {
++ return "live." + (type && type !== "*" ? type + "." : "") + selector.replace(/\./g, "`").replace(/ /g, "&");
++}
++
++jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
++ "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
++ "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
++
++ // Handle event binding
++ jQuery.fn[ name ] = function( fn ) {
++ return fn ? this.bind( name, fn ) : this.trigger( name );
++ };
++
++ if ( jQuery.attrFn ) {
++ jQuery.attrFn[ name ] = true;
++ }
++});
++
++// Prevent memory leaks in IE
++// Window isn't included so as not to unbind existing unload events
++// More info:
++// - http://isaacschlueter.com/2006/10/msie-memory-leaks/
++if ( window.attachEvent && !window.addEventListener ) {
++ window.attachEvent("onunload", function() {
++ for ( var id in jQuery.cache ) {
++ if ( jQuery.cache[ id ].handle ) {
++ // Try/Catch is to handle iframes being unloaded, see #4280
++ try {
++ jQuery.event.remove( jQuery.cache[ id ].handle.elem );
++ } catch(e) {}
++ }
++ }
++ });
++}
++/*!
++ * Sizzle CSS Selector Engine - v1.0
++ * Copyright 2009, The Dojo Foundation
++ * Released under the MIT, BSD, and GPL Licenses.
++ * More information: http://sizzlejs.com/
++ */
++(function(){
++
++var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
++ done = 0,
++ toString = Object.prototype.toString,
++ hasDuplicate = false,
++ baseHasDuplicate = true;
++
++// Here we check if the JavaScript engine is using some sort of
++// optimization where it does not always call our comparision
++// function. If that is the case, discard the hasDuplicate value.
++// Thus far that includes Google Chrome.
++[0, 0].sort(function(){
++ baseHasDuplicate = false;
++ return 0;
++});
++
++var Sizzle = function(selector, context, results, seed) {
++ results = results || [];
++ var origContext = context = context || document;
++
++ if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
++ return [];
++ }
++
++ if ( !selector || typeof selector !== "string" ) {
++ return results;
++ }
++
++ var parts = [], m, set, checkSet, extra, prune = true, contextXML = isXML(context),
++ soFar = selector;
++
++ // Reset the position of the chunker regexp (start from head)
++ while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) {
++ soFar = m[3];
++
++ parts.push( m[1] );
++
++ if ( m[2] ) {
++ extra = m[3];
++ break;
++ }
++ }
++
++ if ( parts.length > 1 && origPOS.exec( selector ) ) {
++ if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
++ set = posProcess( parts[0] + parts[1], context );
++ } else {
++ set = Expr.relative[ parts[0] ] ?
++ [ context ] :
++ Sizzle( parts.shift(), context );
++
++ while ( parts.length ) {
++ selector = parts.shift();
++
++ if ( Expr.relative[ selector ] ) {
++ selector += parts.shift();
++ }
++
++ set = posProcess( selector, set );
++ }
++ }
++ } else {
++ // Take a shortcut and set the context if the root selector is an ID
++ // (but not if it'll be faster if the inner selector is an ID)
++ if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
++ Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
++ var ret = Sizzle.find( parts.shift(), context, contextXML );
++ context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
++ }
++
++ if ( context ) {
++ var ret = seed ?
++ { expr: parts.pop(), set: makeArray(seed) } :
++ Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
++ set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;
++
++ if ( parts.length > 0 ) {
++ checkSet = makeArray(set);
++ } else {
++ prune = false;
++ }
++
++ while ( parts.length ) {
++ var cur = parts.pop(), pop = cur;
++
++ if ( !Expr.relative[ cur ] ) {
++ cur = "";
++ } else {
++ pop = parts.pop();
++ }
++
++ if ( pop == null ) {
++ pop = context;
++ }
++
++ Expr.relative[ cur ]( checkSet, pop, contextXML );
++ }
++ } else {
++ checkSet = parts = [];
++ }
++ }
++
++ if ( !checkSet ) {
++ checkSet = set;
++ }
++
++ if ( !checkSet ) {
++ Sizzle.error( cur || selector );
++ }
++
++ if ( toString.call(checkSet) === "[object Array]" ) {
++ if ( !prune ) {
++ results.push.apply( results, checkSet );
++ } else if ( context && context.nodeType === 1 ) {
++ for ( var i = 0; checkSet[i] != null; i++ ) {
++ if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
++ results.push( set[i] );
++ }
++ }
++ } else {
++ for ( var i = 0; checkSet[i] != null; i++ ) {
++ if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
++ results.push( set[i] );
++ }
++ }
++ }
++ } else {
++ makeArray( checkSet, results );
++ }
++
++ if ( extra ) {
++ Sizzle( extra, origContext, results, seed );
++ Sizzle.uniqueSort( results );
++ }
++
++ return results;
++};
++
++Sizzle.uniqueSort = function(results){
++ if ( sortOrder ) {
++ hasDuplicate = baseHasDuplicate;
++ results.sort(sortOrder);
++
++ if ( hasDuplicate ) {
++ for ( var i = 1; i < results.length; i++ ) {
++ if ( results[i] === results[i-1] ) {
++ results.splice(i--, 1);
++ }
++ }
++ }
++ }
++
++ return results;
++};
++
++Sizzle.matches = function(expr, set){
++ return Sizzle(expr, null, null, set);
++};
++
++Sizzle.find = function(expr, context, isXML){
++ var set, match;
++
++ if ( !expr ) {
++ return [];
++ }
++
++ for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
++ var type = Expr.order[i], match;
++
++ if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
++ var left = match[1];
++ match.splice(1,1);
++
++ if ( left.substr( left.length - 1 ) !== "\\" ) {
++ match[1] = (match[1] || "").replace(/\\/g, "");
++ set = Expr.find[ type ]( match, context, isXML );
++ if ( set != null ) {
++ expr = expr.replace( Expr.match[ type ], "" );
++ break;
++ }
++ }
++ }
++ }
++
++ if ( !set ) {
++ set = context.getElementsByTagName("*");
++ }
++
++ return {set: set, expr: expr};
++};
++
++Sizzle.filter = function(expr, set, inplace, not){
++ var old = expr, result = [], curLoop = set, match, anyFound,
++ isXMLFilter = set && set[0] && isXML(set[0]);
++
++ while ( expr && set.length ) {
++ for ( var type in Expr.filter ) {
++ if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
++ var filter = Expr.filter[ type ], found, item, left = match[1];
++ anyFound = false;
++
++ match.splice(1,1);
++
++ if ( left.substr( left.length - 1 ) === "\\" ) {
++ continue;
++ }
++
++ if ( curLoop === result ) {
++ result = [];
++ }
++
++ if ( Expr.preFilter[ type ] ) {
++ match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
++
++ if ( !match ) {
++ anyFound = found = true;
++ } else if ( match === true ) {
++ continue;
++ }
++ }
++
++ if ( match ) {
++ for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
++ if ( item ) {
++ found = filter( item, match, i, curLoop );
++ var pass = not ^ !!found;
++
++ if ( inplace && found != null ) {
++ if ( pass ) {
++ anyFound = true;
++ } else {
++ curLoop[i] = false;
++ }
++ } else if ( pass ) {
++ result.push( item );
++ anyFound = true;
++ }
++ }
++ }
++ }
++
++ if ( found !== undefined ) {
++ if ( !inplace ) {
++ curLoop = result;
++ }
++
++ expr = expr.replace( Expr.match[ type ], "" );
++
++ if ( !anyFound ) {
++ return [];
++ }
++
++ break;
++ }
++ }
++ }
++
++ // Improper expression
++ if ( expr === old ) {
++ if ( anyFound == null ) {
++ Sizzle.error( expr );
++ } else {
++ break;
++ }
++ }
++
++ old = expr;
++ }
++
++ return curLoop;
++};
++
++Sizzle.error = function( msg ) {
++ throw "Syntax error, unrecognized expression: " + msg;
++};
++
++var Expr = Sizzle.selectors = {
++ order: [ "ID", "NAME", "TAG" ],
++ match: {
++ ID: /#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
++ CLASS: /\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
++ NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,
++ ATTR: /\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
++ TAG: /^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,
++ CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
++ POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
++ PSEUDO: /:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
++ },
++ leftMatch: {},
++ attrMap: {
++ "class": "className",
++ "for": "htmlFor"
++ },
++ attrHandle: {
++ href: function(elem){
++ return elem.getAttribute("href");
++ }
++ },
++ relative: {
++ "+": function(checkSet, part){
++ var isPartStr = typeof part === "string",
++ isTag = isPartStr && !/\W/.test(part),
++ isPartStrNotTag = isPartStr && !isTag;
++
++ if ( isTag ) {
++ part = part.toLowerCase();
++ }
++
++ for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
++ if ( (elem = checkSet[i]) ) {
++ while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
++
++ checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?
++ elem || false :
++ elem === part;
++ }
++ }
++
++ if ( isPartStrNotTag ) {
++ Sizzle.filter( part, checkSet, true );
++ }
++ },
++ ">": function(checkSet, part){
++ var isPartStr = typeof part === "string";
++
++ if ( isPartStr && !/\W/.test(part) ) {
++ part = part.toLowerCase();
++
++ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
++ var elem = checkSet[i];
++ if ( elem ) {
++ var parent = elem.parentNode;
++ checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
++ }
++ }
++ } else {
++ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
++ var elem = checkSet[i];
++ if ( elem ) {
++ checkSet[i] = isPartStr ?
++ elem.parentNode :
++ elem.parentNode === part;
++ }
++ }
++
++ if ( isPartStr ) {
++ Sizzle.filter( part, checkSet, true );
++ }
++ }
++ },
++ "": function(checkSet, part, isXML){
++ var doneName = done++, checkFn = dirCheck;
++
++ if ( typeof part === "string" && !/\W/.test(part) ) {
++ var nodeCheck = part = part.toLowerCase();
++ checkFn = dirNodeCheck;
++ }
++
++ checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
++ },
++ "~": function(checkSet, part, isXML){
++ var doneName = done++, checkFn = dirCheck;
++
++ if ( typeof part === "string" && !/\W/.test(part) ) {
++ var nodeCheck = part = part.toLowerCase();
++ checkFn = dirNodeCheck;
++ }
++
++ checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
++ }
++ },
++ find: {
++ ID: function(match, context, isXML){
++ if ( typeof context.getElementById !== "undefined" && !isXML ) {
++ var m = context.getElementById(match[1]);
++ return m ? [m] : [];
++ }
++ },
++ NAME: function(match, context){
++ if ( typeof context.getElementsByName !== "undefined" ) {
++ var ret = [], results = context.getElementsByName(match[1]);
++
++ for ( var i = 0, l = results.length; i < l; i++ ) {
++ if ( results[i].getAttribute("name") === match[1] ) {
++ ret.push( results[i] );
++ }
++ }
++
++ return ret.length === 0 ? null : ret;
++ }
++ },
++ TAG: function(match, context){
++ return context.getElementsByTagName(match[1]);
++ }
++ },
++ preFilter: {
++ CLASS: function(match, curLoop, inplace, result, not, isXML){
++ match = " " + match[1].replace(/\\/g, "") + " ";
++
++ if ( isXML ) {
++ return match;
++ }
++
++ for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
++ if ( elem ) {
++ if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n]/g, " ").indexOf(match) >= 0) ) {
++ if ( !inplace ) {
++ result.push( elem );
++ }
++ } else if ( inplace ) {
++ curLoop[i] = false;
++ }
++ }
++ }
++
++ return false;
++ },
++ ID: function(match){
++ return match[1].replace(/\\/g, "");
++ },
++ TAG: function(match, curLoop){
++ return match[1].toLowerCase();
++ },
++ CHILD: function(match){
++ if ( match[1] === "nth" ) {
++ // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
++ var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
++ match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
++ !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
++
++ // calculate the numbers (first)n+(last) including if they are negative
++ match[2] = (test[1] + (test[2] || 1)) - 0;
++ match[3] = test[3] - 0;
++ }
++
++ // TODO: Move to normal caching system
++ match[0] = done++;
++
++ return match;
++ },
++ ATTR: function(match, curLoop, inplace, result, not, isXML){
++ var name = match[1].replace(/\\/g, "");
++
++ if ( !isXML && Expr.attrMap[name] ) {
++ match[1] = Expr.attrMap[name];
++ }
++
++ if ( match[2] === "~=" ) {
++ match[4] = " " + match[4] + " ";
++ }
++
++ return match;
++ },
++ PSEUDO: function(match, curLoop, inplace, result, not){
++ if ( match[1] === "not" ) {
++ // If we're dealing with a complex expression, or a simple one
++ if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
++ match[3] = Sizzle(match[3], null, null, curLoop);
++ } else {
++ var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
++ if ( !inplace ) {
++ result.push.apply( result, ret );
++ }
++ return false;
++ }
++ } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
++ return true;
++ }
++
++ return match;
++ },
++ POS: function(match){
++ match.unshift( true );
++ return match;
++ }
++ },
++ filters: {
++ enabled: function(elem){
++ return elem.disabled === false && elem.type !== "hidden";
++ },
++ disabled: function(elem){
++ return elem.disabled === true;
++ },
++ checked: function(elem){
++ return elem.checked === true;
++ },
++ selected: function(elem){
++ // Accessing this property makes selected-by-default
++ // options in Safari work properly
++ elem.parentNode.selectedIndex;
++ return elem.selected === true;
++ },
++ parent: function(elem){
++ return !!elem.firstChild;
++ },
++ empty: function(elem){
++ return !elem.firstChild;
++ },
++ has: function(elem, i, match){
++ return !!Sizzle( match[3], elem ).length;
++ },
++ header: function(elem){
++ return /h\d/i.test( elem.nodeName );
++ },
++ text: function(elem){
++ return "text" === elem.type;
++ },
++ radio: function(elem){
++ return "radio" === elem.type;
++ },
++ checkbox: function(elem){
++ return "checkbox" === elem.type;
++ },
++ file: function(elem){
++ return "file" === elem.type;
++ },
++ password: function(elem){
++ return "password" === elem.type;
++ },
++ submit: function(elem){
++ return "submit" === elem.type;
++ },
++ image: function(elem){
++ return "image" === elem.type;
++ },
++ reset: function(elem){
++ return "reset" === elem.type;
++ },
++ button: function(elem){
++ return "button" === elem.type || elem.nodeName.toLowerCase() === "button";
++ },
++ input: function(elem){
++ return /input|select|textarea|button/i.test(elem.nodeName);
++ }
++ },
++ setFilters: {
++ first: function(elem, i){
++ return i === 0;
++ },
++ last: function(elem, i, match, array){
++ return i === array.length - 1;
++ },
++ even: function(elem, i){
++ return i % 2 === 0;
++ },
++ odd: function(elem, i){
++ return i % 2 === 1;
++ },
++ lt: function(elem, i, match){
++ return i < match[3] - 0;
++ },
++ gt: function(elem, i, match){
++ return i > match[3] - 0;
++ },
++ nth: function(elem, i, match){
++ return match[3] - 0 === i;
++ },
++ eq: function(elem, i, match){
++ return match[3] - 0 === i;
++ }
++ },
++ filter: {
++ PSEUDO: function(elem, match, i, array){
++ var name = match[1], filter = Expr.filters[ name ];
++
++ if ( filter ) {
++ return filter( elem, i, match, array );
++ } else if ( name === "contains" ) {
++ return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0;
++ } else if ( name === "not" ) {
++ var not = match[3];
++
++ for ( var i = 0, l = not.length; i < l; i++ ) {
++ if ( not[i] === elem ) {
++ return false;
++ }
++ }
++
++ return true;
++ } else {
++ Sizzle.error( "Syntax error, unrecognized expression: " + name );
++ }
++ },
++ CHILD: function(elem, match){
++ var type = match[1], node = elem;
++ switch (type) {
++ case 'only':
++ case 'first':
++ while ( (node = node.previousSibling) ) {
++ if ( node.nodeType === 1 ) {
++ return false;
++ }
++ }
++ if ( type === "first" ) {
++ return true;
++ }
++ node = elem;
++ case 'last':
++ while ( (node = node.nextSibling) ) {
++ if ( node.nodeType === 1 ) {
++ return false;
++ }
++ }
++ return true;
++ case 'nth':
++ var first = match[2], last = match[3];
++
++ if ( first === 1 && last === 0 ) {
++ return true;
++ }
++
++ var doneName = match[0],
++ parent = elem.parentNode;
++
++ if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
++ var count = 0;
++ for ( node = parent.firstChild; node; node = node.nextSibling ) {
++ if ( node.nodeType === 1 ) {
++ node.nodeIndex = ++count;
++ }
++ }
++ parent.sizcache = doneName;
++ }
++
++ var diff = elem.nodeIndex - last;
++ if ( first === 0 ) {
++ return diff === 0;
++ } else {
++ return ( diff % first === 0 && diff / first >= 0 );
++ }
++ }
++ },
++ ID: function(elem, match){
++ return elem.nodeType === 1 && elem.getAttribute("id") === match;
++ },
++ TAG: function(elem, match){
++ return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match;
++ },
++ CLASS: function(elem, match){
++ return (" " + (elem.className || elem.getAttribute("class")) + " ")
++ .indexOf( match ) > -1;
++ },
++ ATTR: function(elem, match){
++ var name = match[1],
++ result = Expr.attrHandle[ name ] ?
++ Expr.attrHandle[ name ]( elem ) :
++ elem[ name ] != null ?
++ elem[ name ] :
++ elem.getAttribute( name ),
++ value = result + "",
++ type = match[2],
++ check = match[4];
++
++ return result == null ?
++ type === "!=" :
++ type === "=" ?
++ value === check :
++ type === "*=" ?
++ value.indexOf(check) >= 0 :
++ type === "~=" ?
++ (" " + value + " ").indexOf(check) >= 0 :
++ !check ?
++ value && result !== false :
++ type === "!=" ?
++ value !== check :
++ type === "^=" ?
++ value.indexOf(check) === 0 :
++ type === "$=" ?
++ value.substr(value.length - check.length) === check :
++ type === "|=" ?
++ value === check || value.substr(0, check.length + 1) === check + "-" :
++ false;
++ },
++ POS: function(elem, match, i, array){
++ var name = match[2], filter = Expr.setFilters[ name ];
++
++ if ( filter ) {
++ return filter( elem, i, match, array );
++ }
++ }
++ }
++};
++
++var origPOS = Expr.match.POS;
++
++for ( var type in Expr.match ) {
++ Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
++ Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, function(all, num){
++ return "\\" + (num - 0 + 1);
++ }));
++}
++
++var makeArray = function(array, results) {
++ array = Array.prototype.slice.call( array, 0 );
++
++ if ( results ) {
++ results.push.apply( results, array );
++ return results;
++ }
++
++ return array;
++};
++
++// Perform a simple check to determine if the browser is capable of
++// converting a NodeList to an array using builtin methods.
++// Also verifies that the returned array holds DOM nodes
++// (which is not the case in the Blackberry browser)
++try {
++ Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;
++
++// Provide a fallback method if it does not work
++} catch(e){
++ makeArray = function(array, results) {
++ var ret = results || [];
++
++ if ( toString.call(array) === "[object Array]" ) {
++ Array.prototype.push.apply( ret, array );
++ } else {
++ if ( typeof array.length === "number" ) {
++ for ( var i = 0, l = array.length; i < l; i++ ) {
++ ret.push( array[i] );
++ }
++ } else {
++ for ( var i = 0; array[i]; i++ ) {
++ ret.push( array[i] );
++ }
++ }
++ }
++
++ return ret;
++ };
++}
++
++var sortOrder;
++
++if ( document.documentElement.compareDocumentPosition ) {
++ sortOrder = function( a, b ) {
++ if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
++ if ( a == b ) {
++ hasDuplicate = true;
++ }
++ return a.compareDocumentPosition ? -1 : 1;
++ }
++
++ var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
++ if ( ret === 0 ) {
++ hasDuplicate = true;
++ }
++ return ret;
++ };
++} else if ( "sourceIndex" in document.documentElement ) {
++ sortOrder = function( a, b ) {
++ if ( !a.sourceIndex || !b.sourceIndex ) {
++ if ( a == b ) {
++ hasDuplicate = true;
++ }
++ return a.sourceIndex ? -1 : 1;
++ }
++
++ var ret = a.sourceIndex - b.sourceIndex;
++ if ( ret === 0 ) {
++ hasDuplicate = true;
++ }
++ return ret;
++ };
++} else if ( document.createRange ) {
++ sortOrder = function( a, b ) {
++ if ( !a.ownerDocument || !b.ownerDocument ) {
++ if ( a == b ) {
++ hasDuplicate = true;
++ }
++ return a.ownerDocument ? -1 : 1;
++ }
++
++ var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
++ aRange.setStart(a, 0);
++ aRange.setEnd(a, 0);
++ bRange.setStart(b, 0);
++ bRange.setEnd(b, 0);
++ var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
++ if ( ret === 0 ) {
++ hasDuplicate = true;
++ }
++ return ret;
++ };
++}
++
++// Utility function for retreiving the text value of an array of DOM nodes
++function getText( elems ) {
++ var ret = "", elem;
++
++ for ( var i = 0; elems[i]; i++ ) {
++ elem = elems[i];
++
++ // Get the text from text nodes and CDATA nodes
++ if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
++ ret += elem.nodeValue;
++
++ // Traverse everything else, except comment nodes
++ } else if ( elem.nodeType !== 8 ) {
++ ret += getText( elem.childNodes );
++ }
++ }
++
++ return ret;
++}
++
++// Check to see if the browser returns elements by name when
++// querying by getElementById (and provide a workaround)
++(function(){
++ // We're going to inject a fake input element with a specified name
++ var form = document.createElement("div"),
++ id = "script" + (new Date).getTime();
++ form.innerHTML = "<a name='" + id + "'/>";
++
++ // Inject it into the root element, check its status, and remove it quickly
++ var root = document.documentElement;
++ root.insertBefore( form, root.firstChild );
++
++ // The workaround has to do additional checks after a getElementById
++ // Which slows things down for other browsers (hence the branching)
++ if ( document.getElementById( id ) ) {
++ Expr.find.ID = function(match, context, isXML){
++ if ( typeof context.getElementById !== "undefined" && !isXML ) {
++ var m = context.getElementById(match[1]);
++ return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
++ }
++ };
++
++ Expr.filter.ID = function(elem, match){
++ var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
++ return elem.nodeType === 1 && node && node.nodeValue === match;
++ };
++ }
++
++ root.removeChild( form );
++ root = form = null; // release memory in IE
++})();
++
++(function(){
++ // Check to see if the browser returns only elements
++ // when doing getElementsByTagName("*")
++
++ // Create a fake element
++ var div = document.createElement("div");
++ div.appendChild( document.createComment("") );
++
++ // Make sure no comments are found
++ if ( div.getElementsByTagName("*").length > 0 ) {
++ Expr.find.TAG = function(match, context){
++ var results = context.getElementsByTagName(match[1]);
++
++ // Filter out possible comments
++ if ( match[1] === "*" ) {
++ var tmp = [];
++
++ for ( var i = 0; results[i]; i++ ) {
++ if ( results[i].nodeType === 1 ) {
++ tmp.push( results[i] );
++ }
++ }
++
++ results = tmp;
++ }
++
++ return results;
++ };
++ }
++
++ // Check to see if an attribute returns normalized href attributes
++ div.innerHTML = "<a href='#'></a>";
++ if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
++ div.firstChild.getAttribute("href") !== "#" ) {
++ Expr.attrHandle.href = function(elem){
++ return elem.getAttribute("href", 2);
++ };
++ }
++
++ div = null; // release memory in IE
++})();
++
++if ( document.querySelectorAll ) {
++ (function(){
++ var oldSizzle = Sizzle, div = document.createElement("div");
++ div.innerHTML = "<p class='TEST'></p>";
++
++ // Safari can't handle uppercase or unicode characters when
++ // in quirks mode.
++ if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
++ return;
++ }
++
++ Sizzle = function(query, context, extra, seed){
++ context = context || document;
++
++ // Only use querySelectorAll on non-XML documents
++ // (ID selectors don't work in non-HTML documents)
++ if ( !seed && context.nodeType === 9 && !isXML(context) ) {
++ try {
++ return makeArray( context.querySelectorAll(query), extra );
++ } catch(e){}
++ }
++
++ return oldSizzle(query, context, extra, seed);
++ };
++
++ for ( var prop in oldSizzle ) {
++ Sizzle[ prop ] = oldSizzle[ prop ];
++ }
++
++ div = null; // release memory in IE
++ })();
++}
++
++(function(){
++ var div = document.createElement("div");
++
++ div.innerHTML = "<div class='test e'></div><div class='test'></div>";
++
++ // Opera can't find a second classname (in 9.6)
++ // Also, make sure that getElementsByClassName actually exists
++ if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {
++ return;
++ }
++
++ // Safari caches class attributes, doesn't catch changes (in 3.2)
++ div.lastChild.className = "e";
++
++ if ( div.getElementsByClassName("e").length === 1 ) {
++ return;
++ }
++
++ Expr.order.splice(1, 0, "CLASS");
++ Expr.find.CLASS = function(match, context, isXML) {
++ if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
++ return context.getElementsByClassName(match[1]);
++ }
++ };
++
++ div = null; // release memory in IE
++})();
++
++function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
++ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
++ var elem = checkSet[i];
++ if ( elem ) {
++ elem = elem[dir];
++ var match = false;
++
++ while ( elem ) {
++ if ( elem.sizcache === doneName ) {
++ match = checkSet[elem.sizset];
++ break;
++ }
++
++ if ( elem.nodeType === 1 && !isXML ){
++ elem.sizcache = doneName;
++ elem.sizset = i;
++ }
++
++ if ( elem.nodeName.toLowerCase() === cur ) {
++ match = elem;
++ break;
++ }
++
++ elem = elem[dir];
++ }
++
++ checkSet[i] = match;
++ }
++ }
++}
++
++function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
++ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
++ var elem = checkSet[i];
++ if ( elem ) {
++ elem = elem[dir];
++ var match = false;
++
++ while ( elem ) {
++ if ( elem.sizcache === doneName ) {
++ match = checkSet[elem.sizset];
++ break;
++ }
++
++ if ( elem.nodeType === 1 ) {
++ if ( !isXML ) {
++ elem.sizcache = doneName;
++ elem.sizset = i;
++ }
++ if ( typeof cur !== "string" ) {
++ if ( elem === cur ) {
++ match = true;
++ break;
++ }
++
++ } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
++ match = elem;
++ break;
++ }
++ }
++
++ elem = elem[dir];
++ }
++
++ checkSet[i] = match;
++ }
++ }
++}
++
++var contains = document.compareDocumentPosition ? function(a, b){
++ return !!(a.compareDocumentPosition(b) & 16);
++} : function(a, b){
++ return a !== b && (a.contains ? a.contains(b) : true);
++};
++
++var isXML = function(elem){
++ // documentElement is verified for cases where it doesn't yet exist
++ // (such as loading iframes in IE - #4833)
++ var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
++ return documentElement ? documentElement.nodeName !== "HTML" : false;
++};
++
++var posProcess = function(selector, context){
++ var tmpSet = [], later = "", match,
++ root = context.nodeType ? [context] : context;
++
++ // Position selectors must be done after the filter
++ // And so must :not(positional) so we move all PSEUDOs to the end
++ while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
++ later += match[0];
++ selector = selector.replace( Expr.match.PSEUDO, "" );
++ }
++
++ selector = Expr.relative[selector] ? selector + "*" : selector;
++
++ for ( var i = 0, l = root.length; i < l; i++ ) {
++ Sizzle( selector, root[i], tmpSet );
++ }
++
++ return Sizzle.filter( later, tmpSet );
++};
++
++// EXPOSE
++jQuery.find = Sizzle;
++jQuery.expr = Sizzle.selectors;
++jQuery.expr[":"] = jQuery.expr.filters;
++jQuery.unique = Sizzle.uniqueSort;
++jQuery.text = getText;
++jQuery.isXMLDoc = isXML;
++jQuery.contains = contains;
++
++return;
++
++window.Sizzle = Sizzle;
++
++})();
++var runtil = /Until$/,
++ rparentsprev = /^(?:parents|prevUntil|prevAll)/,
++ // Note: This RegExp should be improved, or likely pulled from Sizzle
++ rmultiselector = /,/,
++ slice = Array.prototype.slice;
++
++// Implement the identical functionality for filter and not
++var winnow = function( elements, qualifier, keep ) {
++ if ( jQuery.isFunction( qualifier ) ) {
++ return jQuery.grep(elements, function( elem, i ) {
++ return !!qualifier.call( elem, i, elem ) === keep;
++ });
++
++ } else if ( qualifier.nodeType ) {
++ return jQuery.grep(elements, function( elem, i ) {
++ return (elem === qualifier) === keep;
++ });
++
++ } else if ( typeof qualifier === "string" ) {
++ var filtered = jQuery.grep(elements, function( elem ) {
++ return elem.nodeType === 1;
++ });
++
++ if ( isSimple.test( qualifier ) ) {
++ return jQuery.filter(qualifier, filtered, !keep);
++ } else {
++ qualifier = jQuery.filter( qualifier, filtered );
++ }
++ }
++
++ return jQuery.grep(elements, function( elem, i ) {
++ return (jQuery.inArray( elem, qualifier ) >= 0) === keep;
++ });
++};
++
++jQuery.fn.extend({
++ find: function( selector ) {
++ var ret = this.pushStack( "", "find", selector ), length = 0;
++
++ for ( var i = 0, l = this.length; i < l; i++ ) {
++ length = ret.length;
++ jQuery.find( selector, this[i], ret );
++
++ if ( i > 0 ) {
++ // Make sure that the results are unique
++ for ( var n = length; n < ret.length; n++ ) {
++ for ( var r = 0; r < length; r++ ) {
++ if ( ret[r] === ret[n] ) {
++ ret.splice(n--, 1);
++ break;
++ }
++ }
++ }
++ }
++ }
++
++ return ret;
++ },
++
++ has: function( target ) {
++ var targets = jQuery( target );
++ return this.filter(function() {
++ for ( var i = 0, l = targets.length; i < l; i++ ) {
++ if ( jQuery.contains( this, targets[i] ) ) {
++ return true;
++ }
++ }
++ });
++ },
++
++ not: function( selector ) {
++ return this.pushStack( winnow(this, selector, false), "not", selector);
++ },
++
++ filter: function( selector ) {
++ return this.pushStack( winnow(this, selector, true), "filter", selector );
++ },
++
++ is: function( selector ) {
++ return !!selector && jQuery.filter( selector, this ).length > 0;
++ },
++
++ closest: function( selectors, context ) {
++ if ( jQuery.isArray( selectors ) ) {
++ var ret = [], cur = this[0], match, matches = {}, selector;
++
++ if ( cur && selectors.length ) {
++ for ( var i = 0, l = selectors.length; i < l; i++ ) {
++ selector = selectors[i];
++
++ if ( !matches[selector] ) {
++ matches[selector] = jQuery.expr.match.POS.test( selector ) ?
++ jQuery( selector, context || this.context ) :
++ selector;
++ }
++ }
++
++ while ( cur && cur.ownerDocument && cur !== context ) {
++ for ( selector in matches ) {
++ match = matches[selector];
++
++ if ( match.jquery ? match.index(cur) > -1 : jQuery(cur).is(match) ) {
++ ret.push({ selector: selector, elem: cur });
++ delete matches[selector];
++ }
++ }
++ cur = cur.parentNode;
++ }
++ }
++
++ return ret;
++ }
++
++ var pos = jQuery.expr.match.POS.test( selectors ) ?
++ jQuery( selectors, context || this.context ) : null;
++
++ return this.map(function( i, cur ) {
++ while ( cur && cur.ownerDocument && cur !== context ) {
++ if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selectors) ) {
++ return cur;
++ }
++ cur = cur.parentNode;
++ }
++ return null;
++ });
++ },
++
++ // Determine the position of an element within
++ // the matched set of elements
++ index: function( elem ) {
++ if ( !elem || typeof elem === "string" ) {
++ return jQuery.inArray( this[0],
++ // If it receives a string, the selector is used
++ // If it receives nothing, the siblings are used
++ elem ? jQuery( elem ) : this.parent().children() );
++ }
++ // Locate the position of the desired element
++ return jQuery.inArray(
++ // If it receives a jQuery object, the first element is used
++ elem.jquery ? elem[0] : elem, this );
++ },
++
++ add: function( selector, context ) {
++ var set = typeof selector === "string" ?
++ jQuery( selector, context || this.context ) :
++ jQuery.makeArray( selector ),
++ all = jQuery.merge( this.get(), set );
++
++ return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?
++ all :
++ jQuery.unique( all ) );
++ },
++
++ andSelf: function() {
++ return this.add( this.prevObject );
++ }
++});
++
++// A painfully simple check to see if an element is disconnected
++// from a document (should be improved, where feasible).
++function isDisconnected( node ) {
++ return !node || !node.parentNode || node.parentNode.nodeType === 11;
++}
++
++jQuery.each({
++ parent: function( elem ) {
++ var parent = elem.parentNode;
++ return parent && parent.nodeType !== 11 ? parent : null;
++ },
++ parents: function( elem ) {
++ return jQuery.dir( elem, "parentNode" );
++ },
++ parentsUntil: function( elem, i, until ) {
++ return jQuery.dir( elem, "parentNode", until );
++ },
++ next: function( elem ) {
++ return jQuery.nth( elem, 2, "nextSibling" );
++ },
++ prev: function( elem ) {
++ return jQuery.nth( elem, 2, "previousSibling" );
++ },
++ nextAll: function( elem ) {
++ return jQuery.dir( elem, "nextSibling" );
++ },
++ prevAll: function( elem ) {
++ return jQuery.dir( elem, "previousSibling" );
++ },
++ nextUntil: function( elem, i, until ) {
++ return jQuery.dir( elem, "nextSibling", until );
++ },
++ prevUntil: function( elem, i, until ) {
++ return jQuery.dir( elem, "previousSibling", until );
++ },
++ siblings: function( elem ) {
++ return jQuery.sibling( elem.parentNode.firstChild, elem );
++ },
++ children: function( elem ) {
++ return jQuery.sibling( elem.firstChild );
++ },
++ contents: function( elem ) {
++ return jQuery.nodeName( elem, "iframe" ) ?
++ elem.contentDocument || elem.contentWindow.document :
++ jQuery.makeArray( elem.childNodes );
++ }
++}, function( name, fn ) {
++ jQuery.fn[ name ] = function( until, selector ) {
++ var ret = jQuery.map( this, fn, until );
++
++ if ( !runtil.test( name ) ) {
++ selector = until;
++ }
++
++ if ( selector && typeof selector === "string" ) {
++ ret = jQuery.filter( selector, ret );
++ }
++
++ ret = this.length > 1 ? jQuery.unique( ret ) : ret;
++
++ if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {
++ ret = ret.reverse();
++ }
++
++ return this.pushStack( ret, name, slice.call(arguments).join(",") );
++ };
++});
++
++jQuery.extend({
++ filter: function( expr, elems, not ) {
++ if ( not ) {
++ expr = ":not(" + expr + ")";
++ }
++
++ return jQuery.find.matches(expr, elems);
++ },
++
++ dir: function( elem, dir, until ) {
++ var matched = [], cur = elem[dir];
++ while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
++ if ( cur.nodeType === 1 ) {
++ matched.push( cur );
++ }
++ cur = cur[dir];
++ }
++ return matched;
++ },
++
++ nth: function( cur, result, dir, elem ) {
++ result = result || 1;
++ var num = 0;
++
++ for ( ; cur; cur = cur[dir] ) {
++ if ( cur.nodeType === 1 && ++num === result ) {
++ break;
++ }
++ }
++
++ return cur;
++ },
++
++ sibling: function( n, elem ) {
++ var r = [];
++
++ for ( ; n; n = n.nextSibling ) {
++ if ( n.nodeType === 1 && n !== elem ) {
++ r.push( n );
++ }
++ }
++
++ return r;
++ }
++});
++var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
++ rleadingWhitespace = /^\s+/,
++ rxhtmlTag = /(<([\w:]+)[^>]*?)\/>/g,
++ rselfClosing = /^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,
++ rtagName = /<([\w:]+)/,
++ rtbody = /<tbody/i,
++ rhtml = /<|&#?\w+;/,
++ rnocache = /<script|<object|<embed|<option|<style/i,
++ rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, // checked="checked" or checked (html5)
++ fcloseTag = function( all, front, tag ) {
++ return rselfClosing.test( tag ) ?
++ all :
++ front + "></" + tag + ">";
++ },
++ wrapMap = {
++ option: [ 1, "<select multiple='multiple'>", "</select>" ],
++ legend: [ 1, "<fieldset>", "</fieldset>" ],
++ thead: [ 1, "<table>", "</table>" ],
++ tr: [ 2, "<table><tbody>", "</tbody></table>" ],
++ td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
++ col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
++ area: [ 1, "<map>", "</map>" ],
++ _default: [ 0, "", "" ]
++ };
++
++wrapMap.optgroup = wrapMap.option;
++wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
++wrapMap.th = wrapMap.td;
++
++// IE can't serialize <link> and <script> tags normally
++if ( !jQuery.support.htmlSerialize ) {
++ wrapMap._default = [ 1, "div<div>", "</div>" ];
++}
++
++jQuery.fn.extend({
++ text: function( text ) {
++ if ( jQuery.isFunction(text) ) {
++ return this.each(function(i) {
++ var self = jQuery(this);
++ self.text( text.call(this, i, self.text()) );
++ });
++ }
++
++ if ( typeof text !== "object" && text !== undefined ) {
++ return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
++ }
++
++ return jQuery.text( this );
++ },
++
++ wrapAll: function( html ) {
++ if ( jQuery.isFunction( html ) ) {
++ return this.each(function(i) {
++ jQuery(this).wrapAll( html.call(this, i) );
++ });
++ }
++
++ if ( this[0] ) {
++ // The elements to wrap the target around
++ var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
++
++ if ( this[0].parentNode ) {
++ wrap.insertBefore( this[0] );
++ }
++
++ wrap.map(function() {
++ var elem = this;
++
++ while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
++ elem = elem.firstChild;
++ }
++
++ return elem;
++ }).append(this);
++ }
++
++ return this;
++ },
++
++ wrapInner: function( html ) {
++ if ( jQuery.isFunction( html ) ) {
++ return this.each(function(i) {
++ jQuery(this).wrapInner( html.call(this, i) );
++ });
++ }
++
++ return this.each(function() {
++ var self = jQuery( this ), contents = self.contents();
++
++ if ( contents.length ) {
++ contents.wrapAll( html );
++
++ } else {
++ self.append( html );
++ }
++ });
++ },
++
++ wrap: function( html ) {
++ return this.each(function() {
++ jQuery( this ).wrapAll( html );
++ });
++ },
++
++ unwrap: function() {
++ return this.parent().each(function() {
++ if ( !jQuery.nodeName( this, "body" ) ) {
++ jQuery( this ).replaceWith( this.childNodes );
++ }
++ }).end();
++ },
++
++ append: function() {
++ return this.domManip(arguments, true, function( elem ) {
++ if ( this.nodeType === 1 ) {
++ this.appendChild( elem );
++ }
++ });
++ },
++
++ prepend: function() {
++ return this.domManip(arguments, true, function( elem ) {
++ if ( this.nodeType === 1 ) {
++ this.insertBefore( elem, this.firstChild );
++ }
++ });
++ },
++
++ before: function() {
++ if ( this[0] && this[0].parentNode ) {
++ return this.domManip(arguments, false, function( elem ) {
++ this.parentNode.insertBefore( elem, this );
++ });
++ } else if ( arguments.length ) {
++ var set = jQuery(arguments[0]);
++ set.push.apply( set, this.toArray() );
++ return this.pushStack( set, "before", arguments );
++ }
++ },
++
++ after: function() {
++ if ( this[0] && this[0].parentNode ) {
++ return this.domManip(arguments, false, function( elem ) {
++ this.parentNode.insertBefore( elem, this.nextSibling );
++ });
++ } else if ( arguments.length ) {
++ var set = this.pushStack( this, "after", arguments );
++ set.push.apply( set, jQuery(arguments[0]).toArray() );
++ return set;
++ }
++ },
++
++ // keepData is for internal use only--do not document
++ remove: function( selector, keepData ) {
++ for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
++ if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
++ if ( !keepData && elem.nodeType === 1 ) {
++ jQuery.cleanData( elem.getElementsByTagName("*") );
++ jQuery.cleanData( [ elem ] );
++ }
++
++ if ( elem.parentNode ) {
++ elem.parentNode.removeChild( elem );
++ }
++ }
++ }
++
++ return this;
++ },
++
++ empty: function() {
++ for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
++ // Remove element nodes and prevent memory leaks
++ if ( elem.nodeType === 1 ) {
++ jQuery.cleanData( elem.getElementsByTagName("*") );
++ }
++
++ // Remove any remaining nodes
++ while ( elem.firstChild ) {
++ elem.removeChild( elem.firstChild );
++ }
++ }
++
++ return this;
++ },
++
++ clone: function( events ) {
++ // Do the clone
++ var ret = this.map(function() {
++ if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) {
++ // IE copies events bound via attachEvent when
++ // using cloneNode. Calling detachEvent on the
++ // clone will also remove the events from the orignal
++ // In order to get around this, we use innerHTML.
++ // Unfortunately, this means some modifications to
++ // attributes in IE that are actually only stored
++ // as properties will not be copied (such as the
++ // the name attribute on an input).
++ var html = this.outerHTML, ownerDocument = this.ownerDocument;
++ if ( !html ) {
++ var div = ownerDocument.createElement("div");
++ div.appendChild( this.cloneNode(true) );
++ html = div.innerHTML;
++ }
++
++ return jQuery.clean([html.replace(rinlinejQuery, "")
++ // Handle the case in IE 8 where action=/test/> self-closes a tag
++ .replace(/=([^="'>\s]+\/)>/g, '="$1">')
++ .replace(rleadingWhitespace, "")], ownerDocument)[0];
++ } else {
++ return this.cloneNode(true);
++ }
++ });
++
++ // Copy the events from the original to the clone
++ if ( events === true ) {
++ cloneCopyEvent( this, ret );
++ cloneCopyEvent( this.find("*"), ret.find("*") );
++ }
++
++ // Return the cloned set
++ return ret;
++ },
++
++ html: function( value ) {
++ if ( value === undefined ) {
++ return this[0] && this[0].nodeType === 1 ?
++ this[0].innerHTML.replace(rinlinejQuery, "") :
++ null;
++
++ // See if we can take a shortcut and just use innerHTML
++ } else if ( typeof value === "string" && !rnocache.test( value ) &&
++ (jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
++ !wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {
++
++ value = value.replace(rxhtmlTag, fcloseTag);
++
++ try {
++ for ( var i = 0, l = this.length; i < l; i++ ) {
++ // Remove element nodes and prevent memory leaks
++ if ( this[i].nodeType === 1 ) {
++ jQuery.cleanData( this[i].getElementsByTagName("*") );
++ this[i].innerHTML = value;
++ }
++ }
++
++ // If using innerHTML throws an exception, use the fallback method
++ } catch(e) {
++ this.empty().append( value );
++ }
++
++ } else if ( jQuery.isFunction( value ) ) {
++ this.each(function(i){
++ var self = jQuery(this), old = self.html();
++ self.empty().append(function(){
++ return value.call( this, i, old );
++ });
++ });
++
++ } else {
++ this.empty().append( value );
++ }
++
++ return this;
++ },
++
++ replaceWith: function( value ) {
++ if ( this[0] && this[0].parentNode ) {
++ // Make sure that the elements are removed from the DOM before they are inserted
++ // this can help fix replacing a parent with child elements
++ if ( jQuery.isFunction( value ) ) {
++ return this.each(function(i) {
++ var self = jQuery(this), old = self.html();
++ self.replaceWith( value.call( this, i, old ) );
++ });
++ }
++
++ if ( typeof value !== "string" ) {
++ value = jQuery(value).detach();
++ }
++
++ return this.each(function() {
++ var next = this.nextSibling, parent = this.parentNode;
++
++ jQuery(this).remove();
++
++ if ( next ) {
++ jQuery(next).before( value );
++ } else {
++ jQuery(parent).append( value );
++ }
++ });
++ } else {
++ return this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value );
++ }
++ },
++
++ detach: function( selector ) {
++ return this.remove( selector, true );
++ },
++
++ domManip: function( args, table, callback ) {
++ var results, first, value = args[0], scripts = [], fragment, parent;
++
++ // We can't cloneNode fragments that contain checked, in WebKit
++ if ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === "string" && rchecked.test( value ) ) {
++ return this.each(function() {
++ jQuery(this).domManip( args, table, callback, true );
++ });
++ }
++
++ if ( jQuery.isFunction(value) ) {
++ return this.each(function(i) {
++ var self = jQuery(this);
++ args[0] = value.call(this, i, table ? self.html() : undefined);
++ self.domManip( args, table, callback );
++ });
++ }
++
++ if ( this[0] ) {
++ parent = value && value.parentNode;
++
++ // If we're in a fragment, just use that instead of building a new one
++ if ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) {
++ results = { fragment: parent };
++
++ } else {
++ results = buildFragment( args, this, scripts );
++ }
++
++ fragment = results.fragment;
++
++ if ( fragment.childNodes.length === 1 ) {
++ first = fragment = fragment.firstChild;
++ } else {
++ first = fragment.firstChild;
++ }
++
++ if ( first ) {
++ table = table && jQuery.nodeName( first, "tr" );
++
++ for ( var i = 0, l = this.length; i < l; i++ ) {
++ callback.call(
++ table ?
++ root(this[i], first) :
++ this[i],
++ i > 0 || results.cacheable || this.length > 1 ?
++ fragment.cloneNode(true) :
++ fragment
++ );
++ }
++ }
++
++ if ( scripts.length ) {
++ jQuery.each( scripts, evalScript );
++ }
++ }
++
++ return this;
++
++ function root( elem, cur ) {
++ return jQuery.nodeName(elem, "table") ?
++ (elem.getElementsByTagName("tbody")[0] ||
++ elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
++ elem;
++ }
++ }
++});
++
++function cloneCopyEvent(orig, ret) {
++ var i = 0;
++
++ ret.each(function() {
++ if ( this.nodeName !== (orig[i] && orig[i].nodeName) ) {
++ return;
++ }
++
++ var oldData = jQuery.data( orig[i++] ), curData = jQuery.data( this, oldData ), events = oldData && oldData.events;
++
++ if ( events ) {
++ delete curData.handle;
++ curData.events = {};
++
++ for ( var type in events ) {
++ for ( var handler in events[ type ] ) {
++ jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data );
++ }
++ }
++ }
++ });
++}
++
++function buildFragment( args, nodes, scripts ) {
++ var fragment, cacheable, cacheresults,
++ doc = (nodes && nodes[0] ? nodes[0].ownerDocument || nodes[0] : document);
++
++ // Only cache "small" (1/2 KB) strings that are associated with the main document
++ // Cloning options loses the selected state, so don't cache them
++ // IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
++ // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
++ if ( args.length === 1 && typeof args[0] === "string" && args[0].length < 512 && doc === document &&
++ !rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) {
++
++ cacheable = true;
++ cacheresults = jQuery.fragments[ args[0] ];
++ if ( cacheresults ) {
++ if ( cacheresults !== 1 ) {
++ fragment = cacheresults;
++ }
++ }
++ }
++
++ if ( !fragment ) {
++ fragment = doc.createDocumentFragment();
++ jQuery.clean( args, doc, fragment, scripts );
++ }
++
++ if ( cacheable ) {
++ jQuery.fragments[ args[0] ] = cacheresults ? fragment : 1;
++ }
++
++ return { fragment: fragment, cacheable: cacheable };
++}
++
++jQuery.fragments = {};
++
++jQuery.each({
++ appendTo: "append",
++ prependTo: "prepend",
++ insertBefore: "before",
++ insertAfter: "after",
++ replaceAll: "replaceWith"
++}, function( name, original ) {
++ jQuery.fn[ name ] = function( selector ) {
++ var ret = [], insert = jQuery( selector ),
++ parent = this.length === 1 && this[0].parentNode;
++
++ if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {
++ insert[ original ]( this[0] );
++ return this;
++
++ } else {
++ for ( var i = 0, l = insert.length; i < l; i++ ) {
++ var elems = (i > 0 ? this.clone(true) : this).get();
++ jQuery.fn[ original ].apply( jQuery(insert[i]), elems );
++ ret = ret.concat( elems );
++ }
++
++ return this.pushStack( ret, name, insert.selector );
++ }
++ };
++});
++
++jQuery.extend({
++ clean: function( elems, context, fragment, scripts ) {
++ context = context || document;
++
++ // !context.createElement fails in IE with an error but returns typeof 'object'
++ if ( typeof context.createElement === "undefined" ) {
++ context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
++ }
++
++ var ret = [];
++
++ for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
++ if ( typeof elem === "number" ) {
++ elem += "";
++ }
++
++ if ( !elem ) {
++ continue;
++ }
++
++ // Convert html string into DOM nodes
++ if ( typeof elem === "string" && !rhtml.test( elem ) ) {
++ elem = context.createTextNode( elem );
++
++ } else if ( typeof elem === "string" ) {
++ // Fix "XHTML"-style tags in all browsers
++ elem = elem.replace(rxhtmlTag, fcloseTag);
++
++ // Trim whitespace, otherwise indexOf won't work as expected
++ var tag = (rtagName.exec( elem ) || ["", ""])[1].toLowerCase(),
++ wrap = wrapMap[ tag ] || wrapMap._default,
++ depth = wrap[0],
++ div = context.createElement("div");
++
++ // Go to html and back, then peel off extra wrappers
++ div.innerHTML = wrap[1] + elem + wrap[2];
++
++ // Move to the right depth
++ while ( depth-- ) {
++ div = div.lastChild;
++ }
++
++ // Remove IE's autoinserted <tbody> from table fragments
++ if ( !jQuery.support.tbody ) {
++
++ // String was a <table>, *may* have spurious <tbody>
++ var hasBody = rtbody.test(elem),
++ tbody = tag === "table" && !hasBody ?
++ div.firstChild && div.firstChild.childNodes :
++
++ // String was a bare <thead> or <tfoot>
++ wrap[1] === "<table>" && !hasBody ?
++ div.childNodes :
++ [];
++
++ for ( var j = tbody.length - 1; j >= 0 ; --j ) {
++ if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
++ tbody[ j ].parentNode.removeChild( tbody[ j ] );
++ }
++ }
++
++ }
++
++ // IE completely kills leading whitespace when innerHTML is used
++ if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
++ div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
++ }
++
++ elem = div.childNodes;
++ }
++
++ if ( elem.nodeType ) {
++ ret.push( elem );
++ } else {
++ ret = jQuery.merge( ret, elem );
++ }
++ }
++
++ if ( fragment ) {
++ for ( var i = 0; ret[i]; i++ ) {
++ if ( scripts && jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
++ scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
++
++ } else {
++ if ( ret[i].nodeType === 1 ) {
++ ret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))) );
++ }
++ fragment.appendChild( ret[i] );
++ }
++ }
++ }
++
++ return ret;
++ },
++
++ cleanData: function( elems ) {
++ var data, id, cache = jQuery.cache,
++ special = jQuery.event.special,
++ deleteExpando = jQuery.support.deleteExpando;
++
++ for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
++ id = elem[ jQuery.expando ];
++
++ if ( id ) {
++ data = cache[ id ];
++
++ if ( data.events ) {
++ for ( var type in data.events ) {
++ if ( special[ type ] ) {
++ jQuery.event.remove( elem, type );
++
++ } else {
++ removeEvent( elem, type, data.handle );
++ }
++ }
++ }
++
++ if ( deleteExpando ) {
++ delete elem[ jQuery.expando ];
++
++ } else if ( elem.removeAttribute ) {
++ elem.removeAttribute( jQuery.expando );
++ }
++
++ delete cache[ id ];
++ }
++ }
++ }
++});
++// exclude the following css properties to add px
++var rexclude = /z-?index|font-?weight|opacity|zoom|line-?height/i,
++ ralpha = /alpha\([^)]*\)/,
++ ropacity = /opacity=([^)]*)/,
++ rfloat = /float/i,
++ rdashAlpha = /-([a-z])/ig,
++ rupper = /([A-Z])/g,
++ rnumpx = /^-?\d+(?:px)?$/i,
++ rnum = /^-?\d/,
++
++ cssShow = { position: "absolute", visibility: "hidden", display:"block" },
++ cssWidth = [ "Left", "Right" ],
++ cssHeight = [ "Top", "Bottom" ],
++
++ // cache check for defaultView.getComputedStyle
++ getComputedStyle = document.defaultView && document.defaultView.getComputedStyle,
++ // normalize float css property
++ styleFloat = jQuery.support.cssFloat ? "cssFloat" : "styleFloat",
++ fcamelCase = function( all, letter ) {
++ return letter.toUpperCase();
++ };
++
++jQuery.fn.css = function( name, value ) {
++ return access( this, name, value, true, function( elem, name, value ) {
++ if ( value === undefined ) {
++ return jQuery.curCSS( elem, name );
++ }
++
++ if ( typeof value === "number" && !rexclude.test(name) ) {
++ value += "px";
++ }
++
++ jQuery.style( elem, name, value );
++ });
++};
++
++jQuery.extend({
++ style: function( elem, name, value ) {
++ // don't set styles on text and comment nodes
++ if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
++ return undefined;
++ }
++
++ // ignore negative width and height values #1599
++ if ( (name === "width" || name === "height") && parseFloat(value) < 0 ) {
++ value = undefined;
++ }
++
++ var style = elem.style || elem, set = value !== undefined;
++
++ // IE uses filters for opacity
++ if ( !jQuery.support.opacity && name === "opacity" ) {
++ if ( set ) {
++ // IE has trouble with opacity if it does not have layout
++ // Force it by setting the zoom level
++ style.zoom = 1;
++
++ // Set the alpha filter to set the opacity
++ var opacity = parseInt( value, 10 ) + "" === "NaN" ? "" : "alpha(opacity=" + value * 100 + ")";
++ var filter = style.filter || jQuery.curCSS( elem, "filter" ) || "";
++ style.filter = ralpha.test(filter) ? filter.replace(ralpha, opacity) : opacity;
++ }
++
++ return style.filter && style.filter.indexOf("opacity=") >= 0 ?
++ (parseFloat( ropacity.exec(style.filter)[1] ) / 100) + "":
++ "";
++ }
++
++ // Make sure we're using the right name for getting the float value
++ if ( rfloat.test( name ) ) {
++ name = styleFloat;
++ }
++
++ name = name.replace(rdashAlpha, fcamelCase);
++
++ if ( set ) {
++ style[ name ] = value;
++ }
++
++ return style[ name ];
++ },
++
++ css: function( elem, name, force, extra ) {
++ if ( name === "width" || name === "height" ) {
++ var val, props = cssShow, which = name === "width" ? cssWidth : cssHeight;
++
++ function getWH() {
++ val = name === "width" ? elem.offsetWidth : elem.offsetHeight;
++
++ if ( extra === "border" ) {
++ return;
++ }
++
++ jQuery.each( which, function() {
++ if ( !extra ) {
++ val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0;
++ }
++
++ if ( extra === "margin" ) {
++ val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0;
++ } else {
++ val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0;
++ }
++ });
++ }
++
++ if ( elem.offsetWidth !== 0 ) {
++ getWH();
++ } else {
++ jQuery.swap( elem, props, getWH );
++ }
++
++ return Math.max(0, Math.round(val));
++ }
++
++ return jQuery.curCSS( elem, name, force );
++ },
++
++ curCSS: function( elem, name, force ) {
++ var ret, style = elem.style, filter;
++
++ // IE uses filters for opacity
++ if ( !jQuery.support.opacity && name === "opacity" && elem.currentStyle ) {
++ ret = ropacity.test(elem.currentStyle.filter || "") ?
++ (parseFloat(RegExp.$1) / 100) + "" :
++ "";
++
++ return ret === "" ?
++ "1" :
++ ret;
++ }
++
++ // Make sure we're using the right name for getting the float value
++ if ( rfloat.test( name ) ) {
++ name = styleFloat;
++ }
++
++ if ( !force && style && style[ name ] ) {
++ ret = style[ name ];
++
++ } else if ( getComputedStyle ) {
++
++ // Only "float" is needed here
++ if ( rfloat.test( name ) ) {
++ name = "float";
++ }
++
++ name = name.replace( rupper, "-$1" ).toLowerCase();
++
++ var defaultView = elem.ownerDocument.defaultView;
++
++ if ( !defaultView ) {
++ return null;
++ }
++
++ var computedStyle = defaultView.getComputedStyle( elem, null );
++
++ if ( computedStyle ) {
++ ret = computedStyle.getPropertyValue( name );
++ }
++
++ // We should always get a number back from opacity
++ if ( name === "opacity" && ret === "" ) {
++ ret = "1";
++ }
++
++ } else if ( elem.currentStyle ) {
++ var camelCase = name.replace(rdashAlpha, fcamelCase);
++
++ ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ];
++
++ // From the awesome hack by Dean Edwards
++ // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
++
++ // If we're not dealing with a regular pixel number
++ // but a number that has a weird ending, we need to convert it to pixels
++ if ( !rnumpx.test( ret ) && rnum.test( ret ) ) {
++ // Remember the original values
++ var left = style.left, rsLeft = elem.runtimeStyle.left;
++
++ // Put in the new values to get a computed value out
++ elem.runtimeStyle.left = elem.currentStyle.left;
++ style.left = camelCase === "fontSize" ? "1em" : (ret || 0);
++ ret = style.pixelLeft + "px";
++
++ // Revert the changed values
++ style.left = left;
++ elem.runtimeStyle.left = rsLeft;
++ }
++ }
++
++ return ret;
++ },
++
++ // A method for quickly swapping in/out CSS properties to get correct calculations
++ swap: function( elem, options, callback ) {
++ var old = {};
++
++ // Remember the old values, and insert the new ones
++ for ( var name in options ) {
++ old[ name ] = elem.style[ name ];
++ elem.style[ name ] = options[ name ];
++ }
++
++ callback.call( elem );
++
++ // Revert the old values
++ for ( var name in options ) {
++ elem.style[ name ] = old[ name ];
++ }
++ }
++});
++
++if ( jQuery.expr && jQuery.expr.filters ) {
++ jQuery.expr.filters.hidden = function( elem ) {
++ var width = elem.offsetWidth, height = elem.offsetHeight,
++ skip = elem.nodeName.toLowerCase() === "tr";
++
++ return width === 0 && height === 0 && !skip ?
++ true :
++ width > 0 && height > 0 && !skip ?
++ false :
++ jQuery.curCSS(elem, "display") === "none";
++ };
++
++ jQuery.expr.filters.visible = function( elem ) {
++ return !jQuery.expr.filters.hidden( elem );
++ };
++}
++var jsc = now(),
++ rscript = /<script(.|\s)*?\/script>/gi,
++ rselectTextarea = /select|textarea/i,
++ rinput = /color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,
++ jsre = /=\?(&|$)/,
++ rquery = /\?/,
++ rts = /(\?|&)_=.*?(&|$)/,
++ rurl = /^(\w+:)?\/\/([^\/?#]+)/,
++ r20 = /%20/g,
++
++ // Keep a copy of the old load method
++ _load = jQuery.fn.load;
++
++jQuery.fn.extend({
++ load: function( url, params, callback ) {
++ if ( typeof url !== "string" ) {
++ return _load.call( this, url );
++
++ // Don't do a request if no elements are being requested
++ } else if ( !this.length ) {
++ return this;
++ }
++
++ var off = url.indexOf(" ");
++ if ( off >= 0 ) {
++ var selector = url.slice(off, url.length);
++ url = url.slice(0, off);
++ }
++
++ // Default to a GET request
++ var type = "GET";
++
++ // If the second parameter was provided
++ if ( params ) {
++ // If it's a function
++ if ( jQuery.isFunction( params ) ) {
++ // We assume that it's the callback
++ callback = params;
++ params = null;
++
++ // Otherwise, build a param string
++ } else if ( typeof params === "object" ) {
++ params = jQuery.param( params, jQuery.ajaxSettings.traditional );
++ type = "POST";
++ }
++ }
++
++ var self = this;
++
++ // Request the remote document
++ jQuery.ajax({
++ url: url,
++ type: type,
++ dataType: "html",
++ data: params,
++ complete: function( res, status ) {
++ // If successful, inject the HTML into all the matched elements
++ if ( status === "success" || status === "notmodified" ) {
++ // See if a selector was specified
++ self.html( selector ?
++ // Create a dummy div to hold the results
++ jQuery("<div />")
++ // inject the contents of the document in, removing the scripts
++ // to avoid any 'Permission Denied' errors in IE
++ .append(res.responseText.replace(rscript, ""))
++
++ // Locate the specified elements
++ .find(selector) :
++
++ // If not, just inject the full result
++ res.responseText );
++ }
++
++ if ( callback ) {
++ self.each( callback, [res.responseText, status, res] );
++ }
++ }
++ });
++
++ return this;
++ },
++
++ serialize: function() {
++ return jQuery.param(this.serializeArray());
++ },
++ serializeArray: function() {
++ return this.map(function() {
++ return this.elements ? jQuery.makeArray(this.elements) : this;
++ })
++ .filter(function() {
++ return this.name && !this.disabled &&
++ (this.checked || rselectTextarea.test(this.nodeName) ||
++ rinput.test(this.type));
++ })
++ .map(function( i, elem ) {
++ var val = jQuery(this).val();
++
++ return val == null ?
++ null :
++ jQuery.isArray(val) ?
++ jQuery.map( val, function( val, i ) {
++ return { name: elem.name, value: val };
++ }) :
++ { name: elem.name, value: val };
++ }).get();
++ }
++});
++
++// Attach a bunch of functions for handling common AJAX events
++jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), function( i, o ) {
++ jQuery.fn[o] = function( f ) {
++ return this.bind(o, f);
++ };
++});
++
++jQuery.extend({
++
++ get: function( url, data, callback, type ) {
++ // shift arguments if data argument was omited
++ if ( jQuery.isFunction( data ) ) {
++ type = type || callback;
++ callback = data;
++ data = null;
++ }
++
++ return jQuery.ajax({
++ type: "GET",
++ url: url,
++ data: data,
++ success: callback,
++ dataType: type
++ });
++ },
++
++ getScript: function( url, callback ) {
++ return jQuery.get(url, null, callback, "script");
++ },
++
++ getJSON: function( url, data, callback ) {
++ return jQuery.get(url, data, callback, "json");
++ },
++
++ post: function( url, data, callback, type ) {
++ // shift arguments if data argument was omited
++ if ( jQuery.isFunction( data ) ) {
++ type = type || callback;
++ callback = data;
++ data = {};
++ }
++
++ return jQuery.ajax({
++ type: "POST",
++ url: url,
++ data: data,
++ success: callback,
++ dataType: type
++ });
++ },
++
++ ajaxSetup: function( settings ) {
++ jQuery.extend( jQuery.ajaxSettings, settings );
++ },
++
++ ajaxSettings: {
++ url: location.href,
++ global: true,
++ type: "GET",
++ contentType: "application/x-www-form-urlencoded",
++ processData: true,
++ async: true,
++ /*
++ timeout: 0,
++ data: null,
++ username: null,
++ password: null,
++ traditional: false,
++ */
++ // Create the request object; Microsoft failed to properly
++ // implement the XMLHttpRequest in IE7 (can't request local files),
++ // so we use the ActiveXObject when it is available
++ // This function can be overriden by calling jQuery.ajaxSetup
++ xhr: window.XMLHttpRequest && (window.location.protocol !== "file:" || !window.ActiveXObject) ?
++ function() {
++ return new window.XMLHttpRequest();
++ } :
++ function() {
++ try {
++ return new window.ActiveXObject("Microsoft.XMLHTTP");
++ } catch(e) {}
++ },
++ accepts: {
++ xml: "application/xml, text/xml",
++ html: "text/html",
++ script: "text/javascript, application/javascript",
++ json: "application/json, text/javascript",
++ text: "text/plain",
++ _default: "*/*"
++ }
++ },
++
++ // Last-Modified header cache for next request
++ lastModified: {},
++ etag: {},
++
++ ajax: function( origSettings ) {
++ var s = jQuery.extend(true, {}, jQuery.ajaxSettings, origSettings);
++
++ var jsonp, status, data,
++ callbackContext = origSettings && origSettings.context || s,
++ type = s.type.toUpperCase();
++
++ // convert data if not already a string
++ if ( s.data && s.processData && typeof s.data !== "string" ) {
++ s.data = jQuery.param( s.data, s.traditional );
++ }
++
++ // Handle JSONP Parameter Callbacks
++ if ( s.dataType === "jsonp" ) {
++ if ( type === "GET" ) {
++ if ( !jsre.test( s.url ) ) {
++ s.url += (rquery.test( s.url ) ? "&" : "?") + (s.jsonp || "callback") + "=?";
++ }
++ } else if ( !s.data || !jsre.test(s.data) ) {
++ s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
++ }
++ s.dataType = "json";
++ }
++
++ // Build temporary JSONP function
++ if ( s.dataType === "json" && (s.data && jsre.test(s.data) || jsre.test(s.url)) ) {
++ jsonp = s.jsonpCallback || ("jsonp" + jsc++);
++
++ // Replace the =? sequence both in the query string and the data
++ if ( s.data ) {
++ s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
++ }
++
++ s.url = s.url.replace(jsre, "=" + jsonp + "$1");
++
++ // We need to make sure
++ // that a JSONP style response is executed properly
++ s.dataType = "script";
++
++ // Handle JSONP-style loading
++ window[ jsonp ] = window[ jsonp ] || function( tmp ) {
++ data = tmp;
++ success();
++ complete();
++ // Garbage collect
++ window[ jsonp ] = undefined;
++
++ try {
++ delete window[ jsonp ];
++ } catch(e) {}
++
++ if ( head ) {
++ head.removeChild( script );
++ }
++ };
++ }
++
++ if ( s.dataType === "script" && s.cache === null ) {
++ s.cache = false;
++ }
++
++ if ( s.cache === false && type === "GET" ) {
++ var ts = now();
++
++ // try replacing _= if it is there
++ var ret = s.url.replace(rts, "$1_=" + ts + "$2");
++
++ // if nothing was replaced, add timestamp to the end
++ s.url = ret + ((ret === s.url) ? (rquery.test(s.url) ? "&" : "?") + "_=" + ts : "");
++ }
++
++ // If data is available, append data to url for get requests
++ if ( s.data && type === "GET" ) {
++ s.url += (rquery.test(s.url) ? "&" : "?") + s.data;
++ }
++
++ // Watch for a new set of requests
++ if ( s.global && ! jQuery.active++ ) {
++ jQuery.event.trigger( "ajaxStart" );
++ }
++
++ // Matches an absolute URL, and saves the domain
++ var parts = rurl.exec( s.url ),
++ remote = parts && (parts[1] && parts[1] !== location.protocol || parts[2] !== location.host);
++
++ // If we're requesting a remote document
++ // and trying to load JSON or Script with a GET
++ if ( s.dataType === "script" && type === "GET" && remote ) {
++ var head = document.getElementsByTagName("head")[0] || document.documentElement;
++ var script = document.createElement("script");
++ script.src = s.url;
++ if ( s.scriptCharset ) {
++ script.charset = s.scriptCharset;
++ }
++
++ // Handle Script loading
++ if ( !jsonp ) {
++ var done = false;
++
++ // Attach handlers for all browsers
++ script.onload = script.onreadystatechange = function() {
++ if ( !done && (!this.readyState ||
++ this.readyState === "loaded" || this.readyState === "complete") ) {
++ done = true;
++ success();
++ complete();
++
++ // Handle memory leak in IE
++ script.onload = script.onreadystatechange = null;
++ if ( head && script.parentNode ) {
++ head.removeChild( script );
++ }
++ }
++ };
++ }
++
++ // Use insertBefore instead of appendChild to circumvent an IE6 bug.
++ // This arises when a base node is used (#2709 and #4378).
++ head.insertBefore( script, head.firstChild );
++
++ // We handle everything using the script element injection
++ return undefined;
++ }
++
++ var requestDone = false;
++
++ // Create the request object
++ var xhr = s.xhr();
++
++ if ( !xhr ) {
++ return;
++ }
++
++ // Open the socket
++ // Passing null username, generates a login popup on Opera (#2865)
++ if ( s.username ) {
++ xhr.open(type, s.url, s.async, s.username, s.password);
++ } else {
++ xhr.open(type, s.url, s.async);
++ }
++
++ // Need an extra try/catch for cross domain requests in Firefox 3
++ try {
++ // Set the correct header, if data is being sent
++ if ( s.data || origSettings && origSettings.contentType ) {
++ xhr.setRequestHeader("Content-Type", s.contentType);
++ }
++
++ // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
++ if ( s.ifModified ) {
++ if ( jQuery.lastModified[s.url] ) {
++ xhr.setRequestHeader("If-Modified-Since", jQuery.lastModified[s.url]);
++ }
++
++ if ( jQuery.etag[s.url] ) {
++ xhr.setRequestHeader("If-None-Match", jQuery.etag[s.url]);
++ }
++ }
++
++ // Set header so the called script knows that it's an XMLHttpRequest
++ // Only send the header if it's not a remote XHR
++ if ( !remote ) {
++ xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
++ }
++
++ // Set the Accepts header for the server, depending on the dataType
++ xhr.setRequestHeader("Accept", s.dataType && s.accepts[ s.dataType ] ?
++ s.accepts[ s.dataType ] + ", */*" :
++ s.accepts._default );
++ } catch(e) {}
++
++ // Allow custom headers/mimetypes and early abort
++ if ( s.beforeSend && s.beforeSend.call(callbackContext, xhr, s) === false ) {
++ // Handle the global AJAX counter
++ if ( s.global && ! --jQuery.active ) {
++ jQuery.event.trigger( "ajaxStop" );
++ }
++
++ // close opended socket
++ xhr.abort();
++ return false;
++ }
++
++ if ( s.global ) {
++ trigger("ajaxSend", [xhr, s]);
++ }
++
++ // Wait for a response to come back
++ var onreadystatechange = xhr.onreadystatechange = function( isTimeout ) {
++ // The request was aborted
++ if ( !xhr || xhr.readyState === 0 || isTimeout === "abort" ) {
++ // Opera doesn't call onreadystatechange before this point
++ // so we simulate the call
++ if ( !requestDone ) {
++ complete();
++ }
++
++ requestDone = true;
++ if ( xhr ) {
++ xhr.onreadystatechange = jQuery.noop;
++ }
++
++ // The transfer is complete and the data is available, or the request timed out
++ } else if ( !requestDone && xhr && (xhr.readyState === 4 || isTimeout === "timeout") ) {
++ requestDone = true;
++ xhr.onreadystatechange = jQuery.noop;
++
++ status = isTimeout === "timeout" ?
++ "timeout" :
++ !jQuery.httpSuccess( xhr ) ?
++ "error" :
++ s.ifModified && jQuery.httpNotModified( xhr, s.url ) ?
++ "notmodified" :
++ "success";
++
++ var errMsg;
++
++ if ( status === "success" ) {
++ // Watch for, and catch, XML document parse errors
++ try {
++ // process the data (runs the xml through httpData regardless of callback)
++ data = jQuery.httpData( xhr, s.dataType, s );
++ } catch(err) {
++ status = "parsererror";
++ errMsg = err;
++ }
++ }
++
++ // Make sure that the request was successful or notmodified
++ if ( status === "success" || status === "notmodified" ) {
++ // JSONP handles its own success callback
++ if ( !jsonp ) {
++ success();
++ }
++ } else {
++ jQuery.handleError(s, xhr, status, errMsg);
++ }
++
++ // Fire the complete handlers
++ complete();
++
++ if ( isTimeout === "timeout" ) {
++ xhr.abort();
++ }
++
++ // Stop memory leaks
++ if ( s.async ) {
++ xhr = null;
++ }
++ }
++ };
++
++ // Override the abort handler, if we can (IE doesn't allow it, but that's OK)
++ // Opera doesn't fire onreadystatechange at all on abort
++ try {
++ var oldAbort = xhr.abort;
++ xhr.abort = function() {
++ if ( xhr ) {
++ oldAbort.call( xhr );
++ }
++
++ onreadystatechange( "abort" );
++ };
++ } catch(e) { }
++
++ // Timeout checker
++ if ( s.async && s.timeout > 0 ) {
++ setTimeout(function() {
++ // Check to see if the request is still happening
++ if ( xhr && !requestDone ) {
++ onreadystatechange( "timeout" );
++ }
++ }, s.timeout);
++ }
++
++ // Send the data
++ try {
++ xhr.send( type === "POST" || type === "PUT" || type === "DELETE" ? s.data : null );
++ } catch(e) {
++ jQuery.handleError(s, xhr, null, e);
++ // Fire the complete handlers
++ complete();
++ }
++
++ // firefox 1.5 doesn't fire statechange for sync requests
++ if ( !s.async ) {
++ onreadystatechange();
++ }
++
++ function success() {
++ // If a local callback was specified, fire it and pass it the data
++ if ( s.success ) {
++ s.success.call( callbackContext, data, status, xhr );
++ }
++
++ // Fire the global callback
++ if ( s.global ) {
++ trigger( "ajaxSuccess", [xhr, s] );
++ }
++ }
++
++ function complete() {
++ // Process result
++ if ( s.complete ) {
++ s.complete.call( callbackContext, xhr, status);
++ }
++
++ // The request was completed
++ if ( s.global ) {
++ trigger( "ajaxComplete", [xhr, s] );
++ }
++
++ // Handle the global AJAX counter
++ if ( s.global && ! --jQuery.active ) {
++ jQuery.event.trigger( "ajaxStop" );
++ }
++ }
++
++ function trigger(type, args) {
++ (s.context ? jQuery(s.context) : jQuery.event).trigger(type, args);
++ }
++
++ // return XMLHttpRequest to allow aborting the request etc.
++ return xhr;
++ },
++
++ handleError: function( s, xhr, status, e ) {
++ // If a local callback was specified, fire it
++ if ( s.error ) {
++ s.error.call( s.context || s, xhr, status, e );
++ }
++
++ // Fire the global callback
++ if ( s.global ) {
++ (s.context ? jQuery(s.context) : jQuery.event).trigger( "ajaxError", [xhr, s, e] );
++ }
++ },
++
++ // Counter for holding the number of active queries
++ active: 0,
++
++ // Determines if an XMLHttpRequest was successful or not
++ httpSuccess: function( xhr ) {
++ try {
++ // IE error sometimes returns 1223 when it should be 204 so treat it as success, see #1450
++ return !xhr.status && location.protocol === "file:" ||
++ // Opera returns 0 when status is 304
++ ( xhr.status >= 200 && xhr.status < 300 ) ||
++ xhr.status === 304 || xhr.status === 1223 || xhr.status === 0;
++ } catch(e) {}
++
++ return false;
++ },
++
++ // Determines if an XMLHttpRequest returns NotModified
++ httpNotModified: function( xhr, url ) {
++ var lastModified = xhr.getResponseHeader("Last-Modified"),
++ etag = xhr.getResponseHeader("Etag");
++
++ if ( lastModified ) {
++ jQuery.lastModified[url] = lastModified;
++ }
++
++ if ( etag ) {
++ jQuery.etag[url] = etag;
++ }
++
++ // Opera returns 0 when status is 304
++ return xhr.status === 304 || xhr.status === 0;
++ },
++
++ httpData: function( xhr, type, s ) {
++ var ct = xhr.getResponseHeader("content-type") || "",
++ xml = type === "xml" || !type && ct.indexOf("xml") >= 0,
++ data = xml ? xhr.responseXML : xhr.responseText;
++
++ if ( xml && data.documentElement.nodeName === "parsererror" ) {
++ jQuery.error( "parsererror" );
++ }
++
++ // Allow a pre-filtering function to sanitize the response
++ // s is checked to keep backwards compatibility
++ if ( s && s.dataFilter ) {
++ data = s.dataFilter( data, type );
++ }
++
++ // The filter can actually parse the response
++ if ( typeof data === "string" ) {
++ // Get the JavaScript object, if JSON is used.
++ if ( type === "json" || !type && ct.indexOf("json") >= 0 ) {
++ data = jQuery.parseJSON( data );
++
++ // If the type is "script", eval it in global context
++ } else if ( type === "script" || !type && ct.indexOf("javascript") >= 0 ) {
++ jQuery.globalEval( data );
++ }
++ }
++
++ return data;
++ },
++
++ // Serialize an array of form elements or a set of
++ // key/values into a query string
++ param: function( a, traditional ) {
++ var s = [];
++
++ // Set traditional to true for jQuery <= 1.3.2 behavior.
++ if ( traditional === undefined ) {
++ traditional = jQuery.ajaxSettings.traditional;
++ }
++
++ // If an array was passed in, assume that it is an array of form elements.
++ if ( jQuery.isArray(a) || a.jquery ) {
++ // Serialize the form elements
++ jQuery.each( a, function() {
++ add( this.name, this.value );
++ });
++
++ } else {
++ // If traditional, encode the "old" way (the way 1.3.2 or older
++ // did it), otherwise encode params recursively.
++ for ( var prefix in a ) {
++ buildParams( prefix, a[prefix] );
++ }
++ }
++
++ // Return the resulting serialization
++ return s.join("&").replace(r20, "+");
++
++ function buildParams( prefix, obj ) {
++ if ( jQuery.isArray(obj) ) {
++ // Serialize array item.
++ jQuery.each( obj, function( i, v ) {
++ if ( traditional || /\[\]$/.test( prefix ) ) {
++ // Treat each array item as a scalar.
++ add( prefix, v );
++ } else {
++ // If array item is non-scalar (array or object), encode its
++ // numeric index to resolve deserialization ambiguity issues.
++ // Note that rack (as of 1.0.0) can't currently deserialize
++ // nested arrays properly, and attempting to do so may cause
++ // a server error. Possible fixes are to modify rack's
++ // deserialization algorithm or to provide an option or flag
++ // to force array serialization to be shallow.
++ buildParams( prefix + "[" + ( typeof v === "object" || jQuery.isArray(v) ? i : "" ) + "]", v );
++ }
++ });
++
++ } else if ( !traditional && obj != null && typeof obj === "object" ) {
++ // Serialize object item.
++ jQuery.each( obj, function( k, v ) {
++ buildParams( prefix + "[" + k + "]", v );
++ });
++
++ } else {
++ // Serialize scalar item.
++ add( prefix, obj );
++ }
++ }
++
++ function add( key, value ) {
++ // If value is a function, invoke it and return its value
++ value = jQuery.isFunction(value) ? value() : value;
++ s[ s.length ] = encodeURIComponent(key) + "=" + encodeURIComponent(value);
++ }
++ }
++});
++var elemdisplay = {},
++ rfxtypes = /toggle|show|hide/,
++ rfxnum = /^([+-]=)?([\d+-.]+)(.*)$/,
++ timerId,
++ fxAttrs = [
++ // height animations
++ [ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
++ // width animations
++ [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
++ // opacity animations
++ [ "opacity" ]
++ ];
++
++jQuery.fn.extend({
++ show: function( speed, callback ) {
++ if ( speed || speed === 0) {
++ return this.animate( genFx("show", 3), speed, callback);
++
++ } else {
++ for ( var i = 0, l = this.length; i < l; i++ ) {
++ var old = jQuery.data(this[i], "olddisplay");
++
++ this[i].style.display = old || "";
++
++ if ( jQuery.css(this[i], "display") === "none" ) {
++ var nodeName = this[i].nodeName, display;
++
++ if ( elemdisplay[ nodeName ] ) {
++ display = elemdisplay[ nodeName ];
++
++ } else {
++ var elem = jQuery("<" + nodeName + " />").appendTo("body");
++
++ display = elem.css("display");
++
++ if ( display === "none" ) {
++ display = "block";
++ }
++
++ elem.remove();
++
++ elemdisplay[ nodeName ] = display;
++ }
++
++ jQuery.data(this[i], "olddisplay", display);
++ }
++ }
++
++ // Set the display of the elements in a second loop
++ // to avoid the constant reflow
++ for ( var j = 0, k = this.length; j < k; j++ ) {
++ this[j].style.display = jQuery.data(this[j], "olddisplay") || "";
++ }
++
++ return this;
++ }
++ },
++
++ hide: function( speed, callback ) {
++ if ( speed || speed === 0 ) {
++ return this.animate( genFx("hide", 3), speed, callback);
++
++ } else {
++ for ( var i = 0, l = this.length; i < l; i++ ) {
++ var old = jQuery.data(this[i], "olddisplay");
++ if ( !old && old !== "none" ) {
++ jQuery.data(this[i], "olddisplay", jQuery.css(this[i], "display"));
++ }
++ }
++
++ // Set the display of the elements in a second loop
++ // to avoid the constant reflow
++ for ( var j = 0, k = this.length; j < k; j++ ) {
++ this[j].style.display = "none";
++ }
++
++ return this;
++ }
++ },
++
++ // Save the old toggle function
++ _toggle: jQuery.fn.toggle,
++
++ toggle: function( fn, fn2 ) {
++ var bool = typeof fn === "boolean";
++
++ if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) {
++ this._toggle.apply( this, arguments );
++
++ } else if ( fn == null || bool ) {
++ this.each(function() {
++ var state = bool ? fn : jQuery(this).is(":hidden");
++ jQuery(this)[ state ? "show" : "hide" ]();
++ });
++
++ } else {
++ this.animate(genFx("toggle", 3), fn, fn2);
++ }
++
++ return this;
++ },
++
++ fadeTo: function( speed, to, callback ) {
++ return this.filter(":hidden").css("opacity", 0).show().end()
++ .animate({opacity: to}, speed, callback);
++ },
++
++ animate: function( prop, speed, easing, callback ) {
++ var optall = jQuery.speed(speed, easing, callback);
++
++ if ( jQuery.isEmptyObject( prop ) ) {
++ return this.each( optall.complete );
++ }
++
++ return this[ optall.queue === false ? "each" : "queue" ](function() {
++ var opt = jQuery.extend({}, optall), p,
++ hidden = this.nodeType === 1 && jQuery(this).is(":hidden"),
++ self = this;
++
++ for ( p in prop ) {
++ var name = p.replace(rdashAlpha, fcamelCase);
++
++ if ( p !== name ) {
++ prop[ name ] = prop[ p ];
++ delete prop[ p ];
++ p = name;
++ }
++
++ if ( prop[p] === "hide" && hidden || prop[p] === "show" && !hidden ) {
++ return opt.complete.call(this);
++ }
++
++ if ( ( p === "height" || p === "width" ) && this.style ) {
++ // Store display property
++ opt.display = jQuery.css(this, "display");
++
++ // Make sure that nothing sneaks out
++ opt.overflow = this.style.overflow;
++ }
++
++ if ( jQuery.isArray( prop[p] ) ) {
++ // Create (if needed) and add to specialEasing
++ (opt.specialEasing = opt.specialEasing || {})[p] = prop[p][1];
++ prop[p] = prop[p][0];
++ }
++ }
++
++ if ( opt.overflow != null ) {
++ this.style.overflow = "hidden";
++ }
++
++ opt.curAnim = jQuery.extend({}, prop);
++
++ jQuery.each( prop, function( name, val ) {
++ var e = new jQuery.fx( self, opt, name );
++
++ if ( rfxtypes.test(val) ) {
++ e[ val === "toggle" ? hidden ? "show" : "hide" : val ]( prop );
++
++ } else {
++ var parts = rfxnum.exec(val),
++ start = e.cur(true) || 0;
++
++ if ( parts ) {
++ var end = parseFloat( parts[2] ),
++ unit = parts[3] || "px";
++
++ // We need to compute starting value
++ if ( unit !== "px" ) {
++ self.style[ name ] = (end || 1) + unit;
++ start = ((end || 1) / e.cur(true)) * start;
++ self.style[ name ] = start + unit;
++ }
++
++ // If a +=/-= token was provided, we're doing a relative animation
++ if ( parts[1] ) {
++ end = ((parts[1] === "-=" ? -1 : 1) * end) + start;
++ }
++
++ e.custom( start, end, unit );
++
++ } else {
++ e.custom( start, val, "" );
++ }
++ }
++ });
++
++ // For JS strict compliance
++ return true;
++ });
++ },
++
++ stop: function( clearQueue, gotoEnd ) {
++ var timers = jQuery.timers;
++
++ if ( clearQueue ) {
++ this.queue([]);
++ }
++
++ this.each(function() {
++ // go in reverse order so anything added to the queue during the loop is ignored
++ for ( var i = timers.length - 1; i >= 0; i-- ) {
++ if ( timers[i].elem === this ) {
++ if (gotoEnd) {
++ // force the next step to be the last
++ timers[i](true);
++ }
++
++ timers.splice(i, 1);
++ }
++ }
++ });
++
++ // start the next in the queue if the last step wasn't forced
++ if ( !gotoEnd ) {
++ this.dequeue();
++ }
++
++ return this;
++ }
++
++});
++
++// Generate shortcuts for custom animations
++jQuery.each({
++ slideDown: genFx("show", 1),
++ slideUp: genFx("hide", 1),
++ slideToggle: genFx("toggle", 1),
++ fadeIn: { opacity: "show" },
++ fadeOut: { opacity: "hide" }
++}, function( name, props ) {
++ jQuery.fn[ name ] = function( speed, callback ) {
++ return this.animate( props, speed, callback );
++ };
++});
++
++jQuery.extend({
++ speed: function( speed, easing, fn ) {
++ var opt = speed && typeof speed === "object" ? speed : {
++ complete: fn || !fn && easing ||
++ jQuery.isFunction( speed ) && speed,
++ duration: speed,
++ easing: fn && easing || easing && !jQuery.isFunction(easing) && easing
++ };
++
++ opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
++ jQuery.fx.speeds[opt.duration] || jQuery.fx.speeds._default;
++
++ // Queueing
++ opt.old = opt.complete;
++ opt.complete = function() {
++ if ( opt.queue !== false ) {
++ jQuery(this).dequeue();
++ }
++ if ( jQuery.isFunction( opt.old ) ) {
++ opt.old.call( this );
++ }
++ };
++
++ return opt;
++ },
++
++ easing: {
++ linear: function( p, n, firstNum, diff ) {
++ return firstNum + diff * p;
++ },
++ swing: function( p, n, firstNum, diff ) {
++ return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
++ }
++ },
++
++ timers: [],
++
++ fx: function( elem, options, prop ) {
++ this.options = options;
++ this.elem = elem;
++ this.prop = prop;
++
++ if ( !options.orig ) {
++ options.orig = {};
++ }
++ }
++
++});
++
++jQuery.fx.prototype = {
++ // Simple function for setting a style value
++ update: function() {
++ if ( this.options.step ) {
++ this.options.step.call( this.elem, this.now, this );
++ }
++
++ (jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );
++
++ // Set display property to block for height/width animations
++ if ( ( this.prop === "height" || this.prop === "width" ) && this.elem.style ) {
++ this.elem.style.display = "block";
++ }
++ },
++
++ // Get the current size
++ cur: function( force ) {
++ if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) ) {
++ return this.elem[ this.prop ];
++ }
++
++ var r = parseFloat(jQuery.css(this.elem, this.prop, force));
++ return r && r > -10000 ? r : parseFloat(jQuery.curCSS(this.elem, this.prop)) || 0;
++ },
++
++ // Start an animation from one number to another
++ custom: function( from, to, unit ) {
++ this.startTime = now();
++ this.start = from;
++ this.end = to;
++ this.unit = unit || this.unit || "px";
++ this.now = this.start;
++ this.pos = this.state = 0;
++
++ var self = this;
++ function t( gotoEnd ) {
++ return self.step(gotoEnd);
++ }
++
++ t.elem = this.elem;
++
++ if ( t() && jQuery.timers.push(t) && !timerId ) {
++ timerId = setInterval(jQuery.fx.tick, 13);
++ }
++ },
++
++ // Simple 'show' function
++ show: function() {
++ // Remember where we started, so that we can go back to it later
++ this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
++ this.options.show = true;
++
++ // Begin the animation
++ // Make sure that we start at a small width/height to avoid any
++ // flash of content
++ this.custom(this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur());
++
++ // Start by showing the element
++ jQuery( this.elem ).show();
++ },
++
++ // Simple 'hide' function
++ hide: function() {
++ // Remember where we started, so that we can go back to it later
++ this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
++ this.options.hide = true;
++
++ // Begin the animation
++ this.custom(this.cur(), 0);
++ },
++
++ // Each step of an animation
++ step: function( gotoEnd ) {
++ var t = now(), done = true;
++
++ if ( gotoEnd || t >= this.options.duration + this.startTime ) {
++ this.now = this.end;
++ this.pos = this.state = 1;
++ this.update();
++
++ this.options.curAnim[ this.prop ] = true;
++
++ for ( var i in this.options.curAnim ) {
++ if ( this.options.curAnim[i] !== true ) {
++ done = false;
++ }
++ }
++
++ if ( done ) {
++ if ( this.options.display != null ) {
++ // Reset the overflow
++ this.elem.style.overflow = this.options.overflow;
++
++ // Reset the display
++ var old = jQuery.data(this.elem, "olddisplay");
++ this.elem.style.display = old ? old : this.options.display;
++
++ if ( jQuery.css(this.elem, "display") === "none" ) {
++ this.elem.style.display = "block";
++ }
++ }
++
++ // Hide the element if the "hide" operation was done
++ if ( this.options.hide ) {
++ jQuery(this.elem).hide();
++ }
++
++ // Reset the properties, if the item has been hidden or shown
++ if ( this.options.hide || this.options.show ) {
++ for ( var p in this.options.curAnim ) {
++ jQuery.style(this.elem, p, this.options.orig[p]);
++ }
++ }
++
++ // Execute the complete function
++ this.options.complete.call( this.elem );
++ }
++
++ return false;
++
++ } else {
++ var n = t - this.startTime;
++ this.state = n / this.options.duration;
++
++ // Perform the easing function, defaults to swing
++ var specialEasing = this.options.specialEasing && this.options.specialEasing[this.prop];
++ var defaultEasing = this.options.easing || (jQuery.easing.swing ? "swing" : "linear");
++ this.pos = jQuery.easing[specialEasing || defaultEasing](this.state, n, 0, 1, this.options.duration);
++ this.now = this.start + ((this.end - this.start) * this.pos);
++
++ // Perform the next step of the animation
++ this.update();
++ }
++
++ return true;
++ }
++};
++
++jQuery.extend( jQuery.fx, {
++ tick: function() {
++ var timers = jQuery.timers;
++
++ for ( var i = 0; i < timers.length; i++ ) {
++ if ( !timers[i]() ) {
++ timers.splice(i--, 1);
++ }
++ }
++
++ if ( !timers.length ) {
++ jQuery.fx.stop();
++ }
++ },
++
++ stop: function() {
++ clearInterval( timerId );
++ timerId = null;
++ },
++
++ speeds: {
++ slow: 600,
++ fast: 200,
++ // Default speed
++ _default: 400
++ },
++
++ step: {
++ opacity: function( fx ) {
++ jQuery.style(fx.elem, "opacity", fx.now);
++ },
++
++ _default: function( fx ) {
++ if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) {
++ fx.elem.style[ fx.prop ] = (fx.prop === "width" || fx.prop === "height" ? Math.max(0, fx.now) : fx.now) + fx.unit;
++ } else {
++ fx.elem[ fx.prop ] = fx.now;
++ }
++ }
++ }
++});
++
++if ( jQuery.expr && jQuery.expr.filters ) {
++ jQuery.expr.filters.animated = function( elem ) {
++ return jQuery.grep(jQuery.timers, function( fn ) {
++ return elem === fn.elem;
++ }).length;
++ };
++}
++
++function genFx( type, num ) {
++ var obj = {};
++
++ jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function() {
++ obj[ this ] = type;
++ });
++
++ return obj;
++}
++if ( "getBoundingClientRect" in document.documentElement ) {
++ jQuery.fn.offset = function( options ) {
++ var elem = this[0];
++
++ if ( options ) {
++ return this.each(function( i ) {
++ jQuery.offset.setOffset( this, options, i );
++ });
++ }
++
++ if ( !elem || !elem.ownerDocument ) {
++ return null;
++ }
++
++ if ( elem === elem.ownerDocument.body ) {
++ return jQuery.offset.bodyOffset( elem );
++ }
++
++ var box = elem.getBoundingClientRect(), doc = elem.ownerDocument, body = doc.body, docElem = doc.documentElement,
++ clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0,
++ top = box.top + (self.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop ) - clientTop,
++ left = box.left + (self.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft) - clientLeft;
++
++ return { top: top, left: left };
++ };
++
++} else {
++ jQuery.fn.offset = function( options ) {
++ var elem = this[0];
++
++ if ( options ) {
++ return this.each(function( i ) {
++ jQuery.offset.setOffset( this, options, i );
++ });
++ }
++
++ if ( !elem || !elem.ownerDocument ) {
++ return null;
++ }
++
++ if ( elem === elem.ownerDocument.body ) {
++ return jQuery.offset.bodyOffset( elem );
++ }
++
++ jQuery.offset.initialize();
++
++ var offsetParent = elem.offsetParent, prevOffsetParent = elem,
++ doc = elem.ownerDocument, computedStyle, docElem = doc.documentElement,
++ body = doc.body, defaultView = doc.defaultView,
++ prevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) : elem.currentStyle,
++ top = elem.offsetTop, left = elem.offsetLeft;
++
++ while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
++ if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
++ break;
++ }
++
++ computedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle;
++ top -= elem.scrollTop;
++ left -= elem.scrollLeft;
++
++ if ( elem === offsetParent ) {
++ top += elem.offsetTop;
++ left += elem.offsetLeft;
++
++ if ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && /^t(able|d|h)$/i.test(elem.nodeName)) ) {
++ top += parseFloat( computedStyle.borderTopWidth ) || 0;
++ left += parseFloat( computedStyle.borderLeftWidth ) || 0;
++ }
++
++ prevOffsetParent = offsetParent, offsetParent = elem.offsetParent;
++ }
++
++ if ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) {
++ top += parseFloat( computedStyle.borderTopWidth ) || 0;
++ left += parseFloat( computedStyle.borderLeftWidth ) || 0;
++ }
++
++ prevComputedStyle = computedStyle;
++ }
++
++ if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) {
++ top += body.offsetTop;
++ left += body.offsetLeft;
++ }
++
++ if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
++ top += Math.max( docElem.scrollTop, body.scrollTop );
++ left += Math.max( docElem.scrollLeft, body.scrollLeft );
++ }
++
++ return { top: top, left: left };
++ };
++}
++
++jQuery.offset = {
++ initialize: function() {
++ var body = document.body, container = document.createElement("div"), innerDiv, checkDiv, table, td, bodyMarginTop = parseFloat( jQuery.curCSS(body, "marginTop", true) ) || 0,
++ html = "<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
++
++ jQuery.extend( container.style, { position: "absolute", top: 0, left: 0, margin: 0, border: 0, width: "1px", height: "1px", visibility: "hidden" } );
++
++ container.innerHTML = html;
++ body.insertBefore( container, body.firstChild );
++ innerDiv = container.firstChild;
++ checkDiv = innerDiv.firstChild;
++ td = innerDiv.nextSibling.firstChild.firstChild;
++
++ this.doesNotAddBorder = (checkDiv.offsetTop !== 5);
++ this.doesAddBorderForTableAndCells = (td.offsetTop === 5);
++
++ checkDiv.style.position = "fixed", checkDiv.style.top = "20px";
++ // safari subtracts parent border width here which is 5px
++ this.supportsFixedPosition = (checkDiv.offsetTop === 20 || checkDiv.offsetTop === 15);
++ checkDiv.style.position = checkDiv.style.top = "";
++
++ innerDiv.style.overflow = "hidden", innerDiv.style.position = "relative";
++ this.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);
++
++ this.doesNotIncludeMarginInBodyOffset = (body.offsetTop !== bodyMarginTop);
++
++ body.removeChild( container );
++ body = container = innerDiv = checkDiv = table = td = null;
++ jQuery.offset.initialize = jQuery.noop;
++ },
++
++ bodyOffset: function( body ) {
++ var top = body.offsetTop, left = body.offsetLeft;
++
++ jQuery.offset.initialize();
++
++ if ( jQuery.offset.doesNotIncludeMarginInBodyOffset ) {
++ top += parseFloat( jQuery.curCSS(body, "marginTop", true) ) || 0;
++ left += parseFloat( jQuery.curCSS(body, "marginLeft", true) ) || 0;
++ }
++
++ return { top: top, left: left };
++ },
++
++ setOffset: function( elem, options, i ) {
++ // set position first, in-case top/left are set even on static elem
++ if ( /static/.test( jQuery.curCSS( elem, "position" ) ) ) {
++ elem.style.position = "relative";
++ }
++ var curElem = jQuery( elem ),
++ curOffset = curElem.offset(),
++ curTop = parseInt( jQuery.curCSS( elem, "top", true ), 10 ) || 0,
++ curLeft = parseInt( jQuery.curCSS( elem, "left", true ), 10 ) || 0;
++
++ if ( jQuery.isFunction( options ) ) {
++ options = options.call( elem, i, curOffset );
++ }
++
++ var props = {
++ top: (options.top - curOffset.top) + curTop,
++ left: (options.left - curOffset.left) + curLeft
++ };
++
++ if ( "using" in options ) {
++ options.using.call( elem, props );
++ } else {
++ curElem.css( props );
++ }
++ }
++};
++
++
++jQuery.fn.extend({
++ position: function() {
++ if ( !this[0] ) {
++ return null;
++ }
++
++ var elem = this[0],
++
++ // Get *real* offsetParent
++ offsetParent = this.offsetParent(),
++
++ // Get correct offsets
++ offset = this.offset(),
++ parentOffset = /^body|html$/i.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset();
++
++ // Subtract element margins
++ // note: when an element has margin: auto the offsetLeft and marginLeft
++ // are the same in Safari causing offset.left to incorrectly be 0
++ offset.top -= parseFloat( jQuery.curCSS(elem, "marginTop", true) ) || 0;
++ offset.left -= parseFloat( jQuery.curCSS(elem, "marginLeft", true) ) || 0;
++
++ // Add offsetParent borders
++ parentOffset.top += parseFloat( jQuery.curCSS(offsetParent[0], "borderTopWidth", true) ) || 0;
++ parentOffset.left += parseFloat( jQuery.curCSS(offsetParent[0], "borderLeftWidth", true) ) || 0;
++
++ // Subtract the two offsets
++ return {
++ top: offset.top - parentOffset.top,
++ left: offset.left - parentOffset.left
++ };
++ },
++
++ offsetParent: function() {
++ return this.map(function() {
++ var offsetParent = this.offsetParent || document.body;
++ while ( offsetParent && (!/^body|html$/i.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) {
++ offsetParent = offsetParent.offsetParent;
++ }
++ return offsetParent;
++ });
++ }
++});
++
++
++// Create scrollLeft and scrollTop methods
++jQuery.each( ["Left", "Top"], function( i, name ) {
++ var method = "scroll" + name;
++
++ jQuery.fn[ method ] = function(val) {
++ var elem = this[0], win;
++
++ if ( !elem ) {
++ return null;
++ }
++
++ if ( val !== undefined ) {
++ // Set the scroll offset
++ return this.each(function() {
++ win = getWindow( this );
++
++ if ( win ) {
++ win.scrollTo(
++ !i ? val : jQuery(win).scrollLeft(),
++ i ? val : jQuery(win).scrollTop()
++ );
++
++ } else {
++ this[ method ] = val;
++ }
++ });
++ } else {
++ win = getWindow( elem );
++
++ // Return the scroll offset
++ return win ? ("pageXOffset" in win) ? win[ i ? "pageYOffset" : "pageXOffset" ] :
++ jQuery.support.boxModel && win.document.documentElement[ method ] ||
++ win.document.body[ method ] :
++ elem[ method ];
++ }
++ };
++});
++
++function getWindow( elem ) {
++ return ("scrollTo" in elem && elem.document) ?
++ elem :
++ elem.nodeType === 9 ?
++ elem.defaultView || elem.parentWindow :
++ false;
++}
++// Create innerHeight, innerWidth, outerHeight and outerWidth methods
++jQuery.each([ "Height", "Width" ], function( i, name ) {
++
++ var type = name.toLowerCase();
++
++ // innerHeight and innerWidth
++ jQuery.fn["inner" + name] = function() {
++ return this[0] ?
++ jQuery.css( this[0], type, false, "padding" ) :
++ null;
++ };
++
++ // outerHeight and outerWidth
++ jQuery.fn["outer" + name] = function( margin ) {
++ return this[0] ?
++ jQuery.css( this[0], type, false, margin ? "margin" : "border" ) :
++ null;
++ };
++
++ jQuery.fn[ type ] = function( size ) {
++ // Get window width or height
++ var elem = this[0];
++ if ( !elem ) {
++ return size == null ? null : this;
++ }
++
++ if ( jQuery.isFunction( size ) ) {
++ return this.each(function( i ) {
++ var self = jQuery( this );
++ self[ type ]( size.call( this, i, self[ type ]() ) );
++ });
++ }
++
++ return ("scrollTo" in elem && elem.document) ? // does it walk and quack like a window?
++ // Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
++ elem.document.compatMode === "CSS1Compat" && elem.document.documentElement[ "client" + name ] ||
++ elem.document.body[ "client" + name ] :
++
++ // Get document width or height
++ (elem.nodeType === 9) ? // is it a document
++ // Either scroll[Width/Height] or offset[Width/Height], whichever is greater
++ Math.max(
++ elem.documentElement["client" + name],
++ elem.body["scroll" + name], elem.documentElement["scroll" + name],
++ elem.body["offset" + name], elem.documentElement["offset" + name]
++ ) :
++
++ // Get or set width or height on the element
++ size === undefined ?
++ // Get width or height on the element
++ jQuery.css( elem, type ) :
++
++ // Set the width or height on the element (default to pixels if value is unitless)
++ this.css( type, typeof size === "string" ? size : size + "px" );
++ };
++
++});
++// Expose jQuery to the global object
++window.jQuery = window.$ = jQuery;
++
++})(window);
--- /dev/null
--- /dev/null
++/*!
++ * jQuery JavaScript Library v1.7.1
++ * 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 Nov 21 21:11:03 2011 -0500
++ */
++(function( window, undefined ) {
++
++// Use the correct document accordingly with window argument (sandbox)
++var document = window.document,
++ navigator = window.navigator,
++ location = window.location;
++var jQuery = (function() {
++
++// Define a local copy of jQuery
++var jQuery = function( selector, context ) {
++ // The jQuery object is actually just the init constructor 'enhanced'
++ return new jQuery.fn.init( selector, context, rootjQuery );
++ },
++
++ // Map over jQuery in case of overwrite
++ _jQuery = window.jQuery,
++
++ // Map over the $ in case of overwrite
++ _$ = window.$,
++
++ // A central reference to the root jQuery(document)
++ rootjQuery,
++
++ // A simple way to check for HTML strings or ID strings
++ // Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
++ quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,
++
++ // Check if a string has a non-whitespace character in it
++ rnotwhite = /\S/,
++
++ // Used for trimming whitespace
++ trimLeft = /^\s+/,
++ trimRight = /\s+$/,
++
++ // Match a standalone tag
++ rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,
++
++ // JSON RegExp
++ rvalidchars = /^[\],:{}\s]*$/,
++ rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
++ rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
++ rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
++
++ // Useragent RegExp
++ rwebkit = /(webkit)[ \/]([\w.]+)/,
++ ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/,
++ rmsie = /(msie) ([\w.]+)/,
++ rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/,
++
++ // Matches dashed string for camelizing
++ rdashAlpha = /-([a-z]|[0-9])/ig,
++ rmsPrefix = /^-ms-/,
++
++ // Used by jQuery.camelCase as callback to replace()
++ fcamelCase = function( all, letter ) {
++ return ( letter + "" ).toUpperCase();
++ },
++
++ // Keep a UserAgent string for use with jQuery.browser
++ userAgent = navigator.userAgent,
++
++ // For matching the engine and version of the browser
++ browserMatch,
++
++ // The deferred used on DOM ready
++ readyList,
++
++ // The ready event handler
++ DOMContentLoaded,
++
++ // Save a reference to some core methods
++ toString = Object.prototype.toString,
++ hasOwn = Object.prototype.hasOwnProperty,
++ push = Array.prototype.push,
++ slice = Array.prototype.slice,
++ trim = String.prototype.trim,
++ indexOf = Array.prototype.indexOf,
++
++ // [[Class]] -> type pairs
++ class2type = {};
++
++jQuery.fn = jQuery.prototype = {
++ constructor: jQuery,
++ init: function( selector, context, rootjQuery ) {
++ var match, elem, ret, doc;
++
++ // Handle $(""), $(null), or $(undefined)
++ if ( !selector ) {
++ return this;
++ }
++
++ // Handle $(DOMElement)
++ if ( selector.nodeType ) {
++ this.context = this[0] = selector;
++ this.length = 1;
++ return this;
++ }
++
++ // The body element only exists once, optimize finding it
++ if ( selector === "body" && !context && document.body ) {
++ this.context = document;
++ this[0] = document.body;
++ this.selector = selector;
++ this.length = 1;
++ return this;
++ }
++
++ // Handle HTML strings
++ if ( typeof selector === "string" ) {
++ // Are we dealing with HTML string or an ID?
++ if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
++ // Assume that strings that start and end with <> are HTML and skip the regex check
++ match = [ null, selector, null ];
++
++ } else {
++ match = quickExpr.exec( selector );
++ }
++
++ // Verify a match, and that no context was specified for #id
++ if ( match && (match[1] || !context) ) {
++
++ // HANDLE: $(html) -> $(array)
++ if ( match[1] ) {
++ context = context instanceof jQuery ? context[0] : context;
++ doc = ( context ? context.ownerDocument || context : document );
++
++ // If a single string is passed in and it's a single tag
++ // just do a createElement and skip the rest
++ ret = rsingleTag.exec( selector );
++
++ if ( ret ) {
++ if ( jQuery.isPlainObject( context ) ) {
++ selector = [ document.createElement( ret[1] ) ];
++ jQuery.fn.attr.call( selector, context, true );
++
++ } else {
++ selector = [ doc.createElement( ret[1] ) ];
++ }
++
++ } else {
++ ret = jQuery.buildFragment( [ match[1] ], [ doc ] );
++ selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes;
++ }
++
++ return jQuery.merge( this, selector );
++
++ // HANDLE: $("#id")
++ } else {
++ elem = document.getElementById( match[2] );
++
++ // Check parentNode to catch when Blackberry 4.6 returns
++ // nodes that are no longer in the document #6963
++ if ( elem && elem.parentNode ) {
++ // Handle the case where IE and Opera return items
++ // by name instead of ID
++ if ( elem.id !== match[2] ) {
++ return rootjQuery.find( selector );
++ }
++
++ // Otherwise, we inject the element directly into the jQuery object
++ this.length = 1;
++ this[0] = elem;
++ }
++
++ this.context = document;
++ this.selector = selector;
++ return this;
++ }
++
++ // HANDLE: $(expr, $(...))
++ } else if ( !context || context.jquery ) {
++ return ( context || rootjQuery ).find( selector );
++
++ // HANDLE: $(expr, context)
++ // (which is just equivalent to: $(context).find(expr)
++ } else {
++ return this.constructor( context ).find( selector );
++ }
++
++ // HANDLE: $(function)
++ // Shortcut for document ready
++ } else if ( jQuery.isFunction( selector ) ) {
++ return rootjQuery.ready( selector );
++ }
++
++ if ( selector.selector !== undefined ) {
++ this.selector = selector.selector;
++ this.context = selector.context;
++ }
++
++ return jQuery.makeArray( selector, this );
++ },
++
++ // Start with an empty selector
++ selector: "",
++
++ // The current version of jQuery being used
++ jquery: "1.7.1",
++
++ // The default length of a jQuery object is 0
++ length: 0,
++
++ // The number of elements contained in the matched element set
++ size: function() {
++ return this.length;
++ },
++
++ toArray: function() {
++ return slice.call( this, 0 );
++ },
++
++ // Get the Nth element in the matched element set OR
++ // Get the whole matched element set as a clean array
++ get: function( num ) {
++ return num == null ?
++
++ // Return a 'clean' array
++ this.toArray() :
++
++ // Return just the object
++ ( num < 0 ? this[ this.length + num ] : this[ num ] );
++ },
++
++ // Take an array of elements and push it onto the stack
++ // (returning the new matched element set)
++ pushStack: function( elems, name, selector ) {
++ // Build a new jQuery matched element set
++ var ret = this.constructor();
++
++ if ( jQuery.isArray( elems ) ) {
++ push.apply( ret, elems );
++
++ } else {
++ jQuery.merge( ret, elems );
++ }
++
++ // Add the old object onto the stack (as a reference)
++ ret.prevObject = this;
++
++ ret.context = this.context;
++
++ if ( name === "find" ) {
++ ret.selector = this.selector + ( this.selector ? " " : "" ) + selector;
++ } else if ( name ) {
++ ret.selector = this.selector + "." + name + "(" + selector + ")";
++ }
++
++ // Return the newly-formed element set
++ return ret;
++ },
++
++ // Execute a callback for every element in the matched set.
++ // (You can seed the arguments with an array of args, but this is
++ // only used internally.)
++ each: function( callback, args ) {
++ return jQuery.each( this, callback, args );
++ },
++
++ ready: function( fn ) {
++ // Attach the listeners
++ jQuery.bindReady();
++
++ // Add the callback
++ readyList.add( fn );
++
++ return this;
++ },
++
++ eq: function( i ) {
++ i = +i;
++ return i === -1 ?
++ this.slice( i ) :
++ this.slice( i, i + 1 );
++ },
++
++ first: function() {
++ return this.eq( 0 );
++ },
++
++ last: function() {
++ return this.eq( -1 );
++ },
++
++ slice: function() {
++ return this.pushStack( slice.apply( this, arguments ),
++ "slice", slice.call(arguments).join(",") );
++ },
++
++ map: function( callback ) {
++ return this.pushStack( jQuery.map(this, function( elem, i ) {
++ return callback.call( elem, i, elem );
++ }));
++ },
++
++ end: function() {
++ return this.prevObject || this.constructor(null);
++ },
++
++ // For internal use only.
++ // Behaves like an Array's method, not like a jQuery method.
++ push: push,
++ sort: [].sort,
++ splice: [].splice
++};
++
++// Give the init function the jQuery prototype for later instantiation
++jQuery.fn.init.prototype = jQuery.fn;
++
++jQuery.extend = jQuery.fn.extend = function() {
++ var options, name, src, copy, copyIsArray, clone,
++ target = arguments[0] || {},
++ i = 1,
++ length = arguments.length,
++ deep = false;
++
++ // Handle a deep copy situation
++ if ( typeof target === "boolean" ) {
++ deep = target;
++ target = arguments[1] || {};
++ // skip the boolean and the target
++ i = 2;
++ }
++
++ // Handle case when target is a string or something (possible in deep copy)
++ if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
++ target = {};
++ }
++
++ // extend jQuery itself if only one argument is passed
++ if ( length === i ) {
++ target = this;
++ --i;
++ }
++
++ for ( ; i < length; i++ ) {
++ // Only deal with non-null/undefined values
++ if ( (options = arguments[ i ]) != null ) {
++ // Extend the base object
++ for ( name in options ) {
++ src = target[ name ];
++ copy = options[ name ];
++
++ // Prevent never-ending loop
++ if ( target === copy ) {
++ continue;
++ }
++
++ // Recurse if we're merging plain objects or arrays
++ if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
++ if ( copyIsArray ) {
++ copyIsArray = false;
++ clone = src && jQuery.isArray(src) ? src : [];
++
++ } else {
++ clone = src && jQuery.isPlainObject(src) ? src : {};
++ }
++
++ // Never move original objects, clone them
++ target[ name ] = jQuery.extend( deep, clone, copy );
++
++ // Don't bring in undefined values
++ } else if ( copy !== undefined ) {
++ target[ name ] = copy;
++ }
++ }
++ }
++ }
++
++ // Return the modified object
++ return target;
++};
++
++jQuery.extend({
++ noConflict: function( deep ) {
++ if ( window.$ === jQuery ) {
++ window.$ = _$;
++ }
++
++ if ( deep && window.jQuery === jQuery ) {
++ window.jQuery = _jQuery;
++ }
++
++ return jQuery;
++ },
++
++ // Is the DOM ready to be used? Set to true once it occurs.
++ isReady: false,
++
++ // A counter to track how many items to wait for before
++ // the ready event fires. See #6781
++ readyWait: 1,
++
++ // Hold (or release) the ready event
++ holdReady: function( hold ) {
++ if ( hold ) {
++ jQuery.readyWait++;
++ } else {
++ jQuery.ready( true );
++ }
++ },
++
++ // Handle when the DOM is ready
++ ready: function( wait ) {
++ // Either a released hold or an DOMready/load event and not yet ready
++ if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) {
++ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
++ if ( !document.body ) {
++ return setTimeout( jQuery.ready, 1 );
++ }
++
++ // Remember that the DOM is ready
++ jQuery.isReady = true;
++
++ // If a normal DOM Ready event fired, decrement, and wait if need be
++ if ( wait !== true && --jQuery.readyWait > 0 ) {
++ return;
++ }
++
++ // If there are functions bound, to execute
++ readyList.fireWith( document, [ jQuery ] );
++
++ // Trigger any bound ready events
++ if ( jQuery.fn.trigger ) {
++ jQuery( document ).trigger( "ready" ).off( "ready" );
++ }
++ }
++ },
++
++ bindReady: function() {
++ if ( readyList ) {
++ return;
++ }
++
++ readyList = jQuery.Callbacks( "once memory" );
++
++ // Catch cases where $(document).ready() is called after the
++ // browser event has already occurred.
++ if ( document.readyState === "complete" ) {
++ // Handle it asynchronously to allow scripts the opportunity to delay ready
++ return setTimeout( jQuery.ready, 1 );
++ }
++
++ // Mozilla, Opera and webkit nightlies currently support this event
++ if ( document.addEventListener ) {
++ // Use the handy event callback
++ document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
++
++ // A fallback to window.onload, that will always work
++ window.addEventListener( "load", jQuery.ready, false );
++
++ // If IE event model is used
++ } else if ( document.attachEvent ) {
++ // ensure firing before onload,
++ // maybe late but safe also for iframes
++ document.attachEvent( "onreadystatechange", DOMContentLoaded );
++
++ // A fallback to window.onload, that will always work
++ window.attachEvent( "onload", jQuery.ready );
++
++ // If IE and not a frame
++ // continually check to see if the document is ready
++ var toplevel = false;
++
++ try {
++ toplevel = window.frameElement == null;
++ } catch(e) {}
++
++ if ( document.documentElement.doScroll && toplevel ) {
++ doScrollCheck();
++ }
++ }
++ },
++
++ // See test/unit/core.js for details concerning isFunction.
++ // Since version 1.3, DOM methods and functions like alert
++ // aren't supported. They return false on IE (#2968).
++ isFunction: function( obj ) {
++ return jQuery.type(obj) === "function";
++ },
++
++ isArray: Array.isArray || function( obj ) {
++ return jQuery.type(obj) === "array";
++ },
++
++ // A crude way of determining if an object is a window
++ isWindow: function( obj ) {
++ return obj && typeof obj === "object" && "setInterval" in obj;
++ },
++
++ isNumeric: function( obj ) {
++ return !isNaN( parseFloat(obj) ) && isFinite( obj );
++ },
++
++ type: function( obj ) {
++ return obj == null ?
++ String( obj ) :
++ class2type[ toString.call(obj) ] || "object";
++ },
++
++ isPlainObject: function( obj ) {
++ // Must be an Object.
++ // Because of IE, we also have to check the presence of the constructor property.
++ // Make sure that DOM nodes and window objects don't pass through, as well
++ if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
++ return false;
++ }
++
++ try {
++ // Not own constructor property must be Object
++ if ( obj.constructor &&
++ !hasOwn.call(obj, "constructor") &&
++ !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
++ return false;
++ }
++ } catch ( e ) {
++ // IE8,9 Will throw exceptions on certain host objects #9897
++ return false;
++ }
++
++ // Own properties are enumerated firstly, so to speed up,
++ // if last one is own, then all properties are own.
++
++ var key;
++ for ( key in obj ) {}
++
++ return key === undefined || hasOwn.call( obj, key );
++ },
++
++ isEmptyObject: function( obj ) {
++ for ( var name in obj ) {
++ return false;
++ }
++ return true;
++ },
++
++ error: function( msg ) {
++ throw new Error( msg );
++ },
++
++ parseJSON: function( data ) {
++ if ( typeof data !== "string" || !data ) {
++ return null;
++ }
++
++ // Make sure leading/trailing whitespace is removed (IE can't handle it)
++ data = jQuery.trim( data );
++
++ // Attempt to parse using the native JSON parser first
++ if ( window.JSON && window.JSON.parse ) {
++ return window.JSON.parse( data );
++ }
++
++ // Make sure the incoming data is actual JSON
++ // Logic borrowed from http://json.org/json2.js
++ if ( rvalidchars.test( data.replace( rvalidescape, "@" )
++ .replace( rvalidtokens, "]" )
++ .replace( rvalidbraces, "")) ) {
++
++ return ( new Function( "return " + data ) )();
++
++ }
++ jQuery.error( "Invalid JSON: " + data );
++ },
++
++ // Cross-browser xml parsing
++ parseXML: function( data ) {
++ var xml, tmp;
++ try {
++ if ( window.DOMParser ) { // Standard
++ tmp = new DOMParser();
++ xml = tmp.parseFromString( data , "text/xml" );
++ } else { // IE
++ xml = new ActiveXObject( "Microsoft.XMLDOM" );
++ xml.async = "false";
++ xml.loadXML( data );
++ }
++ } catch( e ) {
++ xml = undefined;
++ }
++ if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) {
++ jQuery.error( "Invalid XML: " + data );
++ }
++ return xml;
++ },
++
++ noop: function() {},
++
++ // Evaluates a script in a global context
++ // Workarounds based on findings by Jim Driscoll
++ // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
++ globalEval: function( data ) {
++ if ( data && rnotwhite.test( data ) ) {
++ // We use execScript on Internet Explorer
++ // We use an anonymous function so that context is window
++ // rather than jQuery in Firefox
++ ( window.execScript || function( data ) {
++ window[ "eval" ].call( window, data );
++ } )( data );
++ }
++ },
++
++ // Convert dashed to camelCase; used by the css and data modules
++ // Microsoft forgot to hump their vendor prefix (#9572)
++ camelCase: function( string ) {
++ return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
++ },
++
++ nodeName: function( elem, name ) {
++ return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
++ },
++
++ // args is for internal usage only
++ each: function( object, callback, args ) {
++ var name, i = 0,
++ length = object.length,
++ isObj = length === undefined || jQuery.isFunction( object );
++
++ if ( args ) {
++ if ( isObj ) {
++ for ( name in object ) {
++ if ( callback.apply( object[ name ], args ) === false ) {
++ break;
++ }
++ }
++ } else {
++ for ( ; i < length; ) {
++ if ( callback.apply( object[ i++ ], args ) === false ) {
++ break;
++ }
++ }
++ }
++
++ // A special, fast, case for the most common use of each
++ } else {
++ if ( isObj ) {
++ for ( name in object ) {
++ if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
++ break;
++ }
++ }
++ } else {
++ for ( ; i < length; ) {
++ if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) {
++ break;
++ }
++ }
++ }
++ }
++
++ return object;
++ },
++
++ // Use native String.trim function wherever possible
++ trim: trim ?
++ function( text ) {
++ return text == null ?
++ "" :
++ trim.call( text );
++ } :
++
++ // Otherwise use our own trimming functionality
++ function( text ) {
++ return text == null ?
++ "" :
++ text.toString().replace( trimLeft, "" ).replace( trimRight, "" );
++ },
++
++ // results is for internal usage only
++ makeArray: function( array, results ) {
++ var ret = results || [];
++
++ if ( array != null ) {
++ // The window, strings (and functions) also have 'length'
++ // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930
++ var type = jQuery.type( array );
++
++ if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) {
++ push.call( ret, array );
++ } else {
++ jQuery.merge( ret, array );
++ }
++ }
++
++ return ret;
++ },
++
++ inArray: function( elem, array, i ) {
++ var len;
++
++ if ( array ) {
++ if ( indexOf ) {
++ return indexOf.call( array, elem, i );
++ }
++
++ len = array.length;
++ i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
++
++ for ( ; i < len; i++ ) {
++ // Skip accessing in sparse arrays
++ if ( i in array && array[ i ] === elem ) {
++ return i;
++ }
++ }
++ }
++
++ return -1;
++ },
++
++ merge: function( first, second ) {
++ var i = first.length,
++ j = 0;
++
++ if ( typeof second.length === "number" ) {
++ for ( var l = second.length; j < l; j++ ) {
++ first[ i++ ] = second[ j ];
++ }
++
++ } else {
++ while ( second[j] !== undefined ) {
++ first[ i++ ] = second[ j++ ];
++ }
++ }
++
++ first.length = i;
++
++ return first;
++ },
++
++ grep: function( elems, callback, inv ) {
++ var ret = [], retVal;
++ inv = !!inv;
++
++ // Go through the array, only saving the items
++ // that pass the validator function
++ for ( var i = 0, length = elems.length; i < length; i++ ) {
++ retVal = !!callback( elems[ i ], i );
++ if ( inv !== retVal ) {
++ ret.push( elems[ i ] );
++ }
++ }
++
++ return ret;
++ },
++
++ // arg is for internal usage only
++ map: function( elems, callback, arg ) {
++ var value, key, ret = [],
++ i = 0,
++ length = elems.length,
++ // jquery objects are treated as arrays
++ isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ;
++
++ // Go through the array, translating each of the items to their
++ if ( isArray ) {
++ for ( ; i < length; i++ ) {
++ value = callback( elems[ i ], i, arg );
++
++ if ( value != null ) {
++ ret[ ret.length ] = value;
++ }
++ }
++
++ // Go through every key on the object,
++ } else {
++ for ( key in elems ) {
++ value = callback( elems[ key ], key, arg );
++
++ if ( value != null ) {
++ ret[ ret.length ] = value;
++ }
++ }
++ }
++
++ // Flatten any nested arrays
++ return ret.concat.apply( [], ret );
++ },
++
++ // A global GUID counter for objects
++ guid: 1,
++
++ // Bind a function to a context, optionally partially applying any
++ // arguments.
++ proxy: function( fn, context ) {
++ if ( typeof context === "string" ) {
++ var tmp = fn[ context ];
++ context = fn;
++ fn = tmp;
++ }
++
++ // Quick check to determine if target is callable, in the spec
++ // this throws a TypeError, but we will just return undefined.
++ if ( !jQuery.isFunction( fn ) ) {
++ return undefined;
++ }
++
++ // Simulated bind
++ var args = slice.call( arguments, 2 ),
++ proxy = function() {
++ return fn.apply( context, args.concat( slice.call( arguments ) ) );
++ };
++
++ // Set the guid of unique handler to the same of original handler, so it can be removed
++ proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
++
++ return proxy;
++ },
++
++ // Mutifunctional method to get and set values to a collection
++ // The value/s can optionally be executed if it's a function
++ access: function( elems, key, value, exec, fn, pass ) {
++ var length = elems.length;
++
++ // Setting many attributes
++ if ( typeof key === "object" ) {
++ for ( var k in key ) {
++ jQuery.access( elems, k, key[k], exec, fn, value );
++ }
++ return elems;
++ }
++
++ // Setting one attribute
++ if ( value !== undefined ) {
++ // Optionally, function values get executed if exec is true
++ exec = !pass && exec && jQuery.isFunction(value);
++
++ for ( var i = 0; i < length; i++ ) {
++ fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
++ }
++
++ return elems;
++ }
++
++ // Getting an attribute
++ return length ? fn( elems[0], key ) : undefined;
++ },
++
++ now: function() {
++ return ( new Date() ).getTime();
++ },
++
++ // Use of jQuery.browser is frowned upon.
++ // More details: http://docs.jquery.com/Utilities/jQuery.browser
++ uaMatch: function( ua ) {
++ ua = ua.toLowerCase();
++
++ var match = rwebkit.exec( ua ) ||
++ ropera.exec( ua ) ||
++ rmsie.exec( ua ) ||
++ ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) ||
++ [];
++
++ return { browser: match[1] || "", version: match[2] || "0" };
++ },
++
++ sub: function() {
++ function jQuerySub( selector, context ) {
++ return new jQuerySub.fn.init( selector, context );
++ }
++ jQuery.extend( true, jQuerySub, this );
++ jQuerySub.superclass = this;
++ jQuerySub.fn = jQuerySub.prototype = this();
++ jQuerySub.fn.constructor = jQuerySub;
++ jQuerySub.sub = this.sub;
++ jQuerySub.fn.init = function init( selector, context ) {
++ if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {
++ context = jQuerySub( context );
++ }
++
++ return jQuery.fn.init.call( this, selector, context, rootjQuerySub );
++ };
++ jQuerySub.fn.init.prototype = jQuerySub.fn;
++ var rootjQuerySub = jQuerySub(document);
++ return jQuerySub;
++ },
++
++ browser: {}
++});
++
++// Populate the class2type map
++jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
++ class2type[ "[object " + name + "]" ] = name.toLowerCase();
++});
++
++browserMatch = jQuery.uaMatch( userAgent );
++if ( browserMatch.browser ) {
++ jQuery.browser[ browserMatch.browser ] = true;
++ jQuery.browser.version = browserMatch.version;
++}
++
++// Deprecated, use jQuery.browser.webkit instead
++if ( jQuery.browser.webkit ) {
++ jQuery.browser.safari = true;
++}
++
++// IE doesn't match non-breaking spaces with \s
++if ( rnotwhite.test( "\xA0" ) ) {
++ trimLeft = /^[\s\xA0]+/;
++ trimRight = /[\s\xA0]+$/;
++}
++
++// All jQuery objects should point back to these
++rootjQuery = jQuery(document);
++
++// Cleanup functions for the document ready method
++if ( document.addEventListener ) {
++ DOMContentLoaded = function() {
++ document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
++ jQuery.ready();
++ };
++
++} else if ( document.attachEvent ) {
++ DOMContentLoaded = function() {
++ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
++ if ( document.readyState === "complete" ) {
++ document.detachEvent( "onreadystatechange", DOMContentLoaded );
++ jQuery.ready();
++ }
++ };
++}
++
++// The DOM ready check for Internet Explorer
++function doScrollCheck() {
++ if ( jQuery.isReady ) {
++ return;
++ }
++
++ try {
++ // If IE is used, use the trick by Diego Perini
++ // http://javascript.nwbox.com/IEContentLoaded/
++ document.documentElement.doScroll("left");
++ } catch(e) {
++ setTimeout( doScrollCheck, 1 );
++ return;
++ }
++
++ // and execute any waiting functions
++ jQuery.ready();
++}
++
++return jQuery;
++
++})();
++
++
++// String to Object flags format cache
++var flagsCache = {};
++
++// Convert String-formatted flags into Object-formatted ones and store in cache
++function createFlags( flags ) {
++ var object = flagsCache[ flags ] = {},
++ i, length;
++ flags = flags.split( /\s+/ );
++ for ( i = 0, length = flags.length; i < length; i++ ) {
++ object[ flags[i] ] = true;
++ }
++ return object;
++}
++
++/*
++ * Create a callback list using the following parameters:
++ *
++ * flags: an optional list of space-separated flags that will change how
++ * the callback list behaves
++ *
++ * By default a callback list will act like an event callback list and can be
++ * "fired" multiple times.
++ *
++ * Possible flags:
++ *
++ * 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( flags ) {
++
++ // Convert flags from String-formatted to Object-formatted
++ // (we check in cache first)
++ flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {};
++
++ var // Actual callback list
++ list = [],
++ // Stack of fire calls for repeatable lists
++ stack = [],
++ // Last fire value (for non-forgettable lists)
++ memory,
++ // Flag to know if list is currently firing
++ firing,
++ // First callback to fire (used internally by add and fireWith)
++ firingStart,
++ // End of the loop when firing
++ firingLength,
++ // Index of currently firing callback (modified by remove if needed)
++ firingIndex,
++ // Add one or several callbacks to the list
++ add = function( args ) {
++ var i,
++ length,
++ elem,
++ type,
++ actual;
++ for ( i = 0, length = args.length; i < length; i++ ) {
++ elem = args[ i ];
++ type = jQuery.type( elem );
++ if ( type === "array" ) {
++ // Inspect recursively
++ add( elem );
++ } else if ( type === "function" ) {
++ // Add if not in unique mode and callback is not in
++ if ( !flags.unique || !self.has( elem ) ) {
++ list.push( elem );
++ }
++ }
++ }
++ },
++ // Fire callbacks
++ fire = function( context, args ) {
++ args = args || [];
++ memory = !flags.memory || [ context, args ];
++ firing = true;
++ firingIndex = firingStart || 0;
++ firingStart = 0;
++ firingLength = list.length;
++ for ( ; list && firingIndex < firingLength; firingIndex++ ) {
++ if ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) {
++ memory = true; // Mark as halted
++ break;
++ }
++ }
++ firing = false;
++ if ( list ) {
++ if ( !flags.once ) {
++ if ( stack && stack.length ) {
++ memory = stack.shift();
++ self.fireWith( memory[ 0 ], memory[ 1 ] );
++ }
++ } else if ( memory === true ) {
++ self.disable();
++ } else {
++ list = [];
++ }
++ }
++ },
++ // Actual Callbacks object
++ self = {
++ // Add a callback or a collection of callbacks to the list
++ add: function() {
++ if ( list ) {
++ var length = list.length;
++ add( 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, unless previous
++ // firing was halted (stopOnFalse)
++ } else if ( memory && memory !== true ) {
++ firingStart = length;
++ fire( memory[ 0 ], memory[ 1 ] );
++ }
++ }
++ return this;
++ },
++ // Remove a callback from the list
++ remove: function() {
++ if ( list ) {
++ var args = arguments,
++ argIndex = 0,
++ argLength = args.length;
++ for ( ; argIndex < argLength ; argIndex++ ) {
++ for ( var i = 0; i < list.length; i++ ) {
++ if ( args[ argIndex ] === list[ i ] ) {
++ // Handle firingIndex and firingLength
++ if ( firing ) {
++ if ( i <= firingLength ) {
++ firingLength--;
++ if ( i <= firingIndex ) {
++ firingIndex--;
++ }
++ }
++ }
++ // Remove the element
++ list.splice( i--, 1 );
++ // If we have some unicity property then
++ // we only need to do this once
++ if ( flags.unique ) {
++ break;
++ }
++ }
++ }
++ }
++ }
++ return this;
++ },
++ // Control if a given callback is in the list
++ has: function( fn ) {
++ if ( list ) {
++ var i = 0,
++ length = list.length;
++ for ( ; i < length; i++ ) {
++ if ( fn === list[ i ] ) {
++ return true;
++ }
++ }
++ }
++ return false;
++ },
++ // Remove all callbacks from the list
++ empty: function() {
++ list = [];
++ 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 || memory === true ) {
++ self.disable();
++ }
++ return this;
++ },
++ // Is it locked?
++ locked: function() {
++ return !stack;
++ },
++ // Call all callbacks with the given context and arguments
++ fireWith: function( context, args ) {
++ if ( stack ) {
++ if ( firing ) {
++ if ( !flags.once ) {
++ stack.push( [ context, args ] );
++ }
++ } else if ( !( flags.once && memory ) ) {
++ fire( context, 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 !!memory;
++ }
++ };
++
++ return self;
++};
++
++
++
++
++var // Static reference to slice
++ sliceDeferred = [].slice;
++
++jQuery.extend({
++
++ Deferred: function( func ) {
++ var doneList = jQuery.Callbacks( "once memory" ),
++ failList = jQuery.Callbacks( "once memory" ),
++ progressList = jQuery.Callbacks( "memory" ),
++ state = "pending",
++ lists = {
++ resolve: doneList,
++ reject: failList,
++ notify: progressList
++ },
++ promise = {
++ done: doneList.add,
++ fail: failList.add,
++ progress: progressList.add,
++
++ state: function() {
++ return state;
++ },
++
++ // Deprecated
++ isResolved: doneList.fired,
++ isRejected: failList.fired,
++
++ then: function( doneCallbacks, failCallbacks, progressCallbacks ) {
++ deferred.done( doneCallbacks ).fail( failCallbacks ).progress( progressCallbacks );
++ return this;
++ },
++ always: function() {
++ deferred.done.apply( deferred, arguments ).fail.apply( deferred, arguments );
++ return this;
++ },
++ pipe: function( fnDone, fnFail, fnProgress ) {
++ return jQuery.Deferred(function( newDefer ) {
++ jQuery.each( {
++ done: [ fnDone, "resolve" ],
++ fail: [ fnFail, "reject" ],
++ progress: [ fnProgress, "notify" ]
++ }, function( handler, data ) {
++ var fn = data[ 0 ],
++ action = data[ 1 ],
++ returned;
++ if ( jQuery.isFunction( fn ) ) {
++ deferred[ handler ](function() {
++ returned = fn.apply( this, arguments );
++ if ( returned && jQuery.isFunction( returned.promise ) ) {
++ returned.promise().then( newDefer.resolve, newDefer.reject, newDefer.notify );
++ } else {
++ newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] );
++ }
++ });
++ } else {
++ deferred[ handler ]( newDefer[ action ] );
++ }
++ });
++ }).promise();
++ },
++ // Get a promise for this deferred
++ // If obj is provided, the promise aspect is added to the object
++ promise: function( obj ) {
++ if ( obj == null ) {
++ obj = promise;
++ } else {
++ for ( var key in promise ) {
++ obj[ key ] = promise[ key ];
++ }
++ }
++ return obj;
++ }
++ },
++ deferred = promise.promise({}),
++ key;
++
++ for ( key in lists ) {
++ deferred[ key ] = lists[ key ].fire;
++ deferred[ key + "With" ] = lists[ key ].fireWith;
++ }
++
++ // Handle state
++ deferred.done( function() {
++ state = "resolved";
++ }, failList.disable, progressList.lock ).fail( function() {
++ state = "rejected";
++ }, doneList.disable, progressList.lock );
++
++ // Call given func if any
++ if ( func ) {
++ func.call( deferred, deferred );
++ }
++
++ // All done!
++ return deferred;
++ },
++
++ // Deferred helper
++ when: function( firstParam ) {
++ var args = sliceDeferred.call( arguments, 0 ),
++ i = 0,
++ length = args.length,
++ pValues = new Array( length ),
++ count = length,
++ pCount = length,
++ deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ?
++ firstParam :
++ jQuery.Deferred(),
++ promise = deferred.promise();
++ function resolveFunc( i ) {
++ return function( value ) {
++ args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
++ if ( !( --count ) ) {
++ deferred.resolveWith( deferred, args );
++ }
++ };
++ }
++ function progressFunc( i ) {
++ return function( value ) {
++ pValues[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
++ deferred.notifyWith( promise, pValues );
++ };
++ }
++ if ( length > 1 ) {
++ for ( ; i < length; i++ ) {
++ if ( args[ i ] && args[ i ].promise && jQuery.isFunction( args[ i ].promise ) ) {
++ args[ i ].promise().then( resolveFunc(i), deferred.reject, progressFunc(i) );
++ } else {
++ --count;
++ }
++ }
++ if ( !count ) {
++ deferred.resolveWith( deferred, args );
++ }
++ } else if ( deferred !== firstParam ) {
++ deferred.resolveWith( deferred, length ? [ firstParam ] : [] );
++ }
++ return promise;
++ }
++});
++
++
++
++
++jQuery.support = (function() {
++
++ var support,
++ all,
++ a,
++ select,
++ opt,
++ input,
++ marginDiv,
++ fragment,
++ tds,
++ events,
++ eventName,
++ i,
++ isSupported,
++ div = document.createElement( "div" ),
++ documentElement = document.documentElement;
++
++ // Preliminary tests
++ div.setAttribute("className", "t");
++ div.innerHTML = " <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
++
++ all = div.getElementsByTagName( "*" );
++ a = div.getElementsByTagName( "a" )[ 0 ];
++
++ // Can't get basic test support
++ if ( !all || !all.length || !a ) {
++ return {};
++ }
++
++ // First batch of supports tests
++ select = document.createElement( "select" );
++ opt = select.appendChild( document.createElement("option") );
++ input = div.getElementsByTagName( "input" )[ 0 ];
++
++ support = {
++ // IE strips leading whitespace when .innerHTML is used
++ leadingWhitespace: ( div.firstChild.nodeType === 3 ),
++
++ // Make sure that tbody elements aren't automatically inserted
++ // IE will insert them into empty tables
++ tbody: !div.getElementsByTagName("tbody").length,
++
++ // Make sure that link elements get serialized correctly by innerHTML
++ // This requires a wrapper element in IE
++ htmlSerialize: !!div.getElementsByTagName("link").length,
++
++ // Get the style information from getAttribute
++ // (IE uses .cssText instead)
++ style: /top/.test( a.getAttribute("style") ),
++
++ // Make sure that URLs aren't manipulated
++ // (IE normalizes it by default)
++ hrefNormalized: ( a.getAttribute("href") === "/a" ),
++
++ // Make sure that element opacity exists
++ // (IE uses filter instead)
++ // Use a regex to work around a WebKit issue. See #5145
++ opacity: /^0.55/.test( a.style.opacity ),
++
++ // Verify style float existence
++ // (IE uses styleFloat instead of cssFloat)
++ cssFloat: !!a.style.cssFloat,
++
++ // Make sure that if no value is specified for a checkbox
++ // that it defaults to "on".
++ // (WebKit defaults to "" instead)
++ checkOn: ( input.value === "on" ),
++
++ // Make sure that a selected-by-default option has a working selected property.
++ // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
++ optSelected: opt.selected,
++
++ // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
++ getSetAttribute: div.className !== "t",
++
++ // Tests for enctype support on a form(#6743)
++ enctype: !!document.createElement("form").enctype,
++
++ // Makes sure cloning an html5 element does not cause problems
++ // Where outerHTML is undefined, this still works
++ html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav></:nav>",
++
++ // Will be defined later
++ submitBubbles: true,
++ changeBubbles: true,
++ focusinBubbles: false,
++ deleteExpando: true,
++ noCloneEvent: true,
++ inlineBlockNeedsLayout: false,
++ shrinkWrapBlocks: false,
++ reliableMarginRight: true
++ };
++
++ // Make sure checked status is properly cloned
++ input.checked = true;
++ support.noCloneChecked = input.cloneNode( true ).checked;
++
++ // Make sure that the options inside disabled selects aren't marked as disabled
++ // (WebKit marks them as disabled)
++ select.disabled = true;
++ support.optDisabled = !opt.disabled;
++
++ // Test to see if it's possible to delete an expando from an element
++ // Fails in Internet Explorer
++ try {
++ delete div.test;
++ } catch( e ) {
++ support.deleteExpando = false;
++ }
++
++ if ( !div.addEventListener && div.attachEvent && div.fireEvent ) {
++ div.attachEvent( "onclick", function() {
++ // Cloning a node shouldn't copy over any
++ // bound event handlers (IE does this)
++ support.noCloneEvent = false;
++ });
++ div.cloneNode( true ).fireEvent( "onclick" );
++ }
++
++ // Check if a radio maintains its value
++ // after being appended to the DOM
++ input = document.createElement("input");
++ input.value = "t";
++ input.setAttribute("type", "radio");
++ support.radioValue = input.value === "t";
++
++ input.setAttribute("checked", "checked");
++ div.appendChild( input );
++ fragment = document.createDocumentFragment();
++ fragment.appendChild( div.lastChild );
++
++ // WebKit doesn't clone checked state correctly in fragments
++ support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
++
++ // Check if a disconnected checkbox will retain its checked
++ // value of true after appended to the DOM (IE6/7)
++ support.appendChecked = input.checked;
++
++ fragment.removeChild( input );
++ fragment.appendChild( div );
++
++ 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 ( window.getComputedStyle ) {
++ marginDiv = document.createElement( "div" );
++ marginDiv.style.width = "0";
++ marginDiv.style.marginRight = "0";
++ div.style.width = "2px";
++ div.appendChild( marginDiv );
++ support.reliableMarginRight =
++ ( parseInt( ( window.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0;
++ }
++
++ // Technique from Juriy Zaytsev
++ // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/
++ // We only care about the case where non-standard event systems
++ // are used, namely in IE. Short-circuiting here helps us to
++ // avoid an eval call (in setAttribute) which can cause CSP
++ // to go haywire. See: https://developer.mozilla.org/en/Security/CSP
++ if ( div.attachEvent ) {
++ for( i in {
++ submit: 1,
++ change: 1,
++ focusin: 1
++ }) {
++ eventName = "on" + i;
++ isSupported = ( eventName in div );
++ if ( !isSupported ) {
++ div.setAttribute( eventName, "return;" );
++ isSupported = ( typeof div[ eventName ] === "function" );
++ }
++ support[ i + "Bubbles" ] = isSupported;
++ }
++ }
++
++ fragment.removeChild( div );
++
++ // Null elements to avoid leaks in IE
++ fragment = select = opt = marginDiv = div = input = null;
++
++ // Run tests that need a body at doc ready
++ jQuery(function() {
++ var container, outer, inner, table, td, offsetSupport,
++ conMarginTop, ptlm, vb, style, html,
++ body = document.getElementsByTagName("body")[0];
++
++ if ( !body ) {
++ // Return for frameset docs that don't have a body
++ return;
++ }
++
++ conMarginTop = 1;
++ ptlm = "position:absolute;top:0;left:0;width:1px;height:1px;margin:0;";
++ vb = "visibility:hidden;border:0;";
++ style = "style='" + ptlm + "border:5px solid #000;padding:0;'";
++ html = "<div " + style + "><div></div></div>" +
++ "<table " + style + " cellpadding='0' cellspacing='0'>" +
++ "<tr><td></td></tr></table>";
++
++ container = document.createElement("div");
++ container.style.cssText = vb + "width:0;height:0;position:static;top:0;margin-top:" + conMarginTop + "px";
++ body.insertBefore( container, body.firstChild );
++
++ // Construct the test element
++ div = document.createElement("div");
++ container.appendChild( div );
++
++ // 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)
++ div.innerHTML = "<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>";
++ tds = div.getElementsByTagName( "td" );
++ 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 );
++
++ // Figure out if the W3C box model works as expected
++ div.innerHTML = "";
++ div.style.width = div.style.paddingLeft = "1px";
++ jQuery.boxModel = support.boxModel = div.offsetWidth === 2;
++
++ if ( typeof div.style.zoom !== "undefined" ) {
++ // Check if natively block-level elements act like inline-block
++ // elements when setting their display to 'inline' and giving
++ // them layout
++ // (IE < 8 does this)
++ div.style.display = "inline";
++ div.style.zoom = 1;
++ support.inlineBlockNeedsLayout = ( div.offsetWidth === 2 );
++
++ // Check if elements with layout shrink-wrap their children
++ // (IE 6 does this)
++ div.style.display = "";
++ div.innerHTML = "<div style='width:4px;'></div>";
++ support.shrinkWrapBlocks = ( div.offsetWidth !== 2 );
++ }
++
++ div.style.cssText = ptlm + vb;
++ div.innerHTML = html;
++
++ outer = div.firstChild;
++ inner = outer.firstChild;
++ td = outer.nextSibling.firstChild.firstChild;
++
++ offsetSupport = {
++ doesNotAddBorder: ( inner.offsetTop !== 5 ),
++ doesAddBorderForTableAndCells: ( td.offsetTop === 5 )
++ };
++
++ inner.style.position = "fixed";
++ inner.style.top = "20px";
++
++ // safari subtracts parent border width here which is 5px
++ offsetSupport.fixedPosition = ( inner.offsetTop === 20 || inner.offsetTop === 15 );
++ inner.style.position = inner.style.top = "";
++
++ outer.style.overflow = "hidden";
++ outer.style.position = "relative";
++
++ offsetSupport.subtractsBorderForOverflowNotVisible = ( inner.offsetTop === -5 );
++ offsetSupport.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== conMarginTop );
++
++ body.removeChild( container );
++ div = container = null;
++
++ jQuery.extend( support, offsetSupport );
++ });
++
++ return support;
++})();
++
++
++
++
++var rbrace = /^(?:\{.*\}|\[.*\])$/,
++ rmultiDash = /([A-Z])/g;
++
++jQuery.extend({
++ cache: {},
++
++ // Please use with caution
++ uuid: 0,
++
++ // Unique for each copy of jQuery on the page
++ // Non-digits removed to match rinlinejQuery
++ expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),
++
++ // The following elements throw uncatchable exceptions if you
++ // attempt to add expando properties to them.
++ noData: {
++ "embed": true,
++ // Ban all objects except for Flash (which handle expandos)
++ "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
++ "applet": true
++ },
++
++ hasData: function( elem ) {
++ elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
++ return !!elem && !isEmptyDataObject( elem );
++ },
++
++ data: function( elem, name, data, pvt /* Internal Use Only */ ) {
++ if ( !jQuery.acceptData( elem ) ) {
++ return;
++ }
++
++ var privateCache, 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[ internalKey ] : elem[ internalKey ] && internalKey,
++ isEvents = name === "events";
++
++ // 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] || (!isEvents && !pvt && !cache[id].data)) && 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[ internalKey ] = id = ++jQuery.uuid;
++ } else {
++ id = internalKey;
++ }
++ }
++
++ if ( !cache[ id ] ) {
++ cache[ id ] = {};
++
++ // 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 ] = jQuery.extend( cache[ id ], name );
++ } else {
++ cache[ id ].data = jQuery.extend( cache[ id ].data, name );
++ }
++ }
++
++ privateCache = 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;
++ }
++
++ // Users should not attempt to inspect the internal events object using jQuery.data,
++ // it is undocumented and subject to change. But does anyone listen? No.
++ if ( isEvents && !thisCache[ name ] ) {
++ return privateCache.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, i, l,
++
++ // 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[ internalKey ] : internalKey;
++
++ // 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( " " );
++ }
++ }
++ }
++
++ for ( i = 0, l = name.length; i < l; 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 : 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;
++ }
++ }
++
++ // 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 cache and need to eliminate the expando on the node to avoid
++ // false lookups in the cache for entries that no longer exist
++ 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[ internalKey ];
++ } else if ( elem.removeAttribute ) {
++ elem.removeAttribute( internalKey );
++ } else {
++ elem[ internalKey ] = null;
++ }
++ }
++ },
++
++ // For internal use only.
++ _data: function( elem, name, data ) {
++ return jQuery.data( elem, name, data, true );
++ },
++
++ // A method for determining if a DOM node can handle the data expando
++ acceptData: function( elem ) {
++ if ( elem.nodeName ) {
++ var match = jQuery.noData[ elem.nodeName.toLowerCase() ];
++
++ if ( match ) {
++ return !(match === true || elem.getAttribute("classid") !== match);
++ }
++ }
++
++ return true;
++ }
++});
++
++jQuery.fn.extend({
++ data: function( key, value ) {
++ var parts, attr, name,
++ data = null;
++
++ if ( typeof key === "undefined" ) {
++ if ( this.length ) {
++ data = jQuery.data( this[0] );
++
++ if ( this[0].nodeType === 1 && !jQuery._data( this[0], "parsedAttrs" ) ) {
++ attr = this[0].attributes;
++ 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 ] );
++ }
++ }
++ jQuery._data( this[0], "parsedAttrs", true );
++ }
++ }
++
++ return data;
++
++ } else if ( typeof key === "object" ) {
++ return this.each(function() {
++ jQuery.data( this, key );
++ });
++ }
++
++ parts = key.split(".");
++ parts[1] = parts[1] ? "." + parts[1] : "";
++
++ if ( value === undefined ) {
++ data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
++
++ // Try to fetch any internally stored data first
++ if ( data === undefined && this.length ) {
++ data = jQuery.data( this[0], key );
++ data = dataAttr( this[0], key, data );
++ }
++
++ return data === undefined && parts[1] ?
++ this.data( parts[0] ) :
++ data;
++
++ } else {
++ return this.each(function() {
++ var self = jQuery( this ),
++ args = [ parts[0], value ];
++
++ self.triggerHandler( "setData" + parts[1] + "!", args );
++ jQuery.data( this, key, value );
++ self.triggerHandler( "changeData" + parts[1] + "!", args );
++ });
++ }
++ },
++
++ removeData: function( key ) {
++ return this.each(function() {
++ jQuery.removeData( this, key );
++ });
++ }
++});
++
++function dataAttr( elem, key, data ) {
++ // If nothing was found internally, try to fetch any
++ // data from the HTML5 data-* attribute
++ if ( data === undefined && elem.nodeType === 1 ) {
++
++ var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
++
++ data = elem.getAttribute( name );
++
++ if ( typeof data === "string" ) {
++ try {
++ data = data === "true" ? true :
++ data === "false" ? false :
++ data === "null" ? null :
++ jQuery.isNumeric( data ) ? parseFloat( data ) :
++ rbrace.test( data ) ? jQuery.parseJSON( data ) :
++ data;
++ } catch( e ) {}
++
++ // Make sure we set the data so it isn't changed later
++ jQuery.data( elem, key, data );
++
++ } else {
++ data = undefined;
++ }
++ }
++
++ return data;
++}
++
++// checks a cache object for emptiness
++function isEmptyDataObject( obj ) {
++ for ( var 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;
++ }
++ }
++
++ return true;
++}
++
++
++
++
++function handleQueueMarkDefer( elem, type, src ) {
++ var deferDataKey = type + "defer",
++ queueDataKey = type + "queue",
++ markDataKey = type + "mark",
++ defer = jQuery._data( elem, deferDataKey );
++ if ( defer &&
++ ( src === "queue" || !jQuery._data(elem, queueDataKey) ) &&
++ ( src === "mark" || !jQuery._data(elem, markDataKey) ) ) {
++ // 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 ) &&
++ !jQuery._data( elem, markDataKey ) ) {
++ jQuery.removeData( elem, deferDataKey, true );
++ defer.fire();
++ }
++ }, 0 );
++ }
++}
++
++jQuery.extend({
++
++ _mark: function( elem, type ) {
++ if ( elem ) {
++ type = ( type || "fx" ) + "mark";
++ jQuery._data( elem, type, (jQuery._data( elem, type ) || 0) + 1 );
++ }
++ },
++
++ _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 ) || 1) - 1 );
++ if ( count ) {
++ jQuery._data( elem, key, count );
++ } else {
++ jQuery.removeData( elem, key, true );
++ handleQueueMarkDefer( elem, type, "mark" );
++ }
++ }
++ },
++
++ queue: function( elem, type, data ) {
++ var q;
++ if ( elem ) {
++ type = ( type || "fx" ) + "queue";
++ q = 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) );
++ } else {
++ q.push( data );
++ }
++ }
++ return q || [];
++ }
++ },
++
++ dequeue: function( elem, type ) {
++ type = type || "fx";
++
++ var queue = jQuery.queue( elem, type ),
++ fn = queue.shift(),
++ hooks = {};
++
++ // If the fx queue is dequeued, always remove the progress sentinel
++ if ( fn === "inprogress" ) {
++ fn = queue.shift();
++ }
++
++ if ( fn ) {
++ // Add a progress sentinel to prevent the fx queue from being
++ // automatically dequeued
++ if ( type === "fx" ) {
++ queue.unshift( "inprogress" );
++ }
++
++ jQuery._data( elem, type + ".run", hooks );
++ fn.call( elem, function() {
++ jQuery.dequeue( elem, type );
++ }, hooks );
++ }
++
++ if ( !queue.length ) {
++ jQuery.removeData( elem, type + "queue " + type + ".run", true );
++ handleQueueMarkDefer( elem, type, "queue" );
++ }
++ }
++});
++
++jQuery.fn.extend({
++ queue: function( type, data ) {
++ if ( typeof type !== "string" ) {
++ data = type;
++ type = "fx";
++ }
++
++ if ( data === undefined ) {
++ return jQuery.queue( this[0], type );
++ }
++ return this.each(function() {
++ var queue = jQuery.queue( this, type, data );
++
++ if ( type === "fx" && queue[0] !== "inprogress" ) {
++ jQuery.dequeue( this, type );
++ }
++ });
++ },
++ dequeue: function( type ) {
++ return this.each(function() {
++ jQuery.dequeue( this, type );
++ });
++ },
++ // Based off of the plugin by Clint Helfers, with permission.
++ // http://blindsignals.com/index.php/2009/07/jquery-delay/
++ delay: function( time, type ) {
++ time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
++ type = type || "fx";
++
++ return this.queue( type, function( next, hooks ) {
++ var timeout = setTimeout( next, time );
++ hooks.stop = function() {
++ clearTimeout( timeout );
++ };
++ });
++ },
++ clearQueue: function( type ) {
++ return this.queue( type || "fx", [] );
++ },
++ // Get a promise resolved when queues of a certain type
++ // are emptied (fx is the type by default)
++ promise: function( type, object ) {
++ if ( typeof type !== "string" ) {
++ object = type;
++ type = undefined;
++ }
++ type = type || "fx";
++ var defer = jQuery.Deferred(),
++ elements = this,
++ i = elements.length,
++ count = 1,
++ deferDataKey = type + "defer",
++ queueDataKey = type + "queue",
++ markDataKey = type + "mark",
++ tmp;
++ function resolve() {
++ if ( !( --count ) ) {
++ defer.resolveWith( elements, [ elements ] );
++ }
++ }
++ while( i-- ) {
++ if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) ||
++ ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) ||
++ jQuery.data( elements[ i ], markDataKey, undefined, true ) ) &&
++ jQuery.data( elements[ i ], deferDataKey, jQuery.Callbacks( "once memory" ), true ) )) {
++ count++;
++ tmp.add( resolve );
++ }
++ }
++ resolve();
++ return defer.promise();
++ }
++});
++
++
++
++
++var rclass = /[\n\t\r]/g,
++ rspace = /\s+/,
++ rreturn = /\r/g,
++ rtype = /^(?:button|input)$/i,
++ rfocusable = /^(?:button|input|object|select|textarea)$/i,
++ rclickable = /^a(?:rea)?$/i,
++ rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
++ getSetAttribute = jQuery.support.getSetAttribute,
++ nodeHook, boolHook, fixSpecified;
++
++jQuery.fn.extend({
++ attr: function( name, value ) {
++ return jQuery.access( this, name, value, true, jQuery.attr );
++ },
++
++ removeAttr: function( name ) {
++ return this.each(function() {
++ jQuery.removeAttr( this, name );
++ });
++ },
++
++ prop: function( name, value ) {
++ return jQuery.access( this, name, value, true, jQuery.prop );
++ },
++
++ removeProp: function( name ) {
++ name = jQuery.propFix[ name ] || name;
++ return this.each(function() {
++ // try/catch handles cases where IE balks (such as removing a property on window)
++ try {
++ this[ name ] = undefined;
++ delete this[ name ];
++ } catch( e ) {}
++ });
++ },
++
++ addClass: function( value ) {
++ var classNames, i, l, elem,
++ setClass, c, cl;
++
++ if ( jQuery.isFunction( value ) ) {
++ return this.each(function( j ) {
++ jQuery( this ).addClass( value.call(this, j, this.className) );
++ });
++ }
++
++ if ( value && typeof value === "string" ) {
++ classNames = value.split( rspace );
++
++ for ( i = 0, l = this.length; i < l; i++ ) {
++ elem = this[ i ];
++
++ if ( elem.nodeType === 1 ) {
++ if ( !elem.className && classNames.length === 1 ) {
++ elem.className = value;
++
++ } else {
++ setClass = " " + elem.className + " ";
++
++ for ( c = 0, cl = classNames.length; c < cl; c++ ) {
++ if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) {
++ setClass += classNames[ c ] + " ";
++ }
++ }
++ elem.className = jQuery.trim( setClass );
++ }
++ }
++ }
++ }
++
++ return this;
++ },
++
++ removeClass: function( value ) {
++ var classNames, i, l, elem, className, c, cl;
++
++ if ( jQuery.isFunction( value ) ) {
++ return this.each(function( j ) {
++ jQuery( this ).removeClass( value.call(this, j, this.className) );
++ });
++ }
++
++ if ( (value && typeof value === "string") || value === undefined ) {
++ classNames = ( value || "" ).split( rspace );
++
++ for ( i = 0, l = this.length; i < l; i++ ) {
++ elem = this[ i ];
++
++ if ( elem.nodeType === 1 && elem.className ) {
++ if ( value ) {
++ className = (" " + elem.className + " ").replace( rclass, " " );
++ for ( c = 0, cl = classNames.length; c < cl; c++ ) {
++ className = className.replace(" " + classNames[ c ] + " ", " ");
++ }
++ elem.className = jQuery.trim( className );
++
++ } else {
++ elem.className = "";
++ }
++ }
++ }
++ }
++
++ return this;
++ },
++
++ toggleClass: function( value, stateVal ) {
++ var type = typeof value,
++ isBool = typeof stateVal === "boolean";
++
++ if ( jQuery.isFunction( value ) ) {
++ return this.each(function( i ) {
++ jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
++ });
++ }
++
++ return this.each(function() {
++ if ( type === "string" ) {
++ // toggle individual class names
++ var className,
++ i = 0,
++ self = jQuery( this ),
++ state = stateVal,
++ classNames = value.split( rspace );
++
++ while ( (className = classNames[ i++ ]) ) {
++ // check each className given, space seperated list
++ state = isBool ? state : !self.hasClass( className );
++ self[ state ? "addClass" : "removeClass" ]( className );
++ }
++
++ } else if ( type === "undefined" || type === "boolean" ) {
++ if ( this.className ) {
++ // store className if set
++ jQuery._data( this, "__className__", this.className );
++ }
++
++ // toggle whole className
++ this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
++ }
++ });
++ },
++
++ hasClass: function( selector ) {
++ var className = " " + selector + " ",
++ i = 0,
++ l = this.length;
++ for ( ; i < l; i++ ) {
++ if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
++ return true;
++ }
++ }
++
++ return false;
++ },
++
++ val: function( value ) {
++ var hooks, ret, isFunction,
++ elem = this[0];
++
++ if ( !arguments.length ) {
++ if ( elem ) {
++ hooks = jQuery.valHooks[ elem.nodeName.toLowerCase() ] || jQuery.valHooks[ elem.type ];
++
++ if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
++ return ret;
++ }
++
++ ret = elem.value;
++
++ return typeof ret === "string" ?
++ // handle most common string cases
++ ret.replace(rreturn, "") :
++ // handle cases where value is null/undef or number
++ ret == null ? "" : ret;
++ }
++
++ return;
++ }
++
++ isFunction = jQuery.isFunction( value );
++
++ return this.each(function( i ) {
++ var self = jQuery(this), val;
++
++ if ( this.nodeType !== 1 ) {
++ return;
++ }
++
++ if ( isFunction ) {
++ val = value.call( this, i, self.val() );
++ } else {
++ val = value;
++ }
++
++ // Treat null/undefined as ""; convert numbers to string
++ if ( val == null ) {
++ val = "";
++ } else if ( typeof val === "number" ) {
++ val += "";
++ } else if ( jQuery.isArray( val ) ) {
++ val = jQuery.map(val, function ( value ) {
++ return value == null ? "" : value + "";
++ });
++ }
++
++ hooks = jQuery.valHooks[ this.nodeName.toLowerCase() ] || jQuery.valHooks[ this.type ];
++
++ // If set returns undefined, fall back to normal setting
++ if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
++ this.value = val;
++ }
++ });
++ }
++});
++
++jQuery.extend({
++ valHooks: {
++ option: {
++ get: function( elem ) {
++ // attributes.value is undefined in Blackberry 4.7 but
++ // uses .value. See #6932
++ var val = elem.attributes.value;
++ return !val || val.specified ? elem.value : elem.text;
++ }
++ },
++ select: {
++ get: function( elem ) {
++ var value, i, max, option,
++ index = elem.selectedIndex,
++ values = [],
++ options = elem.options,
++ one = elem.type === "select-one";
++
++ // Nothing was selected
++ if ( index < 0 ) {
++ return null;
++ }
++
++ // Loop through all the selected options
++ i = one ? index : 0;
++ max = one ? index + 1 : options.length;
++ for ( ; i < max; i++ ) {
++ option = options[ i ];
++
++ // Don't return options that are disabled or in a disabled optgroup
++ if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) &&
++ (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) {
++
++ // Get the specific value for the option
++ value = jQuery( option ).val();
++
++ // We don't need an array for one selects
++ if ( one ) {
++ return value;
++ }
++
++ // Multi-Selects return an array
++ values.push( value );
++ }
++ }
++
++ // Fixes Bug #2551 -- select.val() broken in IE after form.reset()
++ if ( one && !values.length && options.length ) {
++ return jQuery( options[ index ] ).val();
++ }
++
++ return values;
++ },
++
++ set: function( elem, value ) {
++ var values = jQuery.makeArray( value );
++
++ jQuery(elem).find("option").each(function() {
++ this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
++ });
++
++ if ( !values.length ) {
++ elem.selectedIndex = -1;
++ }
++ return values;
++ }
++ }
++ },
++
++ attrFn: {
++ val: true,
++ css: true,
++ html: true,
++ text: true,
++ data: true,
++ width: true,
++ height: true,
++ offset: true
++ },
++
++ attr: function( elem, name, value, pass ) {
++ var ret, hooks, notxml,
++ nType = elem.nodeType;
++
++ // don't get/set attributes on text, comment and attribute nodes
++ if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
++ return;
++ }
++
++ if ( pass && name in jQuery.attrFn ) {
++ return jQuery( elem )[ name ]( value );
++ }
++
++ // Fallback to prop when attributes are not supported
++ if ( typeof elem.getAttribute === "undefined" ) {
++ return jQuery.prop( elem, name, value );
++ }
++
++ notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
++
++ // All attributes are lowercase
++ // Grab necessary hook if one is defined
++ if ( notxml ) {
++ name = name.toLowerCase();
++ hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook );
++ }
++
++ if ( value !== undefined ) {
++
++ if ( value === null ) {
++ jQuery.removeAttr( elem, name );
++ return;
++
++ } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) {
++ return ret;
++
++ } else {
++ elem.setAttribute( name, "" + value );
++ return value;
++ }
++
++ } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) {
++ return ret;
++
++ } else {
++
++ ret = elem.getAttribute( name );
++
++ // Non-existent attributes return null, we normalize to undefined
++ return ret === null ?
++ undefined :
++ ret;
++ }
++ },
++
++ removeAttr: function( elem, value ) {
++ var propName, attrNames, name, l,
++ i = 0;
++
++ if ( value && elem.nodeType === 1 ) {
++ attrNames = value.toLowerCase().split( rspace );
++ l = attrNames.length;
++
++ for ( ; i < l; i++ ) {
++ name = attrNames[ i ];
++
++ if ( name ) {
++ propName = jQuery.propFix[ name ] || name;
++
++ // See #9699 for explanation of this approach (setting first, then removal)
++ jQuery.attr( elem, name, "" );
++ elem.removeAttribute( getSetAttribute ? name : propName );
++
++ // Set corresponding property to false for boolean attributes
++ if ( rboolean.test( name ) && propName in elem ) {
++ elem[ propName ] = false;
++ }
++ }
++ }
++ }
++ },
++
++ attrHooks: {
++ type: {
++ set: function( elem, value ) {
++ // We can't allow the type property to be changed (since it causes problems in IE)
++ if ( rtype.test( elem.nodeName ) && elem.parentNode ) {
++ jQuery.error( "type property can't be changed" );
++ } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
++ // Setting the type on a radio button after the value resets the value in IE6-9
++ // Reset value to it's default in case type is set after value
++ // This is for element creation
++ var val = elem.value;
++ elem.setAttribute( "type", value );
++ if ( val ) {
++ elem.value = val;
++ }
++ return value;
++ }
++ }
++ },
++ // Use the value property for back compat
++ // Use the nodeHook for button elements in IE6/7 (#1954)
++ value: {
++ get: function( elem, name ) {
++ if ( nodeHook && jQuery.nodeName( elem, "button" ) ) {
++ return nodeHook.get( elem, name );
++ }
++ return name in elem ?
++ elem.value :
++ null;
++ },
++ set: function( elem, value, name ) {
++ if ( nodeHook && jQuery.nodeName( elem, "button" ) ) {
++ return nodeHook.set( elem, value, name );
++ }
++ // Does not return so that setAttribute is also used
++ elem.value = value;
++ }
++ }
++ },
++
++ propFix: {
++ tabindex: "tabIndex",
++ readonly: "readOnly",
++ "for": "htmlFor",
++ "class": "className",
++ maxlength: "maxLength",
++ cellspacing: "cellSpacing",
++ cellpadding: "cellPadding",
++ rowspan: "rowSpan",
++ colspan: "colSpan",
++ usemap: "useMap",
++ frameborder: "frameBorder",
++ contenteditable: "contentEditable"
++ },
++
++ prop: function( elem, name, value ) {
++ var 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;
++ }
++
++ notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
++
++ if ( notxml ) {
++ // Fix name and attach hooks
++ name = jQuery.propFix[ name ] || name;
++ hooks = jQuery.propHooks[ name ];
++ }
++
++ if ( value !== undefined ) {
++ if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
++ return ret;
++
++ } else {
++ return ( elem[ name ] = value );
++ }
++
++ } else {
++ if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
++ return ret;
++
++ } else {
++ return elem[ name ];
++ }
++ }
++ },
++
++ propHooks: {
++ tabIndex: {
++ get: function( elem ) {
++ // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
++ // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
++ var attributeNode = elem.getAttributeNode("tabindex");
++
++ return attributeNode && attributeNode.specified ?
++ parseInt( attributeNode.value, 10 ) :
++ rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
++ 0 :
++ undefined;
++ }
++ }
++ }
++});
++
++// Add the tabIndex propHook to attrHooks for back-compat (different case is intentional)
++jQuery.attrHooks.tabindex = jQuery.propHooks.tabIndex;
++
++// Hook for boolean attributes
++boolHook = {
++ get: function( elem, name ) {
++ // Align boolean attributes with corresponding properties
++ // Fall back to attribute presence where some booleans are not supported
++ var attrNode,
++ 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;
++ }
++};
++
++// IE6/7 do not support getting/setting some attributes with get/setAttribute
++if ( !getSetAttribute ) {
++
++ fixSpecified = {
++ name: true,
++ id: true
++ };
++
++ // 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 ret && ( fixSpecified[ name ] ? ret.nodeValue !== "" : ret.specified ) ?
++ ret.nodeValue :
++ undefined;
++ },
++ set: function( elem, value, name ) {
++ // Set the existing or create a new attribute node
++ var ret = elem.getAttributeNode( name );
++ if ( !ret ) {
++ ret = document.createAttribute( name );
++ elem.setAttributeNode( ret );
++ }
++ return ( ret.nodeValue = value + "" );
++ }
++ };
++
++ // Apply the nodeHook to tabindex
++ jQuery.attrHooks.tabindex.set = nodeHook.set;
++
++ // Set width and height to auto instead of 0 on empty string( Bug #8150 )
++ // This is for removals
++ jQuery.each([ "width", "height" ], function( i, name ) {
++ jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
++ set: function( elem, value ) {
++ if ( value === "" ) {
++ elem.setAttribute( name, "auto" );
++ return value;
++ }
++ }
++ });
++ });
++
++ // Set contenteditable to false on removals(#10429)
++ // Setting to empty string throws an error as an invalid value
++ jQuery.attrHooks.contenteditable = {
++ get: nodeHook.get,
++ set: function( elem, value, name ) {
++ if ( value === "" ) {
++ value = "false";
++ }
++ nodeHook.set( elem, value, name );
++ }
++ };
++}
++
++
++// Some attributes require a special call on IE
++if ( !jQuery.support.hrefNormalized ) {
++ jQuery.each([ "href", "src", "width", "height" ], function( i, name ) {
++ jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
++ get: function( elem ) {
++ var ret = elem.getAttribute( name, 2 );
++ return ret === null ? undefined : ret;
++ }
++ });
++ });
++}
++
++if ( !jQuery.support.style ) {
++ jQuery.attrHooks.style = {
++ get: function( elem ) {
++ // Return undefined in the case of empty string
++ // Normalize to lowercase since IE uppercases css property names
++ return elem.style.cssText.toLowerCase() || undefined;
++ },
++ set: function( elem, value ) {
++ return ( elem.style.cssText = "" + value );
++ }
++ };
++}
++
++// Safari mis-reports the default selected property of an option
++// Accessing the parent's selectedIndex property fixes it
++if ( !jQuery.support.optSelected ) {
++ jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, {
++ get: function( elem ) {
++ var parent = elem.parentNode;
++
++ if ( parent ) {
++ parent.selectedIndex;
++
++ // Make sure that it also works with optgroups, see #5701
++ if ( parent.parentNode ) {
++ parent.parentNode.selectedIndex;
++ }
++ }
++ return null;
++ }
++ });
++}
++
++// 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 ], {
++ set: function( elem, value ) {
++ if ( jQuery.isArray( value ) ) {
++ return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
++ }
++ }
++ });
++});
++
++
++
++
++var rformElems = /^(?:textarea|input|select)$/i,
++ rtypenamespace = /^([^\.]*)?(?:\.(.+))?$/,
++ rhoverHack = /\bhover(\.\S+)?\b/,
++ rkeyEvent = /^key/,
++ rmouseEvent = /^(?:mouse|contextmenu)|click/,
++ rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
++ rquickIs = /^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,
++ quickParse = function( selector ) {
++ var quick = rquickIs.exec( selector );
++ if ( quick ) {
++ // 0 1 2 3
++ // [ _, tag, id, class ]
++ quick[1] = ( quick[1] || "" ).toLowerCase();
++ quick[3] = quick[3] && new RegExp( "(?:^|\\s)" + quick[3] + "(?:\\s|$)" );
++ }
++ return quick;
++ },
++ quickIs = function( elem, m ) {
++ var attrs = elem.attributes || {};
++ return (
++ (!m[1] || elem.nodeName.toLowerCase() === m[1]) &&
++ (!m[2] || (attrs.id || {}).value === m[2]) &&
++ (!m[3] || m[3].test( (attrs[ "class" ] || {}).value ))
++ );
++ },
++ hoverHack = function( events ) {
++ return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" );
++ };
++
++/*
++ * Helper functions for managing events -- not part of the public interface.
++ * Props to Dean Edwards' addEvent library for many of the ideas.
++ */
++jQuery.event = {
++
++ add: function( elem, types, handler, data, selector ) {
++
++ var elemData, eventHandle, events,
++ t, tns, type, namespaces, handleObj,
++ handleObjIn, quick, handlers, special;
++
++ // Don't attach events to noData or text/comment nodes (allow plain objects tho)
++ if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) {
++ return;
++ }
++
++ // 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 handler has a unique ID, used to find/remove it later
++ if ( !handler.guid ) {
++ handler.guid = jQuery.guid++;
++ }
++
++ // Init the element's event structure and main handler, if this is the first
++ events = elemData.events;
++ if ( !events ) {
++ elemData.events = events = {};
++ }
++ eventHandle = elemData.handle;
++ if ( !eventHandle ) {
++ elemData.handle = eventHandle = function( e ) {
++ // Discard the second event of a jQuery.event.trigger() and
++ // when an event is called after a page has unloaded
++ return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ?
++ jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
++ undefined;
++ };
++ // 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 = jQuery.trim( hoverHack(types) ).split( " " );
++ for ( t = 0; t < types.length; t++ ) {
++
++ tns = rtypenamespace.exec( types[t] ) || [];
++ type = tns[1];
++ namespaces = ( tns[2] || "" ).split( "." ).sort();
++
++ // 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: tns[1],
++ data: data,
++ handler: handler,
++ guid: handler.guid,
++ selector: selector,
++ quick: quickParse( selector ),
++ namespace: namespaces.join(".")
++ }, handleObjIn );
++
++ // Init the event handler queue if we're the first
++ handlers = events[ type ];
++ if ( !handlers ) {
++ handlers = events[ type ] = [];
++ 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 ) {
++ elem.addEventListener( type, eventHandle, false );
++
++ } else if ( elem.attachEvent ) {
++ elem.attachEvent( "on" + type, eventHandle );
++ }
++ }
++ }
++
++ if ( special.add ) {
++ special.add.call( elem, handleObj );
++
++ if ( !handleObj.handler.guid ) {
++ handleObj.handler.guid = handler.guid;
++ }
++ }
++
++ // Add 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;
++ }
++
++ // Nullify elem to prevent memory leaks in IE
++ elem = null;
++ },
++
++ global: {},
++
++ // Detach an event or set of events from an element
++ remove: function( elem, types, handler, selector, mappedTypes ) {
++
++ var elemData = jQuery.hasData( elem ) && jQuery._data( elem ),
++ t, tns, type, origType, namespaces, origCount,
++ j, events, special, handle, eventType, handleObj;
++
++ if ( !elemData || !(events = elemData.events) ) {
++ return;
++ }
++
++ // Once for each type.namespace in types; type may be omitted
++ types = jQuery.trim( hoverHack( types || "" ) ).split(" ");
++ for ( t = 0; t < types.length; t++ ) {
++ tns = rtypenamespace.exec( types[t] ) || [];
++ type = origType = tns[1];
++ namespaces = tns[2];
++
++ // 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 ] || {};
++ type = ( selector? special.delegateType : special.bindType ) || type;
++ eventType = events[ type ] || [];
++ origCount = eventType.length;
++ namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.)?") + "(\\.|$)") : null;
++
++ // Remove matching events
++ for ( j = 0; j < eventType.length; j++ ) {
++ handleObj = eventType[ j ];
++
++ if ( ( mappedTypes || origType === handleObj.origType ) &&
++ ( !handler || handler.guid === handleObj.guid ) &&
++ ( !namespaces || namespaces.test( handleObj.namespace ) ) &&
++ ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
++ eventType.splice( j--, 1 );
++
++ if ( handleObj.selector ) {
++ eventType.delegateCount--;
++ }
++ if ( special.remove ) {
++ special.remove.call( elem, handleObj );
++ }
++ }
++ }
++
++ // 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 ( eventType.length === 0 && origCount !== eventType.length ) {
++ if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
++ jQuery.removeEvent( elem, type, elemData.handle );
++ }
++
++ delete events[ type ];
++ }
++ }
++
++ // Remove the expando if it's no longer used
++ if ( jQuery.isEmptyObject( events ) ) {
++ handle = elemData.handle;
++ if ( handle ) {
++ handle.elem = null;
++ }
++
++ // removeData also checks for emptiness and clears the expando if empty
++ // so use it instead of delete
++ jQuery.removeData( elem, [ "events", "handle" ], true );
++ }
++ },
++
++ // Events that are safe to short-circuit if no handlers are attached.
++ // Native DOM events should not be added, they may have inline handlers.
++ customEvent: {
++ "getData": true,
++ "setData": true,
++ "changeData": true
++ },
++
++ trigger: function( event, data, elem, onlyHandlers ) {
++ // Don't do events on text and comment nodes
++ if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) {
++ return;
++ }
++
++ // Event object or event type
++ var type = event.type || event,
++ namespaces = [],
++ cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType;
++
++ // 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 ) {
++ // Exclusive events trigger only for the exact event (no namespaces)
++ type = type.slice(0, -1);
++ exclusive = true;
++ }
++
++ if ( type.indexOf( "." ) >= 0 ) {
++ // Namespaced trigger; create a regexp to match event type in handle()
++ namespaces = type.split(".");
++ type = namespaces.shift();
++ namespaces.sort();
++ }
++
++ if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) {
++ // No jQuery handlers for this event type, and it can't have inline handlers
++ return;
++ }
++
++ // Caller can pass in an Event, Object, or just an event type string
++ event = typeof event === "object" ?
++ // jQuery.Event object
++ event[ jQuery.expando ] ? event :
++ // Object literal
++ new jQuery.Event( type, event ) :
++ // Just the event type (string)
++ new jQuery.Event( type );
++
++ event.type = type;
++ event.isTrigger = true;
++ event.exclusive = exclusive;
++ event.namespace = namespaces.join( "." );
++ event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)") : null;
++ ontype = type.indexOf( ":" ) < 0 ? "on" + type : "";
++
++ // Handle a global trigger
++ if ( !elem ) {
++
++ // TODO: Stop taunting the data cache; remove global events and always attach to document
++ cache = jQuery.cache;
++ for ( i in cache ) {
++ if ( cache[ i ].events && cache[ i ].events[ type ] ) {
++ jQuery.event.trigger( event, data, cache[ i ].handle.elem, true );
++ }
++ }
++ return;
++ }
++
++ // Clean up the event in case it is being reused
++ event.result = undefined;
++ 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 );
++
++ // Allow special events to draw outside the lines
++ special = jQuery.event.special[ type ] || {};
++ if ( 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)
++ eventPath = [[ elem, special.bindType || type ]];
++ if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
++
++ bubbleType = special.delegateType || type;
++ cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode;
++ old = null;
++ for ( ; cur; cur = cur.parentNode ) {
++ eventPath.push([ cur, bubbleType ]);
++ old = cur;
++ }
++
++ // Only add window if we got to document (e.g., not plain obj or detached DOM)
++ if ( old && old === elem.ownerDocument ) {
++ eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]);
++ }
++ }
++
++ // Fire handlers on the event path
++ for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) {
++
++ cur = eventPath[i][0];
++ event.type = eventPath[i][1];
++
++ handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
++ if ( handle ) {
++ handle.apply( cur, data );
++ }
++ // Note that this is a bare JS function and not a jQuery handler
++ handle = ontype && cur[ ontype ];
++ if ( handle && jQuery.acceptData( cur ) && handle.apply( cur, data ) === false ) {
++ event.preventDefault();
++ }
++ }
++ event.type = type;
++
++ // If nobody prevented the default action, do it now
++ if ( !onlyHandlers && !event.isDefaultPrevented() ) {
++
++ if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) &&
++ !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {
++
++ // Call a native DOM method on the target with the same name name as the event.
++ // Can't use an .isFunction() check here because IE6/7 fails that test.
++ // Don't do default actions on window, that's where global variables be (#6170)
++ // IE<9 dies on focus/blur to hidden element (#1486)
++ if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) {
++
++ // Don't re-trigger an onFOO event when we call its FOO() method
++ old = elem[ ontype ];
++
++ if ( old ) {
++ elem[ ontype ] = null;
++ }
++
++ // Prevent re-triggering of the same event, since we already bubbled it above
++ jQuery.event.triggered = type;
++ elem[ type ]();
++ jQuery.event.triggered = undefined;
++
++ if ( old ) {
++ elem[ ontype ] = old;
++ }
++ }
++ }
++ }
++
++ return event.result;
++ },
++
++ dispatch: function( event ) {
++
++ // Make a writable jQuery.Event from the native event object
++ event = jQuery.event.fix( event || window.event );
++
++ var handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []),
++ delegateCount = handlers.delegateCount,
++ args = [].slice.call( arguments, 0 ),
++ run_all = !event.exclusive && !event.namespace,
++ handlerQueue = [],
++ i, j, cur, jqcur, ret, selMatch, matched, matches, handleObj, sel, related;
++
++ // Use the fix-ed jQuery.Event rather than the (read-only) native event
++ args[0] = event;
++ event.delegateTarget = this;
++
++ // Determine handlers that should run if there are delegated events
++ // Avoid disabled elements in IE (#6911) and non-left-click bubbling in Firefox (#3861)
++ if ( delegateCount && !event.target.disabled && !(event.button && event.type === "click") ) {
++
++ // Pregenerate a single jQuery object for reuse with .is()
++ jqcur = jQuery(this);
++ jqcur.context = this.ownerDocument || this;
++
++ for ( cur = event.target; cur != this; cur = cur.parentNode || this ) {
++ selMatch = {};
++ matches = [];
++ jqcur[0] = cur;
++ for ( i = 0; i < delegateCount; i++ ) {
++ handleObj = handlers[ i ];
++ sel = handleObj.selector;
++
++ if ( selMatch[ sel ] === undefined ) {
++ selMatch[ sel ] = (
++ handleObj.quick ? quickIs( cur, handleObj.quick ) : jqcur.is( sel )
++ );
++ }
++ if ( selMatch[ sel ] ) {
++ matches.push( handleObj );
++ }
++ }
++ if ( matches.length ) {
++ handlerQueue.push({ elem: cur, matches: matches });
++ }
++ }
++ }
++
++ // Add the remaining (directly-bound) handlers
++ if ( handlers.length > delegateCount ) {
++ handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) });
++ }
++
++ // Run delegates first; they may want to stop propagation beneath us
++ for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) {
++ matched = handlerQueue[ i ];
++ event.currentTarget = matched.elem;
++
++ for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) {
++ handleObj = matched.matches[ j ];
++
++ // Triggered event must either 1) be non-exclusive and have no namespace, or
++ // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
++ if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) {
++
++ event.data = handleObj.data;
++ event.handleObj = handleObj;
++
++ ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
++ .apply( matched.elem, args );
++
++ if ( ret !== undefined ) {
++ event.result = ret;
++ if ( ret === false ) {
++ event.preventDefault();
++ event.stopPropagation();
++ }
++ }
++ }
++ }
++ }
++
++ return event.result;
++ },
++
++ // Includes some event props shared by KeyEvent and MouseEvent
++ // *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 ***
++ props: "attrChange attrName relatedNode srcElement 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 eventDoc, doc, body,
++ 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;
++ }
++ },
++
++ fix: function( event ) {
++ if ( event[ jQuery.expando ] ) {
++ return event;
++ }
++
++ // Create a writable copy of the event object and normalize some properties
++ var i, prop,
++ originalEvent = event,
++ fixHook = jQuery.event.fixHooks[ event.type ] || {},
++ copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
++
++ event = jQuery.Event( originalEvent );
++
++ for ( i = copy.length; i; ) {
++ prop = copy[ --i ];
++ event[ prop ] = originalEvent[ prop ];
++ }
++
++ // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2)
++ if ( !event.target ) {
++ event.target = originalEvent.srcElement || document;
++ }
++
++ // Target should not be a text node (#504, Safari)
++ if ( event.target.nodeType === 3 ) {
++ event.target = event.target.parentNode;
++ }
++
++ // For mouse/key events; add metaKey if it's not there (#3368, IE6/7/8)
++ if ( event.metaKey === undefined ) {
++ event.metaKey = event.ctrlKey;
++ }
++
++ return fixHook.filter? fixHook.filter( event, originalEvent ) : event;
++ },
++
++ special: {
++ ready: {
++ // Make sure the ready event is setup
++ setup: jQuery.bindReady
++ },
++
++ load: {
++ // Prevent triggered image.load events from bubbling to window.load
++ noBubble: true
++ },
++
++ focus: {
++ delegateType: "focusin"
++ },
++ blur: {
++ delegateType: "focusout"
++ },
++
++ 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;
++ }
++ }
++ }
++ },
++
++ 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();
++ }
++ }
++};
++
++// Some plugins are using, but it's undocumented/deprecated and will be removed.
++// The 1.7 special event interface should provide all the hooks needed now.
++jQuery.event.handle = jQuery.event.dispatch;
++
++jQuery.removeEvent = document.removeEventListener ?
++ function( elem, type, handle ) {
++ if ( elem.removeEventListener ) {
++ elem.removeEventListener( type, handle, false );
++ }
++ } :
++ function( elem, type, handle ) {
++ if ( elem.detachEvent ) {
++ elem.detachEvent( "on" + type, handle );
++ }
++ };
++
++jQuery.Event = function( src, props ) {
++ // Allow instantiation without the 'new' keyword
++ if ( !(this instanceof jQuery.Event) ) {
++ return new jQuery.Event( src, props );
++ }
++
++ // Event object
++ if ( src && src.type ) {
++ this.originalEvent = src;
++ this.type = src.type;
++
++ // Events bubbling up the document may have been marked as prevented
++ // by a handler lower down the tree; reflect the correct value.
++ this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false ||
++ src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse;
++
++ // Event type
++ } else {
++ this.type = src;
++ }
++
++ // Put explicitly provided properties onto the event object
++ if ( props ) {
++ jQuery.extend( this, props );
++ }
++
++ // 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 = {
++ preventDefault: function() {
++ this.isDefaultPrevented = returnTrue;
++
++ var e = this.originalEvent;
++ if ( !e ) {
++ return;
++ }
++
++ // if preventDefault exists run it on the original event
++ if ( e.preventDefault ) {
++ e.preventDefault();
++
++ // otherwise set the returnValue property of the original event to false (IE)
++ } else {
++ e.returnValue = false;
++ }
++ },
++ stopPropagation: function() {
++ this.isPropagationStopped = returnTrue;
++
++ var e = this.originalEvent;
++ if ( !e ) {
++ return;
++ }
++ // if stopPropagation exists run it on the original event
++ if ( e.stopPropagation ) {
++ e.stopPropagation();
++ }
++ // otherwise set the cancelBubble property of the original event to true (IE)
++ e.cancelBubble = true;
++ },
++ stopImmediatePropagation: function() {
++ this.isImmediatePropagationStopped = returnTrue;
++ this.stopPropagation();
++ },
++ isDefaultPrevented: returnFalse,
++ isPropagationStopped: returnFalse,
++ isImmediatePropagationStopped: returnFalse
++};
++
++// Create mouseenter/leave events using mouseover/out and event-time checks
++jQuery.each({
++ mouseenter: "mouseover",
++ mouseleave: "mouseout"
++}, function( orig, fix ) {
++ jQuery.event.special[ orig ] = {
++ delegateType: fix,
++ bindType: fix,
++
++ handle: function( event ) {
++ var target = this,
++ related = event.relatedTarget,
++ handleObj = event.handleObj,
++ selector = handleObj.selector,
++ ret;
++
++ // 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;
++ }
++ };
++});
++
++// IE submit delegation
++if ( !jQuery.support.submitBubbles ) {
++
++ jQuery.event.special.submit = {
++ 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 && !form._submit_attached ) {
++ jQuery.event.add( form, "submit._submit", function( event ) {
++ // If form was submitted by the user, bubble the event up the tree
++ if ( this.parentNode && !event.isTrigger ) {
++ jQuery.event.simulate( "submit", this.parentNode, event, true );
++ }
++ });
++ form._submit_attached = true;
++ }
++ });
++ // return undefined since we don't need an event listener
++ },
++
++ 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" );
++ }
++ };
++}
++
++// IE change delegation and checkbox/radio fix
++if ( !jQuery.support.changeBubbles ) {
++
++ jQuery.event.special.change = {
++
++ 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;
++ 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;
++
++ if ( rformElems.test( elem.nodeName ) && !elem._change_attached ) {
++ jQuery.event.add( elem, "change._change", function( event ) {
++ if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
++ jQuery.event.simulate( "change", this.parentNode, event, true );
++ }
++ });
++ elem._change_attached = 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 );
++ }
++ },
++
++ teardown: function() {
++ jQuery.event.remove( this, "._change" );
++
++ return rformElems.test( this.nodeName );
++ }
++ };
++}
++
++// Create "bubbling" focus and blur events
++if ( !jQuery.support.focusinBubbles ) {
++ jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
++
++ // Attach a single capturing handler while someone wants focusin/focusout
++ var attaches = 0,
++ handler = function( event ) {
++ jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
++ };
++
++ jQuery.event.special[ fix ] = {
++ setup: function() {
++ if ( attaches++ === 0 ) {
++ document.addEventListener( orig, handler, true );
++ }
++ },
++ teardown: function() {
++ if ( --attaches === 0 ) {
++ document.removeEventListener( orig, handler, true );
++ }
++ }
++ };
++ });
++}
++
++jQuery.fn.extend({
++
++ on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
++ var origFn, type;
++
++ // Types can be a map of types/handlers
++ if ( typeof types === "object" ) {
++ // ( types-Object, selector, data )
++ if ( typeof selector !== "string" ) {
++ // ( types-Object, data )
++ data = selector;
++ selector = undefined;
++ }
++ for ( type in types ) {
++ this.on( type, selector, data, types[ type ], one );
++ }
++ return this;
++ }
++
++ 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 );
++ };
++ // 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.call( this, types, selector, data, fn, 1 );
++ },
++ off: function( types, selector, fn ) {
++ if ( types && types.preventDefault && types.handleObj ) {
++ // ( event ) dispatched jQuery.Event
++ var handleObj = types.handleObj;
++ jQuery( types.delegateTarget ).off(
++ handleObj.namespace? handleObj.type + "." + handleObj.namespace : handleObj.type,
++ handleObj.selector,
++ handleObj.handler
++ );
++ return this;
++ }
++ if ( typeof types === "object" ) {
++ // ( types-object [, selector] )
++ for ( var 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 );
++ });
++ },
++
++ bind: function( types, data, fn ) {
++ return this.on( types, null, data, fn );
++ },
++ unbind: function( types, fn ) {
++ return this.off( types, null, fn );
++ },
++
++ live: function( types, data, fn ) {
++ jQuery( this.context ).on( types, this.selector, data, fn );
++ return this;
++ },
++ die: function( types, fn ) {
++ jQuery( this.context ).off( types, this.selector || "**", fn );
++ return this;
++ },
++
++ delegate: function( selector, types, data, fn ) {
++ return this.on( types, selector, data, fn );
++ },
++ undelegate: function( selector, types, fn ) {
++ // ( namespace ) or ( selector, types [, fn] )
++ return arguments.length == 1? this.off( selector, "**" ) : this.off( types, selector, fn );
++ },
++
++ trigger: function( type, data ) {
++ return this.each(function() {
++ jQuery.event.trigger( type, data, this );
++ });
++ },
++ triggerHandler: function( type, data ) {
++ if ( this[0] ) {
++ return jQuery.event.trigger( type, data, this[0], true );
++ }
++ },
++
++ toggle: function( fn ) {
++ // Save reference to arguments for access in closure
++ var args = arguments,
++ guid = fn.guid || jQuery.guid++,
++ i = 0,
++ toggler = function( event ) {
++ // Figure out which function to execute
++ var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i;
++ jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 );
++
++ // Make sure that clicks stop
++ event.preventDefault();
++
++ // and execute the function
++ return args[ lastToggle ].apply( this, arguments ) || false;
++ };
++
++ // link all the functions, so any of them can unbind this click handler
++ toggler.guid = guid;
++ while ( i < args.length ) {
++ args[ i++ ].guid = guid;
++ }
++
++ return this.click( toggler );
++ },
++
++ hover: function( fnOver, fnOut ) {
++ return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
++ }
++});
++
++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 contextmenu").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.on( name, null, data, fn ) :
++ this.trigger( name );
++ };
++
++ if ( jQuery.attrFn ) {
++ jQuery.attrFn[ name ] = true;
++ }
++
++ if ( rkeyEvent.test( name ) ) {
++ jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks;
++ }
++
++ if ( rmouseEvent.test( name ) ) {
++ jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks;
++ }
++});
++
++
++
++/*!
++ * 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,
++ expando = "sizcache" + (Math.random() + '').replace('.', ''),
++ done = 0,
++ toString = Object.prototype.toString,
++ hasDuplicate = false,
++ baseHasDuplicate = true,
++ rBackslash = /\\/g,
++ rReturn = /\r\n/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, seed );
++
++ } 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, seed );
++ }
++ }
++
++ } 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, i, len, match, type, left;
++
++ if ( !expr ) {
++ return [];
++ }
++
++ for ( i = 0, len = Expr.order.length; i < len; i++ ) {
++ type = Expr.order[i];
++
++ if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
++ 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,
++ type, found, item, filter, left,
++ i, pass,
++ old = expr,
++ result = [],
++ curLoop = set,
++ isXMLFilter = set && set[0] && Sizzle.isXML( set[0] );
++
++ while ( expr && set.length ) {
++ for ( type in Expr.filter ) {
++ if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
++ 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 ( i = 0; (item = curLoop[i]) != null; i++ ) {
++ if ( item ) {
++ found = filter( item, match, i, curLoop );
++ 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 new Error( "Syntax error, unrecognized expression: " + msg );
++};
++
++/**
++ * Utility function for retreiving the text value of an array of DOM nodes
++ * @param {Array|Element} elem
++ */
++var getText = Sizzle.getText = function( elem ) {
++ var i, node,
++ nodeType = elem.nodeType,
++ ret = "";
++
++ if ( nodeType ) {
++ if ( nodeType === 1 || nodeType === 9 ) {
++ // Use textContent || innerText for elements
++ if ( typeof elem.textContent === 'string' ) {
++ return elem.textContent;
++ } else if ( typeof elem.innerText === 'string' ) {
++ // Replace IE's carriage returns
++ return elem.innerText.replace( rReturn, '' );
++ } else {
++ // Traverse it's children
++ for ( elem = elem.firstChild; elem; elem = elem.nextSibling) {
++ ret += getText( elem );
++ }
++ }
++ } else if ( nodeType === 3 || nodeType === 4 ) {
++ return elem.nodeValue;
++ }
++ } else {
++
++ // If no nodeType, this is expected to be an array
++ for ( i = 0; (node = elem[i]); i++ ) {
++ // Do not traverse comment nodes
++ if ( node.nodeType !== 8 ) {
++ ret += getText( node );
++ }
++ }
++ }
++ return ret;
++};
++
++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 || 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 first, last,
++ doneName, parent, cache,
++ count, diff,
++ 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":
++ first = match[2];
++ last = match[3];
++
++ if ( first === 1 && last === 0 ) {
++ return true;
++ }
++
++ doneName = match[0];
++ parent = elem.parentNode;
++
++ if ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) {
++ count = 0;
++
++ for ( node = parent.firstChild; node; node = node.nextSibling ) {
++ if ( node.nodeType === 1 ) {
++ node.nodeIndex = ++count;
++ }
++ }
++
++ parent[ expando ] = doneName;
++ }
++
++ 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 && 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 = Sizzle.attr ?
++ Sizzle.attr( elem, name ) :
++ 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 && Sizzle.attr ?
++ result != null :
++ 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;
++ };
++}
++
++// Check to see if the browser returns elements by name when
++// querying by getElementById (and provide a workaround)
++(function(){
++ // We're going to inject a fake input element with a specified name
++ var form = document.createElement("div"),
++ id = "script" + (new Date()).getTime(),
++ root = document.documentElement;
++
++ form.innerHTML = "<a name='" + id + "'/>";
++
++ // Inject it into the root element, check its status, and remove it quickly
++ root.insertBefore( form, root.firstChild );
++
++ // The workaround has to do additional checks after a getElementById
++ // Which slows things down for other browsers (hence the branching)
++ if ( document.getElementById( id ) ) {
++ Expr.find.ID = function( match, context, isXML ) {
++ if ( typeof context.getElementById !== "undefined" && !isXML ) {
++ var m = context.getElementById(match[1]);
++
++ return m ?
++ m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ?
++ [m] :
++ undefined :
++ [];
++ }
++ };
++
++ Expr.filter.ID = function( elem, match ) {
++ var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
++
++ return elem.nodeType === 1 && node && node.nodeValue === match;
++ };
++ }
++
++ root.removeChild( form );
++
++ // release memory in IE
++ root = form = null;
++})();
++
++(function(){
++ // Check to see if the browser returns only elements
++ // when doing getElementsByTagName("*")
++
++ // Create a fake element
++ var div = document.createElement("div");
++ div.appendChild( document.createComment("") );
++
++ // Make sure no comments are found
++ if ( div.getElementsByTagName("*").length > 0 ) {
++ Expr.find.TAG = function( match, context ) {
++ var results = context.getElementsByTagName( match[1] );
++
++ // Filter out possible comments
++ if ( match[1] === "*" ) {
++ var tmp = [];
++
++ for ( var i = 0; results[i]; i++ ) {
++ if ( results[i].nodeType === 1 ) {
++ tmp.push( results[i] );
++ }
++ }
++
++ results = tmp;
++ }
++
++ return results;
++ };
++ }
++
++ // Check to see if an attribute returns normalized href attributes
++ div.innerHTML = "<a href='#'></a>";
++
++ if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
++ div.firstChild.getAttribute("href") !== "#" ) {
++
++ Expr.attrHandle.href = function( elem ) {
++ return elem.getAttribute( "href", 2 );
++ };
++ }
++
++ // release memory in IE
++ div = null;
++})();
++
++if ( document.querySelectorAll ) {
++ (function(){
++ var oldSizzle = Sizzle,
++ div = document.createElement("div"),
++ id = "__sizzle__";
++
++ div.innerHTML = "<p class='TEST'></p>";
++
++ // Safari can't handle uppercase or unicode characters when
++ // in quirks mode.
++ if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
++ return;
++ }
++
++ Sizzle = function( query, context, extra, seed ) {
++ context = context || document;
++
++ // Only use querySelectorAll on non-XML documents
++ // (ID selectors don't work in non-HTML documents)
++ if ( !seed && !Sizzle.isXML(context) ) {
++ // See if we find a selector to speed up
++ var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query );
++
++ if ( match && (context.nodeType === 1 || context.nodeType === 9) ) {
++ // Speed-up: Sizzle("TAG")
++ if ( match[1] ) {
++ return makeArray( context.getElementsByTagName( query ), extra );
++
++ // Speed-up: Sizzle(".CLASS")
++ } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) {
++ return makeArray( context.getElementsByClassName( match[2] ), extra );
++ }
++ }
++
++ if ( context.nodeType === 9 ) {
++ // Speed-up: Sizzle("body")
++ // The body element only exists once, optimize finding it
++ if ( query === "body" && context.body ) {
++ return makeArray( [ context.body ], extra );
++
++ // Speed-up: Sizzle("#ID")
++ } else if ( match && match[3] ) {
++ var elem = context.getElementById( match[3] );
++
++ // Check parentNode to catch when Blackberry 4.6 returns
++ // nodes that are no longer in the document #6963
++ if ( elem && elem.parentNode ) {
++ // Handle the case where IE and Opera return items
++ // by name instead of ID
++ if ( elem.id === match[3] ) {
++ return makeArray( [ elem ], extra );
++ }
++
++ } else {
++ return makeArray( [], extra );
++ }
++ }
++
++ try {
++ return makeArray( context.querySelectorAll(query), extra );
++ } catch(qsaError) {}
++
++ // qSA works strangely on Element-rooted queries
++ // We can work around this by specifying an extra ID on the root
++ // and working up from there (Thanks to Andrew Dupont for the technique)
++ // IE 8 doesn't work on object elements
++ } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
++ var oldContext = context,
++ old = context.getAttribute( "id" ),
++ nid = old || id,
++ hasParent = context.parentNode,
++ relativeHierarchySelector = /^\s*[+~]/.test( query );
++
++ if ( !old ) {
++ context.setAttribute( "id", nid );
++ } else {
++ nid = nid.replace( /'/g, "\\$&" );
++ }
++ if ( relativeHierarchySelector && hasParent ) {
++ context = context.parentNode;
++ }
++
++ try {
++ if ( !relativeHierarchySelector || hasParent ) {
++ return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra );
++ }
++
++ } catch(pseudoError) {
++ } finally {
++ if ( !old ) {
++ oldContext.removeAttribute( "id" );
++ }
++ }
++ }
++ }
++
++ return oldSizzle(query, context, extra, seed);
++ };
++
++ for ( var prop in oldSizzle ) {
++ Sizzle[ prop ] = oldSizzle[ prop ];
++ }
++
++ // release memory in IE
++ div = null;
++ })();
++}
++
++(function(){
++ var html = document.documentElement,
++ matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector;
++
++ if ( matches ) {
++ // Check to see if it's possible to do matchesSelector
++ // on a disconnected node (IE 9 fails this)
++ var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ),
++ pseudoWorks = false;
++
++ try {
++ // This should fail with an exception
++ // Gecko does not error, returns false instead
++ matches.call( document.documentElement, "[test!='']:sizzle" );
++
++ } catch( pseudoError ) {
++ pseudoWorks = true;
++ }
++
++ Sizzle.matchesSelector = function( node, expr ) {
++ // Make sure that attribute selectors are quoted
++ expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']");
++
++ if ( !Sizzle.isXML( node ) ) {
++ try {
++ if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) {
++ var ret = matches.call( node, expr );
++
++ // IE 9's matchesSelector returns false on disconnected nodes
++ if ( ret || !disconnectedMatch ||
++ // As well, disconnected nodes are said to be in a document
++ // fragment in IE 9, so check for that
++ node.document && node.document.nodeType !== 11 ) {
++ return ret;
++ }
++ }
++ } catch(e) {}
++ }
++
++ return Sizzle(expr, null, null, [node]).length > 0;
++ };
++ }
++})();
++
++(function(){
++ var div = document.createElement("div");
++
++ div.innerHTML = "<div class='test e'></div><div class='test'></div>";
++
++ // Opera can't find a second classname (in 9.6)
++ // Also, make sure that getElementsByClassName actually exists
++ if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {
++ return;
++ }
++
++ // Safari caches class attributes, doesn't catch changes (in 3.2)
++ div.lastChild.className = "e";
++
++ if ( div.getElementsByClassName("e").length === 1 ) {
++ return;
++ }
++
++ Expr.order.splice(1, 0, "CLASS");
++ Expr.find.CLASS = function( match, context, isXML ) {
++ if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
++ return context.getElementsByClassName(match[1]);
++ }
++ };
++
++ // release memory in IE
++ div = null;
++})();
++
++function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
++ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
++ var elem = checkSet[i];
++
++ if ( elem ) {
++ var match = false;
++
++ elem = elem[dir];
++
++ while ( elem ) {
++ if ( elem[ expando ] === doneName ) {
++ match = checkSet[elem.sizset];
++ break;
++ }
++
++ if ( elem.nodeType === 1 && !isXML ){
++ elem[ expando ] = 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[ expando ] === doneName ) {
++ match = checkSet[elem.sizset];
++ break;
++ }
++
++ if ( elem.nodeType === 1 ) {
++ if ( !isXML ) {
++ elem[ expando ] = 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, seed ) {
++ 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, seed );
++ }
++
++ return Sizzle.filter( later, tmpSet );
++};
++
++// EXPOSE
++// Override sizzle attribute retrieval
++Sizzle.attr = jQuery.attr;
++Sizzle.selectors.attrMap = {};
++jQuery.find = Sizzle;
++jQuery.expr = Sizzle.selectors;
++jQuery.expr[":"] = jQuery.expr.filters;
++jQuery.unique = Sizzle.uniqueSort;
++jQuery.text = Sizzle.getText;
++jQuery.isXMLDoc = Sizzle.isXML;
++jQuery.contains = Sizzle.contains;
++
++
++})();
++
++
++var runtil = /Until$/,
++ rparentsprev = /^(?:parents|prevUntil|prevAll)/,
++ // Note: This RegExp should be improved, or likely pulled from Sizzle
++ rmultiselector = /,/,
++ isSimple = /^.[^:#\[\.,]*$/,
++ slice = Array.prototype.slice,
++ POS = jQuery.expr.match.POS,
++ // methods guaranteed to produce a unique set when starting from a unique set
++ guaranteedUnique = {
++ children: true,
++ contents: true,
++ next: true,
++ prev: true
++ };
++
++jQuery.fn.extend({
++ find: function( selector ) {
++ var self = this,
++ i, l;
++
++ if ( typeof selector !== "string" ) {
++ return jQuery( selector ).filter(function() {
++ for ( i = 0, l = self.length; i < l; i++ ) {
++ if ( jQuery.contains( self[ i ], this ) ) {
++ return true;
++ }
++ }
++ });
++ }
++
++ var ret = this.pushStack( "", "find", selector ),
++ length, n, r;
++
++ for ( i = 0, l = this.length; i < l; i++ ) {
++ length = ret.length;
++ jQuery.find( selector, this[i], ret );
++
++ if ( i > 0 ) {
++ // Make sure that the results are unique
++ for ( n = length; n < ret.length; n++ ) {
++ for ( r = 0; r < length; r++ ) {
++ if ( ret[r] === ret[n] ) {
++ ret.splice(n--, 1);
++ break;
++ }
++ }
++ }
++ }
++ }
++
++ return ret;
++ },
++
++ has: function( target ) {
++ var targets = jQuery( target );
++ return this.filter(function() {
++ for ( var i = 0, l = targets.length; i < l; i++ ) {
++ if ( jQuery.contains( this, targets[i] ) ) {
++ return true;
++ }
++ }
++ });
++ },
++
++ not: function( selector ) {
++ return this.pushStack( winnow(this, selector, false), "not", selector);
++ },
++
++ filter: function( selector ) {
++ return this.pushStack( winnow(this, selector, true), "filter", selector );
++ },
++
++ is: function( selector ) {
++ return !!selector && (
++ typeof selector === "string" ?
++ // If this is a positional selector, check membership in the returned set
++ // so $("p:first").is("p:last") won't return true for a doc with two "p".
++ POS.test( selector ) ?
++ jQuery( selector, this.context ).index( this[0] ) >= 0 :
++ jQuery.filter( selector, this ).length > 0 :
++ this.filter( selector ).length > 0 );
++ },
++
++ closest: function( selectors, context ) {
++ var ret = [], i, l, cur = this[0];
++
++ // Array (deprecated as of jQuery 1.7)
++ if ( jQuery.isArray( selectors ) ) {
++ var level = 1;
++
++ while ( cur && cur.ownerDocument && cur !== context ) {
++ for ( i = 0; i < selectors.length; i++ ) {
++
++ if ( jQuery( cur ).is( selectors[ i ] ) ) {
++ ret.push({ selector: selectors[ i ], elem: cur, level: level });
++ }
++ }
++
++ cur = cur.parentNode;
++ level++;
++ }
++
++ return ret;
++ }
++
++ // String
++ var pos = POS.test( selectors ) || typeof selectors !== "string" ?
++ jQuery( selectors, context || this.context ) :
++ 0;
++
++ for ( i = 0, l = this.length; i < l; i++ ) {
++ cur = this[i];
++
++ while ( cur ) {
++ if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
++ ret.push( cur );
++ break;
++
++ } else {
++ cur = cur.parentNode;
++ if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) {
++ break;
++ }
++ }
++ }
++ }
++
++ ret = ret.length > 1 ? jQuery.unique( ret ) : ret;
++
++ return this.pushStack( ret, "closest", selectors );
++ },
++
++ // Determine the position of an element within
++ // the matched set of elements
++ index: function( elem ) {
++
++ // No argument, return index in parent
++ if ( !elem ) {
++ return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1;
++ }
++
++ // index in selector
++ if ( typeof elem === "string" ) {
++ return jQuery.inArray( this[0], jQuery( elem ) );
++ }
++
++ // Locate the position of the desired element
++ return jQuery.inArray(
++ // If it receives a jQuery object, the first element is used
++ elem.jquery ? elem[0] : elem, this );
++ },
++
++ add: function( selector, context ) {
++ var set = typeof selector === "string" ?
++ jQuery( selector, context ) :
++ jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),
++ all = jQuery.merge( this.get(), set );
++
++ return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?
++ all :
++ jQuery.unique( all ) );
++ },
++
++ andSelf: function() {
++ return this.add( this.prevObject );
++ }
++});
++
++// A painfully simple check to see if an element is disconnected
++// from a document (should be improved, where feasible).
++function isDisconnected( node ) {
++ return !node || !node.parentNode || node.parentNode.nodeType === 11;
++}
++
++jQuery.each({
++ parent: function( elem ) {
++ var parent = elem.parentNode;
++ return parent && parent.nodeType !== 11 ? parent : null;
++ },
++ parents: function( elem ) {
++ return jQuery.dir( elem, "parentNode" );
++ },
++ parentsUntil: function( elem, i, until ) {
++ return jQuery.dir( elem, "parentNode", until );
++ },
++ next: function( elem ) {
++ return jQuery.nth( elem, 2, "nextSibling" );
++ },
++ prev: function( elem ) {
++ return jQuery.nth( elem, 2, "previousSibling" );
++ },
++ nextAll: function( elem ) {
++ return jQuery.dir( elem, "nextSibling" );
++ },
++ prevAll: function( elem ) {
++ return jQuery.dir( elem, "previousSibling" );
++ },
++ nextUntil: function( elem, i, until ) {
++ return jQuery.dir( elem, "nextSibling", until );
++ },
++ prevUntil: function( elem, i, until ) {
++ return jQuery.dir( elem, "previousSibling", until );
++ },
++ siblings: function( elem ) {
++ return jQuery.sibling( elem.parentNode.firstChild, elem );
++ },
++ children: function( elem ) {
++ return jQuery.sibling( elem.firstChild );
++ },
++ contents: function( elem ) {
++ return jQuery.nodeName( elem, "iframe" ) ?
++ elem.contentDocument || elem.contentWindow.document :
++ jQuery.makeArray( elem.childNodes );
++ }
++}, function( name, fn ) {
++ jQuery.fn[ name ] = function( until, selector ) {
++ var ret = jQuery.map( this, fn, until );
++
++ if ( !runtil.test( name ) ) {
++ selector = until;
++ }
++
++ if ( selector && typeof selector === "string" ) {
++ ret = jQuery.filter( selector, ret );
++ }
++
++ ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret;
++
++ if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {
++ ret = ret.reverse();
++ }
++
++ return this.pushStack( ret, name, slice.call( arguments ).join(",") );
++ };
++});
++
++jQuery.extend({
++ filter: function( expr, elems, not ) {
++ if ( not ) {
++ expr = ":not(" + expr + ")";
++ }
++
++ return elems.length === 1 ?
++ jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :
++ jQuery.find.matches(expr, elems);
++ },
++
++ dir: function( elem, dir, until ) {
++ var matched = [],
++ cur = elem[ dir ];
++
++ while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
++ if ( cur.nodeType === 1 ) {
++ matched.push( cur );
++ }
++ cur = cur[dir];
++ }
++ return matched;
++ },
++
++ nth: function( cur, result, dir, elem ) {
++ result = result || 1;
++ var num = 0;
++
++ for ( ; cur; cur = cur[dir] ) {
++ if ( cur.nodeType === 1 && ++num === result ) {
++ break;
++ }
++ }
++
++ return cur;
++ },
++
++ sibling: function( n, elem ) {
++ var r = [];
++
++ for ( ; n; n = n.nextSibling ) {
++ if ( n.nodeType === 1 && n !== elem ) {
++ r.push( n );
++ }
++ }
++
++ return r;
++ }
++});
++
++// Implement the identical functionality for filter and not
++function winnow( elements, qualifier, keep ) {
++
++ // Can't pass null or undefined to indexOf in Firefox 4
++ // Set to 0 to skip string check
++ qualifier = qualifier || 0;
++
++ if ( jQuery.isFunction( qualifier ) ) {
++ return jQuery.grep(elements, function( elem, i ) {
++ var retVal = !!qualifier.call( elem, i, elem );
++ return retVal === keep;
++ });
++
++ } else if ( qualifier.nodeType ) {
++ return jQuery.grep(elements, function( elem, i ) {
++ return ( elem === qualifier ) === keep;
++ });
++
++ } else if ( typeof qualifier === "string" ) {
++ var filtered = jQuery.grep(elements, function( elem ) {
++ return elem.nodeType === 1;
++ });
++
++ if ( isSimple.test( qualifier ) ) {
++ return jQuery.filter(qualifier, filtered, !keep);
++ } else {
++ qualifier = jQuery.filter( qualifier, filtered );
++ }
++ }
++
++ return jQuery.grep(elements, function( elem, i ) {
++ return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep;
++ });
++}
++
++
++
++
++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|canvas|datalist|details|figcaption|figure|footer|" +
++ "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
++ rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
++ rleadingWhitespace = /^\s+/,
++ rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,
++ rtagName = /<([\w:]+)/,
++ rtbody = /<tbody/i,
++ rhtml = /<|&#?\w+;/,
++ rnoInnerhtml = /<(?:script|style)/i,
++ rnocache = /<(?:script|object|embed|option|style)/i,
++ rnoshimcache = new RegExp("<(?:" + nodeNames + ")", "i"),
++ // checked="checked" or checked
++ rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
++ rscriptType = /\/(java|ecma)script/i,
++ rcleanScript = /^\s*<!(?:\[CDATA\[|\-\-)/,
++ wrapMap = {
++ option: [ 1, "<select multiple='multiple'>", "</select>" ],
++ legend: [ 1, "<fieldset>", "</fieldset>" ],
++ thead: [ 1, "<table>", "</table>" ],
++ tr: [ 2, "<table><tbody>", "</tbody></table>" ],
++ td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
++ col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
++ area: [ 1, "<map>", "</map>" ],
++ _default: [ 0, "", "" ]
++ },
++ safeFragment = createSafeFragment( document );
++
++wrapMap.optgroup = wrapMap.option;
++wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
++wrapMap.th = wrapMap.td;
++
++// IE can't serialize <link> and <script> tags normally
++if ( !jQuery.support.htmlSerialize ) {
++ wrapMap._default = [ 1, "div<div>", "</div>" ];
++}
++
++jQuery.fn.extend({
++ text: function( text ) {
++ if ( jQuery.isFunction(text) ) {
++ return this.each(function(i) {
++ var self = jQuery( this );
++
++ self.text( text.call(this, i, self.text()) );
++ });
++ }
++
++ if ( typeof text !== "object" && text !== undefined ) {
++ return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
++ }
++
++ return jQuery.text( this );
++ },
++
++ wrapAll: function( html ) {
++ if ( jQuery.isFunction( html ) ) {
++ return this.each(function(i) {
++ jQuery(this).wrapAll( html.call(this, i) );
++ });
++ }
++
++ if ( this[0] ) {
++ // The elements to wrap the target around
++ var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
++
++ if ( this[0].parentNode ) {
++ wrap.insertBefore( this[0] );
++ }
++
++ wrap.map(function() {
++ var elem = this;
++
++ while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
++ elem = elem.firstChild;
++ }
++
++ return elem;
++ }).append( this );
++ }
++
++ return this;
++ },
++
++ wrapInner: function( html ) {
++ if ( jQuery.isFunction( html ) ) {
++ return this.each(function(i) {
++ jQuery(this).wrapInner( html.call(this, i) );
++ });
++ }
++
++ return this.each(function() {
++ var self = jQuery( this ),
++ contents = self.contents();
++
++ if ( contents.length ) {
++ contents.wrapAll( html );
++
++ } else {
++ self.append( html );
++ }
++ });
++ },
++
++ wrap: function( html ) {
++ var isFunction = jQuery.isFunction( html );
++
++ return this.each(function(i) {
++ jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
++ });
++ },
++
++ unwrap: function() {
++ return this.parent().each(function() {
++ if ( !jQuery.nodeName( this, "body" ) ) {
++ jQuery( this ).replaceWith( this.childNodes );
++ }
++ }).end();
++ },
++
++ append: function() {
++ return this.domManip(arguments, true, function( elem ) {
++ if ( this.nodeType === 1 ) {
++ this.appendChild( elem );
++ }
++ });
++ },
++
++ prepend: function() {
++ return this.domManip(arguments, true, function( elem ) {
++ if ( this.nodeType === 1 ) {
++ this.insertBefore( elem, this.firstChild );
++ }
++ });
++ },
++
++ before: function() {
++ if ( this[0] && this[0].parentNode ) {
++ return this.domManip(arguments, false, function( elem ) {
++ this.parentNode.insertBefore( elem, this );
++ });
++ } else if ( arguments.length ) {
++ var set = jQuery.clean( arguments );
++ set.push.apply( set, this.toArray() );
++ return this.pushStack( set, "before", arguments );
++ }
++ },
++
++ after: function() {
++ if ( this[0] && this[0].parentNode ) {
++ return this.domManip(arguments, false, function( elem ) {
++ this.parentNode.insertBefore( elem, this.nextSibling );
++ });
++ } else if ( arguments.length ) {
++ var set = this.pushStack( this, "after", arguments );
++ set.push.apply( set, jQuery.clean(arguments) );
++ return set;
++ }
++ },
++
++ // keepData is for internal use only--do not document
++ remove: function( selector, keepData ) {
++ for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
++ if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
++ if ( !keepData && elem.nodeType === 1 ) {
++ jQuery.cleanData( elem.getElementsByTagName("*") );
++ jQuery.cleanData( [ elem ] );
++ }
++
++ if ( elem.parentNode ) {
++ elem.parentNode.removeChild( elem );
++ }
++ }
++ }
++
++ return this;
++ },
++
++ empty: function() {
++ for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
++ // Remove element nodes and prevent memory leaks
++ if ( elem.nodeType === 1 ) {
++ jQuery.cleanData( elem.getElementsByTagName("*") );
++ }
++
++ // Remove any remaining nodes
++ while ( elem.firstChild ) {
++ elem.removeChild( elem.firstChild );
++ }
++ }
++
++ return this;
++ },
++
++ clone: function( dataAndEvents, deepDataAndEvents ) {
++ dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
++ deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
++
++ return this.map( function () {
++ return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
++ });
++ },
++
++ html: function( value ) {
++ if ( value === undefined ) {
++ return this[0] && this[0].nodeType === 1 ?
++ this[0].innerHTML.replace(rinlinejQuery, "") :
++ null;
++
++ // See if we can take a shortcut and just use innerHTML
++ } else if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
++ (jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
++ !wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {
++
++ value = value.replace(rxhtmlTag, "<$1></$2>");
++
++ try {
++ for ( var i = 0, l = this.length; i < l; i++ ) {
++ // Remove element nodes and prevent memory leaks
++ if ( this[i].nodeType === 1 ) {
++ jQuery.cleanData( this[i].getElementsByTagName("*") );
++ this[i].innerHTML = value;
++ }
++ }
++
++ // If using innerHTML throws an exception, use the fallback method
++ } catch(e) {
++ this.empty().append( value );
++ }
++
++ } else if ( jQuery.isFunction( value ) ) {
++ this.each(function(i){
++ var self = jQuery( this );
++
++ self.html( value.call(this, i, self.html()) );
++ });
++
++ } else {
++ this.empty().append( value );
++ }
++
++ return this;
++ },
++
++ replaceWith: function( value ) {
++ if ( this[0] && this[0].parentNode ) {
++ // Make sure that the elements are removed from the DOM before they are inserted
++ // this can help fix replacing a parent with child elements
++ if ( jQuery.isFunction( value ) ) {
++ return this.each(function(i) {
++ var self = jQuery(this), old = self.html();
++ self.replaceWith( value.call( this, i, old ) );
++ });
++ }
++
++ if ( typeof value !== "string" ) {
++ value = jQuery( value ).detach();
++ }
++
++ return this.each(function() {
++ var next = this.nextSibling,
++ parent = this.parentNode;
++
++ jQuery( this ).remove();
++
++ if ( next ) {
++ jQuery(next).before( value );
++ } else {
++ jQuery(parent).append( value );
++ }
++ });
++ } else {
++ return this.length ?
++ this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value ) :
++ this;
++ }
++ },
++
++ detach: function( selector ) {
++ return this.remove( selector, true );
++ },
++
++ domManip: function( args, table, callback ) {
++ var results, first, fragment, parent,
++ value = args[0],
++ scripts = [];
++
++ // We can't cloneNode fragments that contain checked, in WebKit
++ if ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === "string" && rchecked.test( value ) ) {
++ return this.each(function() {
++ jQuery(this).domManip( args, table, callback, true );
++ });
++ }
++
++ if ( jQuery.isFunction(value) ) {
++ return this.each(function(i) {
++ var self = jQuery(this);
++ args[0] = value.call(this, i, table ? self.html() : undefined);
++ self.domManip( args, table, callback );
++ });
++ }
++
++ if ( this[0] ) {
++ parent = value && value.parentNode;
++
++ // If we're in a fragment, just use that instead of building a new one
++ if ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) {
++ results = { fragment: parent };
++
++ } else {
++ results = jQuery.buildFragment( args, this, scripts );
++ }
++
++ fragment = results.fragment;
++
++ if ( fragment.childNodes.length === 1 ) {
++ first = fragment = fragment.firstChild;
++ } else {
++ first = fragment.firstChild;
++ }
++
++ if ( first ) {
++ table = table && jQuery.nodeName( first, "tr" );
++
++ for ( var i = 0, l = this.length, lastIndex = l - 1; i < l; i++ ) {
++ callback.call(
++ table ?
++ root(this[i], first) :
++ this[i],
++ // Make sure that we do not leak memory by inadvertently discarding
++ // the original fragment (which might have attached data) instead of
++ // using it; in addition, use the original fragment object for the last
++ // item instead of first because it can end up being emptied incorrectly
++ // in certain situations (Bug #8070).
++ // Fragments from the fragment cache must always be cloned and never used
++ // in place.
++ results.cacheable || ( l > 1 && i < lastIndex ) ?
++ jQuery.clone( fragment, true, true ) :
++ fragment
++ );
++ }
++ }
++
++ if ( scripts.length ) {
++ jQuery.each( scripts, evalScript );
++ }
++ }
++
++ return this;
++ }
++});
++
++function root( elem, cur ) {
++ return jQuery.nodeName(elem, "table") ?
++ (elem.getElementsByTagName("tbody")[0] ||
++ elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
++ elem;
++}
++
++function cloneCopyEvent( src, dest ) {
++
++ if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
++ return;
++ }
++
++ var type, i, l,
++ oldData = jQuery._data( src ),
++ curData = jQuery._data( dest, oldData ),
++ events = oldData.events;
++
++ if ( events ) {
++ delete curData.handle;
++ curData.events = {};
++
++ for ( type in events ) {
++ for ( i = 0, l = events[ type ].length; i < l; i++ ) {
++ jQuery.event.add( dest, type + ( events[ type ][ i ].namespace ? "." : "" ) + events[ type ][ i ].namespace, events[ type ][ i ], events[ type ][ i ].data );
++ }
++ }
++ }
++
++ // make the cloned public data object a copy from the original
++ if ( curData.data ) {
++ curData.data = jQuery.extend( {}, curData.data );
++ }
++}
++
++function cloneFixAttributes( src, dest ) {
++ var nodeName;
++
++ // We do not need to do anything for non-Elements
++ if ( dest.nodeType !== 1 ) {
++ return;
++ }
++
++ // clearAttributes removes the attributes, which we don't want,
++ // but also removes the attachEvent events, which we *do* want
++ if ( dest.clearAttributes ) {
++ dest.clearAttributes();
++ }
++
++ // mergeAttributes, in contrast, only merges back on the
++ // original attributes, not the events
++ if ( dest.mergeAttributes ) {
++ dest.mergeAttributes( src );
++ }
++
++ nodeName = dest.nodeName.toLowerCase();
++
++ // IE6-8 fail to clone children inside object elements that use
++ // the proprietary classid attribute value (rather than the type
++ // attribute) to identify the type of content to display
++ if ( nodeName === "object" ) {
++ dest.outerHTML = src.outerHTML;
++
++ } else if ( nodeName === "input" && (src.type === "checkbox" || src.type === "radio") ) {
++ // IE6-8 fails to persist the checked state of a cloned checkbox
++ // or radio button. Worse, IE6-7 fail to give the cloned element
++ // a checked appearance if the defaultChecked value isn't also set
++ if ( src.checked ) {
++ dest.defaultChecked = dest.checked = src.checked;
++ }
++
++ // IE6-7 get confused and end up setting the value of a cloned
++ // checkbox/radio button to an empty string instead of "on"
++ if ( dest.value !== src.value ) {
++ dest.value = src.value;
++ }
++
++ // IE6-8 fails to return the selected option to the default selected
++ // state when cloning options
++ } else if ( nodeName === "option" ) {
++ dest.selected = src.defaultSelected;
++
++ // IE6-8 fails to set the defaultValue to the correct value when
++ // cloning other types of input fields
++ } else if ( nodeName === "input" || nodeName === "textarea" ) {
++ dest.defaultValue = src.defaultValue;
++ }
++
++ // Event data gets referenced instead of copied if the expando
++ // gets copied too
++ dest.removeAttribute( jQuery.expando );
++}
++
++jQuery.buildFragment = function( args, nodes, scripts ) {
++ var fragment, cacheable, cacheresults, doc,
++ first = args[ 0 ];
++
++ // nodes may contain either an explicit document object,
++ // a jQuery collection or context object.
++ // If nodes[0] contains a valid object to assign to doc
++ if ( nodes && nodes[0] ) {
++ doc = nodes[0].ownerDocument || nodes[0];
++ }
++
++ // Ensure that an attr object doesn't incorrectly stand in as a document object
++ // Chrome and Firefox seem to allow this to occur and will throw exception
++ // Fixes #8950
++ if ( !doc.createDocumentFragment ) {
++ doc = document;
++ }
++
++ // Only cache "small" (1/2 KB) HTML strings that are associated with the main document
++ // Cloning options loses the selected state, so don't cache them
++ // IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
++ // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
++ // Lastly, IE6,7,8 will not correctly reuse cached fragments that were created from unknown elems #10501
++ if ( args.length === 1 && typeof first === "string" && first.length < 512 && doc === document &&
++ first.charAt(0) === "<" && !rnocache.test( first ) &&
++ (jQuery.support.checkClone || !rchecked.test( first )) &&
++ (jQuery.support.html5Clone || !rnoshimcache.test( first )) ) {
++
++ cacheable = true;
++
++ cacheresults = jQuery.fragments[ first ];
++ if ( cacheresults && cacheresults !== 1 ) {
++ fragment = cacheresults;
++ }
++ }
++
++ if ( !fragment ) {
++ fragment = doc.createDocumentFragment();
++ jQuery.clean( args, doc, fragment, scripts );
++ }
++
++ if ( cacheable ) {
++ jQuery.fragments[ first ] = cacheresults ? fragment : 1;
++ }
++
++ return { fragment: fragment, cacheable: cacheable };
++};
++
++jQuery.fragments = {};
++
++jQuery.each({
++ appendTo: "append",
++ prependTo: "prepend",
++ insertBefore: "before",
++ insertAfter: "after",
++ replaceAll: "replaceWith"
++}, function( name, original ) {
++ jQuery.fn[ name ] = function( selector ) {
++ var ret = [],
++ insert = jQuery( selector ),
++ parent = this.length === 1 && this[0].parentNode;
++
++ if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {
++ insert[ original ]( this[0] );
++ return this;
++
++ } else {
++ for ( var i = 0, l = insert.length; i < l; i++ ) {
++ var elems = ( i > 0 ? this.clone(true) : this ).get();
++ jQuery( insert[i] )[ original ]( elems );
++ ret = ret.concat( elems );
++ }
++
++ return this.pushStack( ret, name, insert.selector );
++ }
++ };
++});
++
++function getAll( elem ) {
++ if ( typeof elem.getElementsByTagName !== "undefined" ) {
++ return elem.getElementsByTagName( "*" );
++
++ } else if ( typeof elem.querySelectorAll !== "undefined" ) {
++ return elem.querySelectorAll( "*" );
++
++ } else {
++ return [];
++ }
++}
++
++// Used in clean, fixes the defaultChecked property
++function fixDefaultChecked( elem ) {
++ if ( elem.type === "checkbox" || elem.type === "radio" ) {
++ elem.defaultChecked = elem.checked;
++ }
++}
++// Finds all inputs and passes them to fixDefaultChecked
++function findInputs( elem ) {
++ var nodeName = ( elem.nodeName || "" ).toLowerCase();
++ if ( nodeName === "input" ) {
++ fixDefaultChecked( elem );
++ // Skip scripts, get other children
++ } else if ( nodeName !== "script" && typeof elem.getElementsByTagName !== "undefined" ) {
++ jQuery.grep( elem.getElementsByTagName("input"), fixDefaultChecked );
++ }
++}
++
++// Derived From: http://www.iecss.com/shimprove/javascript/shimprove.1-0-1.js
++function shimCloneNode( elem ) {
++ var div = document.createElement( "div" );
++ safeFragment.appendChild( div );
++
++ div.innerHTML = elem.outerHTML;
++ return div.firstChild;
++}
++
++jQuery.extend({
++ clone: function( elem, dataAndEvents, deepDataAndEvents ) {
++ var srcElements,
++ destElements,
++ i,
++ // IE<=8 does not properly clone detached, unknown element nodes
++ clone = jQuery.support.html5Clone || !rnoshimcache.test( "<" + elem.nodeName ) ?
++ elem.cloneNode( true ) :
++ shimCloneNode( elem );
++
++ if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) &&
++ (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {
++ // IE copies events bound via attachEvent when using cloneNode.
++ // Calling detachEvent on the clone will also remove the events
++ // from the original. In order to get around this, we use some
++ // proprietary methods to clear the events. Thanks to MooTools
++ // guys for this hotness.
++
++ cloneFixAttributes( elem, clone );
++
++ // Using Sizzle here is crazy slow, so we use getElementsByTagName instead
++ srcElements = getAll( elem );
++ destElements = getAll( clone );
++
++ // Weird iteration because IE will replace the length property
++ // with an element if you are cloning the body and one of the
++ // elements on the page has a name or id of "length"
++ for ( i = 0; srcElements[i]; ++i ) {
++ // Ensure that the destination node is not null; Fixes #9587
++ if ( destElements[i] ) {
++ cloneFixAttributes( srcElements[i], destElements[i] );
++ }
++ }
++ }
++
++ // Copy the events from the original to the clone
++ if ( dataAndEvents ) {
++ cloneCopyEvent( elem, clone );
++
++ if ( deepDataAndEvents ) {
++ srcElements = getAll( elem );
++ destElements = getAll( clone );
++
++ for ( i = 0; srcElements[i]; ++i ) {
++ cloneCopyEvent( srcElements[i], destElements[i] );
++ }
++ }
++ }
++
++ srcElements = destElements = null;
++
++ // Return the cloned set
++ return clone;
++ },
++
++ clean: function( elems, context, fragment, scripts ) {
++ var checkScriptType;
++
++ context = context || document;
++
++ // !context.createElement fails in IE with an error but returns typeof 'object'
++ if ( typeof context.createElement === "undefined" ) {
++ context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
++ }
++
++ var ret = [], j;
++
++ for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
++ if ( typeof elem === "number" ) {
++ elem += "";
++ }
++
++ if ( !elem ) {
++ continue;
++ }
++
++ // Convert html string into DOM nodes
++ if ( typeof elem === "string" ) {
++ if ( !rhtml.test( elem ) ) {
++ elem = context.createTextNode( elem );
++ } else {
++ // Fix "XHTML"-style tags in all browsers
++ elem = elem.replace(rxhtmlTag, "<$1></$2>");
++
++ // Trim whitespace, otherwise indexOf won't work as expected
++ var tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase(),
++ wrap = wrapMap[ tag ] || wrapMap._default,
++ depth = wrap[0],
++ div = context.createElement("div");
++
++ // Append wrapper element to unknown element safe doc fragment
++ if ( context === document ) {
++ // Use the fragment we've already created for this document
++ safeFragment.appendChild( div );
++ } else {
++ // Use a fragment created with the owner document
++ createSafeFragment( context ).appendChild( div );
++ }
++
++ // Go to html and back, then peel off extra wrappers
++ div.innerHTML = wrap[1] + elem + wrap[2];
++
++ // Move to the right depth
++ while ( depth-- ) {
++ div = div.lastChild;
++ }
++
++ // Remove IE's autoinserted <tbody> from table fragments
++ if ( !jQuery.support.tbody ) {
++
++ // String was a <table>, *may* have spurious <tbody>
++ var hasBody = rtbody.test(elem),
++ tbody = tag === "table" && !hasBody ?
++ div.firstChild && div.firstChild.childNodes :
++
++ // String was a bare <thead> or <tfoot>
++ wrap[1] === "<table>" && !hasBody ?
++ div.childNodes :
++ [];
++
++ for ( j = tbody.length - 1; j >= 0 ; --j ) {
++ if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
++ tbody[ j ].parentNode.removeChild( tbody[ j ] );
++ }
++ }
++ }
++
++ // IE completely kills leading whitespace when innerHTML is used
++ if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
++ div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
++ }
++
++ elem = div.childNodes;
++ }
++ }
++
++ // Resets defaultChecked for any radios and checkboxes
++ // about to be appended to the DOM in IE 6/7 (#8060)
++ var len;
++ if ( !jQuery.support.appendChecked ) {
++ if ( elem[0] && typeof (len = elem.length) === "number" ) {
++ for ( j = 0; j < len; j++ ) {
++ findInputs( elem[j] );
++ }
++ } else {
++ findInputs( elem );
++ }
++ }
++
++ if ( elem.nodeType ) {
++ ret.push( elem );
++ } else {
++ ret = jQuery.merge( ret, elem );
++ }
++ }
++
++ if ( fragment ) {
++ checkScriptType = function( elem ) {
++ return !elem.type || rscriptType.test( elem.type );
++ };
++ for ( i = 0; ret[i]; i++ ) {
++ if ( scripts && jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
++ scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
++
++ } else {
++ if ( ret[i].nodeType === 1 ) {
++ var jsTags = jQuery.grep( ret[i].getElementsByTagName( "script" ), checkScriptType );
++
++ ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) );
++ }
++ fragment.appendChild( ret[i] );
++ }
++ }
++ }
++
++ return ret;
++ },
++
++ cleanData: function( elems ) {
++ var data, id,
++ cache = jQuery.cache,
++ special = jQuery.event.special,
++ deleteExpando = jQuery.support.deleteExpando;
++
++ for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
++ if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
++ continue;
++ }
++
++ id = elem[ jQuery.expando ];
++
++ if ( id ) {
++ data = cache[ id ];
++
++ if ( data && data.events ) {
++ for ( var type in data.events ) {
++ if ( special[ type ] ) {
++ jQuery.event.remove( elem, type );
++
++ // This is a shortcut to avoid jQuery.event.remove's overhead
++ } else {
++ jQuery.removeEvent( elem, type, data.handle );
++ }
++ }
++
++ // Null the DOM reference to avoid IE6/7/8 leak (#7054)
++ if ( data.handle ) {
++ data.handle.elem = null;
++ }
++ }
++
++ if ( deleteExpando ) {
++ delete elem[ jQuery.expando ];
++
++ } else if ( elem.removeAttribute ) {
++ elem.removeAttribute( jQuery.expando );
++ }
++
++ delete cache[ id ];
++ }
++ }
++ }
++});
++
++function evalScript( i, elem ) {
++ if ( elem.src ) {
++ jQuery.ajax({
++ url: elem.src,
++ async: false,
++ dataType: "script"
++ });
++ } else {
++ jQuery.globalEval( ( elem.text || elem.textContent || elem.innerHTML || "" ).replace( rcleanScript, "/*$0*/" ) );
++ }
++
++ if ( elem.parentNode ) {
++ elem.parentNode.removeChild( elem );
++ }
++}
++
++
++
++
++var ralpha = /alpha\([^)]*\)/i,
++ ropacity = /opacity=([^)]*)/,
++ // fixed for IE9, see #8346
++ rupper = /([A-Z]|^ms)/g,
++ rnumpx = /^-?\d+(?:px)?$/i,
++ rnum = /^-?\d/,
++ rrelNum = /^([\-+])=([\-+.\de]+)/,
++
++ cssShow = { position: "absolute", visibility: "hidden", display: "block" },
++ cssWidth = [ "Left", "Right" ],
++ cssHeight = [ "Top", "Bottom" ],
++ curCSS,
++
++ getComputedStyle,
++ currentStyle;
++
++jQuery.fn.css = function( name, value ) {
++ // Setting 'undefined' is a no-op
++ if ( arguments.length === 2 && value === undefined ) {
++ return this;
++ }
++
++ return jQuery.access( this, name, value, true, function( elem, name, value ) {
++ return value !== undefined ?
++ jQuery.style( elem, name, value ) :
++ jQuery.css( elem, name );
++ });
++};
++
++jQuery.extend({
++ // Add in style property hooks for overriding the default
++ // behavior of getting and setting a style property
++ cssHooks: {
++ opacity: {
++ get: function( elem, computed ) {
++ if ( computed ) {
++ // We should always get a number back from opacity
++ var ret = curCSS( elem, "opacity", "opacity" );
++ return ret === "" ? "1" : ret;
++
++ } else {
++ return elem.style.opacity;
++ }
++ }
++ }
++ },
++
++ // Exclude the following css properties to add px
++ cssNumber: {
++ "fillOpacity": true,
++ "fontWeight": true,
++ "lineHeight": true,
++ "opacity": true,
++ "orphans": true,
++ "widows": true,
++ "zIndex": true,
++ "zoom": true
++ },
++
++ // Add in properties whose names you wish to fix before
++ // setting or getting the value
++ cssProps: {
++ // normalize float css property
++ "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat"
++ },
++
++ // Get and set the style property on a DOM Node
++ style: function( elem, name, value, extra ) {
++ // Don't set styles on text and comment nodes
++ if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
++ return;
++ }
++
++ // Make sure that we're working with the right name
++ var ret, type, origName = jQuery.camelCase( name ),
++ style = elem.style, hooks = jQuery.cssHooks[ origName ];
++
++ name = jQuery.cssProps[ origName ] || origName;
++
++ // Check if we're setting a value
++ if ( value !== undefined ) {
++ type = typeof value;
++
++ // convert relative number strings (+= or -=) to relative numbers. #7345
++ if ( type === "string" && (ret = rrelNum.exec( value )) ) {
++ value = ( +( ret[1] + 1) * +ret[2] ) + parseFloat( jQuery.css( elem, name ) );
++ // Fixes bug #9237
++ type = "number";
++ }
++
++ // Make sure that NaN and null values aren't set. See: #7116
++ if ( value == null || type === "number" && isNaN( value ) ) {
++ return;
++ }
++
++ // If a number was passed in, add 'px' to the (except for certain CSS properties)
++ if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
++ value += "px";
++ }
++
++ // If a hook was provided, use that value, otherwise just set the specified value
++ if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value )) !== undefined ) {
++ // Wrapped to prevent IE from throwing errors when 'invalid' values are provided
++ // Fixes bug #5509
++ try {
++ style[ name ] = value;
++ } catch(e) {}
++ }
++
++ } else {
++ // If a hook was provided get the non-computed value from there
++ if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
++ return ret;
++ }
++
++ // Otherwise just get the value from the style object
++ return style[ name ];
++ }
++ },
++
++ css: function( elem, name, extra ) {
++ var ret, hooks;
++
++ // Make sure that we're working with the right name
++ name = jQuery.camelCase( name );
++ hooks = jQuery.cssHooks[ name ];
++ name = jQuery.cssProps[ name ] || name;
++
++ // cssFloat needs a special treatment
++ if ( name === "cssFloat" ) {
++ name = "float";
++ }
++
++ // If a hook was provided get the computed value from there
++ if ( hooks && "get" in hooks && (ret = hooks.get( elem, true, extra )) !== undefined ) {
++ return ret;
++
++ // Otherwise, if a way to get the computed value exists, use that
++ } else if ( curCSS ) {
++ return curCSS( elem, name );
++ }
++ },
++
++ // A method for quickly swapping in/out CSS properties to get correct calculations
++ swap: function( elem, options, callback ) {
++ var old = {};
++
++ // Remember the old values, and insert the new ones
++ for ( var name in options ) {
++ old[ name ] = elem.style[ name ];
++ elem.style[ name ] = options[ name ];
++ }
++
++ callback.call( elem );
++
++ // Revert the old values
++ for ( name in options ) {
++ elem.style[ name ] = old[ name ];
++ }
++ }
++});
++
++// DEPRECATED, Use jQuery.css() instead
++jQuery.curCSS = jQuery.css;
++
++jQuery.each(["height", "width"], function( i, name ) {
++ jQuery.cssHooks[ name ] = {
++ get: function( elem, computed, extra ) {
++ var val;
++
++ if ( computed ) {
++ if ( elem.offsetWidth !== 0 ) {
++ return getWH( elem, name, extra );
++ } else {
++ jQuery.swap( elem, cssShow, function() {
++ val = getWH( elem, name, extra );
++ });
++ }
++
++ return val;
++ }
++ },
++
++ set: function( elem, value ) {
++ if ( rnumpx.test( value ) ) {
++ // ignore negative width and height values #1599
++ value = parseFloat( value );
++
++ if ( value >= 0 ) {
++ return value + "px";
++ }
++
++ } else {
++ return value;
++ }
++ }
++ };
++});
++
++if ( !jQuery.support.opacity ) {
++ jQuery.cssHooks.opacity = {
++ get: function( elem, computed ) {
++ // IE uses filters for opacity
++ return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ?
++ ( parseFloat( RegExp.$1 ) / 100 ) + "" :
++ computed ? "1" : "";
++ },
++
++ set: function( elem, value ) {
++ var style = elem.style,
++ currentStyle = elem.currentStyle,
++ opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "",
++ filter = currentStyle && currentStyle.filter || style.filter || "";
++
++ // IE has trouble with opacity if it does not have layout
++ // Force it by setting the zoom level
++ style.zoom = 1;
++
++ // if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652
++ if ( value >= 1 && jQuery.trim( filter.replace( ralpha, "" ) ) === "" ) {
++
++ // Setting style.filter to null, "" & " " still leave "filter:" in the cssText
++ // if "filter:" is present at all, clearType is disabled, we want to avoid this
++ // style.removeAttribute is IE Only, but so apparently is this code path...
++ style.removeAttribute( "filter" );
++
++ // if there there is no filter style applied in a css rule, we are done
++ if ( currentStyle && !currentStyle.filter ) {
++ return;
++ }
++ }
++
++ // otherwise, set new filter values
++ style.filter = ralpha.test( filter ) ?
++ filter.replace( ralpha, opacity ) :
++ filter + " " + opacity;
++ }
++ };
++}
++
++jQuery(function() {
++ // This hook cannot be added until DOM ready because the support test
++ // for it is not run until after DOM ready
++ if ( !jQuery.support.reliableMarginRight ) {
++ jQuery.cssHooks.marginRight = {
++ get: function( elem, computed ) {
++ // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
++ // Work around by temporarily setting element display to inline-block
++ var ret;
++ jQuery.swap( elem, { "display": "inline-block" }, function() {
++ if ( computed ) {
++ ret = curCSS( elem, "margin-right", "marginRight" );
++ } else {
++ ret = elem.style.marginRight;
++ }
++ });
++ return ret;
++ }
++ };
++ }
++});
++
++if ( document.defaultView && document.defaultView.getComputedStyle ) {
++ getComputedStyle = function( elem, name ) {
++ var ret, defaultView, computedStyle;
++
++ name = name.replace( rupper, "-$1" ).toLowerCase();
++
++ if ( (defaultView = elem.ownerDocument.defaultView) &&
++ (computedStyle = defaultView.getComputedStyle( elem, null )) ) {
++ ret = computedStyle.getPropertyValue( name );
++ if ( ret === "" && !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) {
++ ret = jQuery.style( elem, name );
++ }
++ }
++
++ return ret;
++ };
++}
++
++if ( document.documentElement.currentStyle ) {
++ currentStyle = function( elem, name ) {
++ var left, rsLeft, uncomputed,
++ ret = elem.currentStyle && elem.currentStyle[ name ],
++ style = elem.style;
++
++ // Avoid setting ret to empty string here
++ // so we don't default to auto
++ if ( ret === null && style && (uncomputed = style[ name ]) ) {
++ ret = uncomputed;
++ }
++
++ // From the awesome hack by Dean Edwards
++ // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
++
++ // If we're not dealing with a regular pixel number
++ // but a number that has a weird ending, we need to convert it to pixels
++ if ( !rnumpx.test( ret ) && rnum.test( ret ) ) {
++
++ // Remember the original values
++ left = style.left;
++ rsLeft = elem.runtimeStyle && elem.runtimeStyle.left;
++
++ // Put in the new values to get a computed value out
++ if ( rsLeft ) {
++ elem.runtimeStyle.left = elem.currentStyle.left;
++ }
++ style.left = name === "fontSize" ? "1em" : ( ret || 0 );
++ ret = style.pixelLeft + "px";
++
++ // Revert the changed values
++ style.left = left;
++ if ( rsLeft ) {
++ elem.runtimeStyle.left = rsLeft;
++ }
++ }
++
++ return ret === "" ? "auto" : ret;
++ };
++}
++
++curCSS = getComputedStyle || currentStyle;
++
++function getWH( elem, name, extra ) {
++
++ // Start with offset property
++ var val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
++ which = name === "width" ? cssWidth : cssHeight,
++ i = 0,
++ len = which.length;
++
++ if ( val > 0 ) {
++ if ( extra !== "border" ) {
++ for ( ; i < len; i++ ) {
++ if ( !extra ) {
++ val -= parseFloat( jQuery.css( elem, "padding" + which[ i ] ) ) || 0;
++ }
++ if ( extra === "margin" ) {
++ val += parseFloat( jQuery.css( elem, extra + which[ i ] ) ) || 0;
++ } else {
++ val -= parseFloat( jQuery.css( elem, "border" + which[ i ] + "Width" ) ) || 0;
++ }
++ }
++ }
++
++ return val + "px";
++ }
++
++ // Fall back to computed then uncomputed css if necessary
++ val = curCSS( elem, name, name );
++ if ( val < 0 || val == null ) {
++ val = elem.style[ name ] || 0;
++ }
++ // Normalize "", auto, and prepare for extra
++ val = parseFloat( val ) || 0;
++
++ // Add padding, border, margin
++ if ( extra ) {
++ for ( ; i < len; i++ ) {
++ val += parseFloat( jQuery.css( elem, "padding" + which[ i ] ) ) || 0;
++ if ( extra !== "padding" ) {
++ val += parseFloat( jQuery.css( elem, "border" + which[ i ] + "Width" ) ) || 0;
++ }
++ if ( extra === "margin" ) {
++ val += parseFloat( jQuery.css( elem, extra + which[ i ] ) ) || 0;
++ }
++ }
++ }
++
++ return val + "px";
++}
++
++if ( jQuery.expr && jQuery.expr.filters ) {
++ jQuery.expr.filters.hidden = function( elem ) {
++ var width = elem.offsetWidth,
++ height = elem.offsetHeight;
++
++ return ( width === 0 && height === 0 ) || (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || jQuery.css( elem, "display" )) === "none");
++ };
++
++ jQuery.expr.filters.visible = function( elem ) {
++ return !jQuery.expr.filters.hidden( elem );
++ };
++}
++
++
++
++
++var r20 = /%20/g,
++ rbracket = /\[\]$/,
++ rCRLF = /\r?\n/g,
++ rhash = /#.*$/,
++ rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL
++ rinput = /^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,
++ // #7653, #8125, #8152: local protocol detection
++ rlocalProtocol = /^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,
++ rnoContent = /^(?:GET|HEAD)$/,
++ rprotocol = /^\/\//,
++ rquery = /\?/,
++ rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
++ rselectTextarea = /^(?:select|textarea)/i,
++ rspacesAjax = /\s+/,
++ rts = /([?&])_=[^&]*/,
++ rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,
++
++ // Keep a copy of the old load method
++ _load = jQuery.fn.load,
++
++ /* Prefilters
++ * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
++ * 2) These are called:
++ * - BEFORE asking for a transport
++ * - AFTER param serialization (s.data is a string if s.processData is true)
++ * 3) key is the dataType
++ * 4) the catchall symbol "*" can be used
++ * 5) execution will start with transport dataType and THEN continue down to "*" if needed
++ */
++ prefilters = {},
++
++ /* Transports bindings
++ * 1) key is the dataType
++ * 2) the catchall symbol "*" can be used
++ * 3) selection will start with transport dataType and THEN go to "*" if needed
++ */
++ transports = {},
++
++ // Document location
++ ajaxLocation,
++
++ // Document location segments
++ ajaxLocParts,
++
++ // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
++ allTypes = ["*/"] + ["*"];
++
++// #8138, IE may throw an exception when accessing
++// a field from window.location if document.domain has been set
++try {
++ ajaxLocation = location.href;
++} catch( e ) {
++ // Use the href attribute of an A element
++ // since IE will modify it given document.location
++ ajaxLocation = document.createElement( "a" );
++ ajaxLocation.href = "";
++ ajaxLocation = ajaxLocation.href;
++}
++
++// Segment location into parts
++ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
++
++// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
++function addToPrefiltersOrTransports( structure ) {
++
++ // dataTypeExpression is optional and defaults to "*"
++ return function( dataTypeExpression, func ) {
++
++ if ( typeof dataTypeExpression !== "string" ) {
++ func = dataTypeExpression;
++ dataTypeExpression = "*";
++ }
++
++ if ( jQuery.isFunction( func ) ) {
++ var dataTypes = dataTypeExpression.toLowerCase().split( rspacesAjax ),
++ i = 0,
++ length = dataTypes.length,
++ dataType,
++ list,
++ placeBefore;
++
++ // For each dataType in the dataTypeExpression
++ for ( ; i < length; i++ ) {
++ dataType = dataTypes[ i ];
++ // We control if we're asked to add before
++ // any existing element
++ placeBefore = /^\+/.test( dataType );
++ if ( placeBefore ) {
++ dataType = dataType.substr( 1 ) || "*";
++ }
++ list = structure[ dataType ] = structure[ dataType ] || [];
++ // then we add to the structure accordingly
++ list[ placeBefore ? "unshift" : "push" ]( func );
++ }
++ }
++ };
++}
++
++// Base inspection function for prefilters and transports
++function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR,
++ dataType /* internal */, inspected /* internal */ ) {
++
++ dataType = dataType || options.dataTypes[ 0 ];
++ inspected = inspected || {};
++
++ inspected[ dataType ] = true;
++
++ var list = structure[ dataType ],
++ i = 0,
++ length = list ? list.length : 0,
++ executeOnly = ( structure === prefilters ),
++ selection;
++
++ for ( ; i < length && ( executeOnly || !selection ); i++ ) {
++ selection = list[ i ]( options, originalOptions, jqXHR );
++ // If we got redirected to another dataType
++ // we try there if executing only and not done already
++ if ( typeof selection === "string" ) {
++ if ( !executeOnly || inspected[ selection ] ) {
++ selection = undefined;
++ } else {
++ options.dataTypes.unshift( selection );
++ selection = inspectPrefiltersOrTransports(
++ structure, options, originalOptions, jqXHR, selection, inspected );
++ }
++ }
++ }
++ // If we're only executing or nothing was selected
++ // we try the catchall dataType if not done already
++ if ( ( executeOnly || !selection ) && !inspected[ "*" ] ) {
++ selection = inspectPrefiltersOrTransports(
++ structure, options, originalOptions, jqXHR, "*", inspected );
++ }
++ // unnecessary when only executing (prefilters)
++ // but it'll be ignored by the caller in that case
++ return selection;
++}
++
++// A special extend for ajax options
++// that takes "flat" options (not to be deep extended)
++// Fixes #9887
++function ajaxExtend( target, src ) {
++ var key, deep,
++ flatOptions = jQuery.ajaxSettings.flatOptions || {};
++ for ( key in src ) {
++ if ( src[ key ] !== undefined ) {
++ ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ];
++ }
++ }
++ if ( deep ) {
++ jQuery.extend( true, target, deep );
++ }
++}
++
++jQuery.fn.extend({
++ load: function( url, params, callback ) {
++ if ( typeof url !== "string" && _load ) {
++ return _load.apply( this, arguments );
++
++ // Don't do a request if no elements are being requested
++ } else if ( !this.length ) {
++ return this;
++ }
++
++ var off = url.indexOf( " " );
++ if ( off >= 0 ) {
++ var selector = url.slice( off, url.length );
++ url = url.slice( 0, off );
++ }
++
++ // Default to a GET request
++ var type = "GET";
++
++ // If the second parameter was provided
++ if ( params ) {
++ // If it's a function
++ if ( jQuery.isFunction( params ) ) {
++ // We assume that it's the callback
++ callback = params;
++ params = undefined;
++
++ // Otherwise, build a param string
++ } else if ( typeof params === "object" ) {
++ params = jQuery.param( params, jQuery.ajaxSettings.traditional );
++ type = "POST";
++ }
++ }
++
++ var self = this;
++
++ // Request the remote document
++ jQuery.ajax({
++ url: url,
++ type: type,
++ dataType: "html",
++ data: params,
++ // Complete callback (responseText is used internally)
++ complete: function( jqXHR, status, responseText ) {
++ // Store the response as specified by the jqXHR object
++ responseText = jqXHR.responseText;
++ // If successful, inject the HTML into all the matched elements
++ if ( jqXHR.isResolved() ) {
++ // #4825: Get the actual response in case
++ // a dataFilter is present in ajaxSettings
++ jqXHR.done(function( r ) {
++ responseText = r;
++ });
++ // See if a selector was specified
++ self.html( selector ?
++ // Create a dummy div to hold the results
++ jQuery("<div>")
++ // inject the contents of the document in, removing the scripts
++ // to avoid any 'Permission Denied' errors in IE
++ .append(responseText.replace(rscript, ""))
++
++ // Locate the specified elements
++ .find(selector) :
++
++ // If not, just inject the full result
++ responseText );
++ }
++
++ if ( callback ) {
++ self.each( callback, [ responseText, status, jqXHR ] );
++ }
++ }
++ });
++
++ return this;
++ },
++
++ serialize: function() {
++ return jQuery.param( this.serializeArray() );
++ },
++
++ serializeArray: function() {
++ return this.map(function(){
++ return this.elements ? jQuery.makeArray( this.elements ) : this;
++ })
++ .filter(function(){
++ return this.name && !this.disabled &&
++ ( this.checked || rselectTextarea.test( this.nodeName ) ||
++ rinput.test( this.type ) );
++ })
++ .map(function( i, elem ){
++ var val = jQuery( this ).val();
++
++ return val == null ?
++ null :
++ jQuery.isArray( val ) ?
++ jQuery.map( val, function( val, i ){
++ return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
++ }) :
++ { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
++ }).get();
++ }
++});
++
++// Attach a bunch of functions for handling common AJAX events
++jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split( " " ), function( i, o ){
++ jQuery.fn[ o ] = function( f ){
++ return this.on( o, f );
++ };
++});
++
++jQuery.each( [ "get", "post" ], function( i, method ) {
++ jQuery[ method ] = function( url, data, callback, type ) {
++ // shift arguments if data argument was omitted
++ if ( jQuery.isFunction( data ) ) {
++ type = type || callback;
++ callback = data;
++ data = undefined;
++ }
++
++ return jQuery.ajax({
++ type: method,
++ url: url,
++ data: data,
++ success: callback,
++ dataType: type
++ });
++ };
++});
++
++jQuery.extend({
++
++ getScript: function( url, callback ) {
++ return jQuery.get( url, undefined, callback, "script" );
++ },
++
++ getJSON: function( url, data, callback ) {
++ return jQuery.get( url, data, callback, "json" );
++ },
++
++ // Creates a full fledged settings object into target
++ // with both ajaxSettings and settings fields.
++ // If target is omitted, writes into ajaxSettings.
++ ajaxSetup: function( target, settings ) {
++ if ( settings ) {
++ // Building a settings object
++ ajaxExtend( target, jQuery.ajaxSettings );
++ } else {
++ // Extending ajaxSettings
++ settings = target;
++ target = jQuery.ajaxSettings;
++ }
++ ajaxExtend( target, settings );
++ return target;
++ },
++
++ ajaxSettings: {
++ url: ajaxLocation,
++ isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
++ global: true,
++ type: "GET",
++ contentType: "application/x-www-form-urlencoded",
++ processData: true,
++ async: true,
++ /*
++ timeout: 0,
++ data: null,
++ dataType: null,
++ username: null,
++ password: null,
++ cache: null,
++ traditional: false,
++ headers: {},
++ */
++
++ accepts: {
++ xml: "application/xml, text/xml",
++ html: "text/html",
++ text: "text/plain",
++ json: "application/json, text/javascript",
++ "*": allTypes
++ },
++
++ contents: {
++ xml: /xml/,
++ html: /html/,
++ json: /json/
++ },
++
++ responseFields: {
++ xml: "responseXML",
++ text: "responseText"
++ },
++
++ // List of data converters
++ // 1) key format is "source_type destination_type" (a single space in-between)
++ // 2) the catchall symbol "*" can be used for source_type
++ converters: {
++
++ // Convert anything to text
++ "* text": window.String,
++
++ // Text to html (true = no transformation)
++ "text html": true,
++
++ // Evaluate text as a json expression
++ "text json": jQuery.parseJSON,
++
++ // Parse text as xml
++ "text xml": jQuery.parseXML
++ },
++
++ // For options that shouldn't be deep extended:
++ // you can add your own custom options here if
++ // and when you create one that shouldn't be
++ // deep extended (see ajaxExtend)
++ flatOptions: {
++ context: true,
++ url: true
++ }
++ },
++
++ ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
++ ajaxTransport: addToPrefiltersOrTransports( transports ),
++
++ // Main method
++ ajax: function( url, options ) {
++
++ // If url is an object, simulate pre-1.5 signature
++ if ( typeof url === "object" ) {
++ options = url;
++ url = undefined;
++ }
++
++ // Force options to be an object
++ options = options || {};
++
++ var // Create the final options object
++ s = jQuery.ajaxSetup( {}, options ),
++ // Callbacks context
++ callbackContext = s.context || s,
++ // Context for global events
++ // It's the callbackContext if one was provided in the options
++ // and if it's a DOM node or a jQuery collection
++ globalEventContext = callbackContext !== s &&
++ ( callbackContext.nodeType || callbackContext instanceof jQuery ) ?
++ jQuery( callbackContext ) : jQuery.event,
++ // Deferreds
++ deferred = jQuery.Deferred(),
++ completeDeferred = jQuery.Callbacks( "once memory" ),
++ // Status-dependent callbacks
++ statusCode = s.statusCode || {},
++ // ifModified key
++ ifModifiedKey,
++ // Headers (they are sent all at once)
++ requestHeaders = {},
++ requestHeadersNames = {},
++ // Response headers
++ responseHeadersString,
++ responseHeaders,
++ // transport
++ transport,
++ // timeout handle
++ timeoutTimer,
++ // Cross-domain detection vars
++ parts,
++ // The jqXHR state
++ state = 0,
++ // To know if global events are to be dispatched
++ fireGlobals,
++ // Loop variable
++ i,
++ // Fake xhr
++ jqXHR = {
++
++ readyState: 0,
++
++ // Caches the header
++ setRequestHeader: function( name, value ) {
++ if ( !state ) {
++ var lname = name.toLowerCase();
++ name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
++ requestHeaders[ name ] = value;
++ }
++ return this;
++ },
++
++ // Raw string
++ getAllResponseHeaders: function() {
++ return state === 2 ? responseHeadersString : null;
++ },
++
++ // Builds headers hashtable if needed
++ getResponseHeader: function( key ) {
++ var match;
++ if ( state === 2 ) {
++ if ( !responseHeaders ) {
++ responseHeaders = {};
++ while( ( match = rheaders.exec( responseHeadersString ) ) ) {
++ responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
++ }
++ }
++ match = responseHeaders[ key.toLowerCase() ];
++ }
++ return match === undefined ? null : match;
++ },
++
++ // Overrides response content-type header
++ overrideMimeType: function( type ) {
++ if ( !state ) {
++ s.mimeType = type;
++ }
++ return this;
++ },
++
++ // Cancel the request
++ abort: function( statusText ) {
++ statusText = statusText || "abort";
++ if ( transport ) {
++ transport.abort( statusText );
++ }
++ done( 0, statusText );
++ return this;
++ }
++ };
++
++ // Callback for when everything is done
++ // It is defined here because jslint complains if it is declared
++ // at the end of the function (which would be more logical and readable)
++ function done( status, nativeStatusText, responses, headers ) {
++
++ // Called once
++ if ( state === 2 ) {
++ return;
++ }
++
++ // State is "done" now
++ state = 2;
++
++ // Clear timeout if it exists
++ if ( timeoutTimer ) {
++ clearTimeout( timeoutTimer );
++ }
++
++ // Dereference transport for early garbage collection
++ // (no matter how long the jqXHR object will be used)
++ transport = undefined;
++
++ // Cache response headers
++ responseHeadersString = headers || "";
++
++ // Set readyState
++ jqXHR.readyState = status > 0 ? 4 : 0;
++
++ var isSuccess,
++ success,
++ error,
++ statusText = nativeStatusText,
++ response = responses ? ajaxHandleResponses( s, jqXHR, responses ) : undefined,
++ lastModified,
++ etag;
++
++ // If successful, handle type chaining
++ if ( status >= 200 && status < 300 || status === 304 ) {
++
++ // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
++ if ( s.ifModified ) {
++
++ if ( ( lastModified = jqXHR.getResponseHeader( "Last-Modified" ) ) ) {
++ jQuery.lastModified[ ifModifiedKey ] = lastModified;
++ }
++ if ( ( etag = jqXHR.getResponseHeader( "Etag" ) ) ) {
++ jQuery.etag[ ifModifiedKey ] = etag;
++ }
++ }
++
++ // If not modified
++ if ( status === 304 ) {
++
++ statusText = "notmodified";
++ isSuccess = true;
++
++ // If we have data
++ } else {
++
++ try {
++ success = ajaxConvert( s, response );
++ statusText = "success";
++ isSuccess = true;
++ } catch(e) {
++ // We have a parsererror
++ statusText = "parsererror";
++ error = e;
++ }
++ }
++ } else {
++ // We extract error from statusText
++ // then normalize statusText and status for non-aborts
++ error = statusText;
++ if ( !statusText || status ) {
++ statusText = "error";
++ if ( status < 0 ) {
++ status = 0;
++ }
++ }
++ }
++
++ // Set data for the fake xhr object
++ jqXHR.status = status;
++ jqXHR.statusText = "" + ( nativeStatusText || statusText );
++
++ // Success/Error
++ if ( isSuccess ) {
++ deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
++ } else {
++ deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
++ }
++
++ // Status-dependent callbacks
++ jqXHR.statusCode( statusCode );
++ statusCode = undefined;
++
++ if ( fireGlobals ) {
++ globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ),
++ [ jqXHR, s, isSuccess ? success : error ] );
++ }
++
++ // Complete
++ completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
++
++ if ( fireGlobals ) {
++ globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
++ // Handle the global AJAX counter
++ if ( !( --jQuery.active ) ) {
++ jQuery.event.trigger( "ajaxStop" );
++ }
++ }
++ }
++
++ // Attach deferreds
++ deferred.promise( jqXHR );
++ jqXHR.success = jqXHR.done;
++ jqXHR.error = jqXHR.fail;
++ jqXHR.complete = completeDeferred.add;
++
++ // Status-dependent callbacks
++ jqXHR.statusCode = function( map ) {
++ if ( map ) {
++ var tmp;
++ if ( state < 2 ) {
++ for ( tmp in map ) {
++ statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ];
++ }
++ } else {
++ tmp = map[ jqXHR.status ];
++ jqXHR.then( tmp, tmp );
++ }
++ }
++ return this;
++ };
++
++ // Remove hash character (#7531: and string promotion)
++ // Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
++ // We also use the url parameter if available
++ s.url = ( ( url || s.url ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
++
++ // Extract dataTypes list
++ s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( rspacesAjax );
++
++ // Determine if a cross-domain request is in order
++ if ( s.crossDomain == null ) {
++ parts = rurl.exec( s.url.toLowerCase() );
++ s.crossDomain = !!( parts &&
++ ( parts[ 1 ] != ajaxLocParts[ 1 ] || parts[ 2 ] != ajaxLocParts[ 2 ] ||
++ ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) !=
++ ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) )
++ );
++ }
++
++ // Convert data if not already a string
++ if ( s.data && s.processData && typeof s.data !== "string" ) {
++ s.data = jQuery.param( s.data, s.traditional );
++ }
++
++ // Apply prefilters
++ inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
++
++ // If request was aborted inside a prefiler, stop there
++ if ( state === 2 ) {
++ return false;
++ }
++
++ // We can fire global events as of now if asked to
++ fireGlobals = s.global;
++
++ // Uppercase the type
++ s.type = s.type.toUpperCase();
++
++ // Determine if request has content
++ s.hasContent = !rnoContent.test( s.type );
++
++ // Watch for a new set of requests
++ if ( fireGlobals && jQuery.active++ === 0 ) {
++ jQuery.event.trigger( "ajaxStart" );
++ }
++
++ // More options handling for requests with no content
++ if ( !s.hasContent ) {
++
++ // If data is available, append data to url
++ if ( s.data ) {
++ s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data;
++ // #9682: remove data so that it's not used in an eventual retry
++ delete s.data;
++ }
++
++ // Get ifModifiedKey before adding the anti-cache parameter
++ ifModifiedKey = s.url;
++
++ // Add anti-cache in url if needed
++ if ( s.cache === false ) {
++
++ var ts = jQuery.now(),
++ // try replacing _= if it is there
++ ret = s.url.replace( rts, "$1_=" + ts );
++
++ // if nothing was replaced, add timestamp to the end
++ s.url = ret + ( ( ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" );
++ }
++ }
++
++ // Set the correct header, if data is being sent
++ if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
++ jqXHR.setRequestHeader( "Content-Type", s.contentType );
++ }
++
++ // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
++ if ( s.ifModified ) {
++ ifModifiedKey = ifModifiedKey || s.url;
++ if ( jQuery.lastModified[ ifModifiedKey ] ) {
++ jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ ifModifiedKey ] );
++ }
++ if ( jQuery.etag[ ifModifiedKey ] ) {
++ jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ ifModifiedKey ] );
++ }
++ }
++
++ // Set the Accepts header for the server, depending on the dataType
++ jqXHR.setRequestHeader(
++ "Accept",
++ s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
++ s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
++ s.accepts[ "*" ]
++ );
++
++ // Check for headers option
++ for ( i in s.headers ) {
++ jqXHR.setRequestHeader( i, s.headers[ i ] );
++ }
++
++ // Allow custom headers/mimetypes and early abort
++ if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
++ // Abort if not done already
++ jqXHR.abort();
++ return false;
++
++ }
++
++ // Install callbacks on deferreds
++ for ( i in { success: 1, error: 1, complete: 1 } ) {
++ jqXHR[ i ]( s[ i ] );
++ }
++
++ // Get transport
++ transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
++
++ // If no transport, we auto-abort
++ if ( !transport ) {
++ done( -1, "No Transport" );
++ } else {
++ jqXHR.readyState = 1;
++ // Send global event
++ if ( fireGlobals ) {
++ globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
++ }
++ // Timeout
++ if ( s.async && s.timeout > 0 ) {
++ timeoutTimer = setTimeout( function(){
++ jqXHR.abort( "timeout" );
++ }, s.timeout );
++ }
++
++ try {
++ state = 1;
++ transport.send( requestHeaders, done );
++ } catch (e) {
++ // Propagate exception as error if not done
++ if ( state < 2 ) {
++ done( -1, e );
++ // Simply rethrow otherwise
++ } else {
++ throw e;
++ }
++ }
++ }
++
++ return jqXHR;
++ },
++
++ // Serialize an array of form elements or a set of
++ // key/values into a query string
++ param: function( a, traditional ) {
++ var s = [],
++ add = function( key, value ) {
++ // If value is a function, invoke it and return its value
++ value = jQuery.isFunction( value ) ? value() : value;
++ s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
++ };
++
++ // Set traditional to true for jQuery <= 1.3.2 behavior.
++ if ( traditional === undefined ) {
++ traditional = jQuery.ajaxSettings.traditional;
++ }
++
++ // If an array was passed in, assume that it is an array of form elements.
++ if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
++ // Serialize the form elements
++ jQuery.each( a, function() {
++ add( this.name, this.value );
++ });
++
++ } else {
++ // If traditional, encode the "old" way (the way 1.3.2 or older
++ // did it), otherwise encode params recursively.
++ for ( var prefix in a ) {
++ buildParams( prefix, a[ prefix ], traditional, add );
++ }
++ }
++
++ // Return the resulting serialization
++ return s.join( "&" ).replace( r20, "+" );
++ }
++});
++
++function buildParams( prefix, obj, traditional, add ) {
++ if ( jQuery.isArray( obj ) ) {
++ // Serialize array item.
++ jQuery.each( obj, function( i, v ) {
++ if ( traditional || rbracket.test( prefix ) ) {
++ // Treat each array item as a scalar.
++ add( prefix, v );
++
++ } else {
++ // If array item is non-scalar (array or object), encode its
++ // numeric index to resolve deserialization ambiguity issues.
++ // Note that rack (as of 1.0.0) can't currently deserialize
++ // nested arrays properly, and attempting to do so may cause
++ // a server error. Possible fixes are to modify rack's
++ // deserialization algorithm or to provide an option or flag
++ // to force array serialization to be shallow.
++ buildParams( prefix + "[" + ( typeof v === "object" || jQuery.isArray(v) ? i : "" ) + "]", v, traditional, add );
++ }
++ });
++
++ } else if ( !traditional && obj != null && typeof obj === "object" ) {
++ // Serialize object item.
++ for ( var name in obj ) {
++ buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
++ }
++
++ } else {
++ // Serialize scalar item.
++ add( prefix, obj );
++ }
++}
++
++// This is still on the jQuery object... for now
++// Want to move this to jQuery.ajax some day
++jQuery.extend({
++
++ // Counter for holding the number of active queries
++ active: 0,
++
++ // Last-Modified header cache for next request
++ lastModified: {},
++ etag: {}
++
++});
++
++/* Handles responses to an ajax request:
++ * - sets all responseXXX fields accordingly
++ * - finds the right dataType (mediates between content-type and expected dataType)
++ * - returns the corresponding response
++ */
++function ajaxHandleResponses( s, jqXHR, responses ) {
++
++ var contents = s.contents,
++ dataTypes = s.dataTypes,
++ responseFields = s.responseFields,
++ ct,
++ type,
++ finalDataType,
++ firstDataType;
++
++ // Fill responseXXX fields
++ for ( type in responseFields ) {
++ if ( type in responses ) {
++ jqXHR[ responseFields[type] ] = responses[ type ];
++ }
++ }
++
++ // Remove auto dataType and get content-type in the process
++ while( dataTypes[ 0 ] === "*" ) {
++ dataTypes.shift();
++ if ( ct === undefined ) {
++ ct = s.mimeType || jqXHR.getResponseHeader( "content-type" );
++ }
++ }
++
++ // Check if we're dealing with a known content-type
++ if ( ct ) {
++ for ( type in contents ) {
++ if ( contents[ type ] && contents[ type ].test( ct ) ) {
++ dataTypes.unshift( type );
++ break;
++ }
++ }
++ }
++
++ // Check to see if we have a response for the expected dataType
++ if ( dataTypes[ 0 ] in responses ) {
++ finalDataType = dataTypes[ 0 ];
++ } else {
++ // Try convertible dataTypes
++ for ( type in responses ) {
++ if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
++ finalDataType = type;
++ break;
++ }
++ if ( !firstDataType ) {
++ firstDataType = type;
++ }
++ }
++ // Or just use first one
++ finalDataType = finalDataType || firstDataType;
++ }
++
++ // If we found a dataType
++ // We add the dataType to the list if needed
++ // and return the corresponding response
++ if ( finalDataType ) {
++ if ( finalDataType !== dataTypes[ 0 ] ) {
++ dataTypes.unshift( finalDataType );
++ }
++ return responses[ finalDataType ];
++ }
++}
++
++// Chain conversions given the request and the original response
++function ajaxConvert( s, response ) {
++
++ // Apply the dataFilter if provided
++ if ( s.dataFilter ) {
++ response = s.dataFilter( response, s.dataType );
++ }
++
++ var dataTypes = s.dataTypes,
++ converters = {},
++ i,
++ key,
++ length = dataTypes.length,
++ tmp,
++ // Current and previous dataTypes
++ current = dataTypes[ 0 ],
++ prev,
++ // Conversion expression
++ conversion,
++ // Conversion function
++ conv,
++ // Conversion functions (transitive conversion)
++ conv1,
++ conv2;
++
++ // For each dataType in the chain
++ for ( i = 1; i < length; i++ ) {
++
++ // Create converters map
++ // with lowercased keys
++ if ( i === 1 ) {
++ for ( key in s.converters ) {
++ if ( typeof key === "string" ) {
++ converters[ key.toLowerCase() ] = s.converters[ key ];
++ }
++ }
++ }
++
++ // Get the dataTypes
++ prev = current;
++ current = dataTypes[ i ];
++
++ // If current is auto dataType, update it to prev
++ if ( current === "*" ) {
++ current = prev;
++ // If no auto and dataTypes are actually different
++ } else if ( prev !== "*" && prev !== current ) {
++
++ // Get the converter
++ conversion = prev + " " + current;
++ conv = converters[ conversion ] || converters[ "* " + current ];
++
++ // If there is no direct converter, search transitively
++ if ( !conv ) {
++ conv2 = undefined;
++ for ( conv1 in converters ) {
++ tmp = conv1.split( " " );
++ if ( tmp[ 0 ] === prev || tmp[ 0 ] === "*" ) {
++ conv2 = converters[ tmp[1] + " " + current ];
++ if ( conv2 ) {
++ conv1 = converters[ conv1 ];
++ if ( conv1 === true ) {
++ conv = conv2;
++ } else if ( conv2 === true ) {
++ conv = conv1;
++ }
++ break;
++ }
++ }
++ }
++ }
++ // If we found no converter, dispatch an error
++ if ( !( conv || conv2 ) ) {
++ jQuery.error( "No conversion from " + conversion.replace(" "," to ") );
++ }
++ // If found converter is not an equivalence
++ if ( conv !== true ) {
++ // Convert with 1 or 2 converters accordingly
++ response = conv ? conv( response ) : conv2( conv1(response) );
++ }
++ }
++ }
++ return response;
++}
++
++
++
++
++var jsc = jQuery.now(),
++ jsre = /(\=)\?(&|$)|\?\?/i;
++
++// Default jsonp settings
++jQuery.ajaxSetup({
++ jsonp: "callback",
++ jsonpCallback: function() {
++ return jQuery.expando + "_" + ( jsc++ );
++ }
++});
++
++// Detect, normalize options and install callbacks for jsonp requests
++jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
++
++ var inspectData = s.contentType === "application/x-www-form-urlencoded" &&
++ ( typeof s.data === "string" );
++
++ if ( s.dataTypes[ 0 ] === "jsonp" ||
++ s.jsonp !== false && ( jsre.test( s.url ) ||
++ inspectData && jsre.test( s.data ) ) ) {
++
++ var responseContainer,
++ jsonpCallback = s.jsonpCallback =
++ jQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.jsonpCallback,
++ previous = window[ jsonpCallback ],
++ url = s.url,
++ data = s.data,
++ replace = "$1" + jsonpCallback + "$2";
++
++ if ( s.jsonp !== false ) {
++ url = url.replace( jsre, replace );
++ if ( s.url === url ) {
++ if ( inspectData ) {
++ data = data.replace( jsre, replace );
++ }
++ if ( s.data === data ) {
++ // Add callback manually
++ url += (/\?/.test( url ) ? "&" : "?") + s.jsonp + "=" + jsonpCallback;
++ }
++ }
++ }
++
++ s.url = url;
++ s.data = data;
++
++ // Install callback
++ window[ jsonpCallback ] = function( response ) {
++ responseContainer = [ response ];
++ };
++
++ // Clean-up function
++ jqXHR.always(function() {
++ // Set callback back to previous value
++ window[ jsonpCallback ] = previous;
++ // Call if it was a function and we have a response
++ if ( responseContainer && jQuery.isFunction( previous ) ) {
++ window[ jsonpCallback ]( responseContainer[ 0 ] );
++ }
++ });
++
++ // Use data converter to retrieve json after script execution
++ s.converters["script json"] = function() {
++ if ( !responseContainer ) {
++ jQuery.error( jsonpCallback + " was not called" );
++ }
++ return responseContainer[ 0 ];
++ };
++
++ // force json dataType
++ s.dataTypes[ 0 ] = "json";
++
++ // Delegate to script
++ return "script";
++ }
++});
++
++
++
++
++// Install script dataType
++jQuery.ajaxSetup({
++ accepts: {
++ script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
++ },
++ contents: {
++ script: /javascript|ecmascript/
++ },
++ converters: {
++ "text script": function( text ) {
++ jQuery.globalEval( text );
++ return text;
++ }
++ }
++});
++
++// Handle cache's special case and global
++jQuery.ajaxPrefilter( "script", function( s ) {
++ if ( s.cache === undefined ) {
++ s.cache = false;
++ }
++ if ( s.crossDomain ) {
++ s.type = "GET";
++ s.global = false;
++ }
++});
++
++// Bind script tag hack transport
++jQuery.ajaxTransport( "script", function(s) {
++
++ // This transport only deals with cross domain requests
++ if ( s.crossDomain ) {
++
++ var script,
++ head = document.head || document.getElementsByTagName( "head" )[0] || document.documentElement;
++
++ return {
++
++ send: function( _, callback ) {
++
++ script = document.createElement( "script" );
++
++ script.async = "async";
++
++ if ( s.scriptCharset ) {
++ script.charset = s.scriptCharset;
++ }
++
++ script.src = s.url;
++
++ // Attach handlers for all browsers
++ script.onload = script.onreadystatechange = function( _, isAbort ) {
++
++ if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
++
++ // Handle memory leak in IE
++ script.onload = script.onreadystatechange = null;
++
++ // Remove the script
++ if ( head && script.parentNode ) {
++ head.removeChild( script );
++ }
++
++ // Dereference the script
++ script = undefined;
++
++ // Callback if not abort
++ if ( !isAbort ) {
++ callback( 200, "success" );
++ }
++ }
++ };
++ // Use insertBefore instead of appendChild to circumvent an IE6 bug.
++ // This arises when a base node is used (#2709 and #4378).
++ head.insertBefore( script, head.firstChild );
++ },
++
++ abort: function() {
++ if ( script ) {
++ script.onload( 0, 1 );
++ }
++ }
++ };
++ }
++});
++
++
++
++
++var // #5280: Internet Explorer will keep connections alive if we don't abort on unload
++ xhrOnUnloadAbort = window.ActiveXObject ? function() {
++ // Abort all pending requests
++ for ( var key in xhrCallbacks ) {
++ xhrCallbacks[ key ]( 0, 1 );
++ }
++ } : false,
++ xhrId = 0,
++ xhrCallbacks;
++
++// Functions to create xhrs
++function createStandardXHR() {
++ try {
++ return new window.XMLHttpRequest();
++ } catch( e ) {}
++}
++
++function createActiveXHR() {
++ try {
++ return new window.ActiveXObject( "Microsoft.XMLHTTP" );
++ } catch( e ) {}
++}
++
++// Create the request object
++// (This is still attached to ajaxSettings for backward compatibility)
++jQuery.ajaxSettings.xhr = window.ActiveXObject ?
++ /* Microsoft failed to properly
++ * implement the XMLHttpRequest in IE7 (can't request local files),
++ * so we use the ActiveXObject when it is available
++ * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
++ * we need a fallback.
++ */
++ function() {
++ return !this.isLocal && createStandardXHR() || createActiveXHR();
++ } :
++ // For all other browsers, use the standard XMLHttpRequest object
++ createStandardXHR;
++
++// Determine support properties
++(function( xhr ) {
++ jQuery.extend( jQuery.support, {
++ ajax: !!xhr,
++ cors: !!xhr && ( "withCredentials" in xhr )
++ });
++})( jQuery.ajaxSettings.xhr() );
++
++// Create transport if the browser can provide an xhr
++if ( jQuery.support.ajax ) {
++
++ jQuery.ajaxTransport(function( s ) {
++ // Cross domain only allowed if supported through XMLHttpRequest
++ if ( !s.crossDomain || jQuery.support.cors ) {
++
++ var callback;
++
++ return {
++ send: function( headers, complete ) {
++
++ // Get a new xhr
++ var xhr = s.xhr(),
++ handle,
++ i;
++
++ // Open the socket
++ // Passing null username, generates a login popup on Opera (#2865)
++ if ( s.username ) {
++ xhr.open( s.type, s.url, s.async, s.username, s.password );
++ } else {
++ xhr.open( s.type, s.url, s.async );
++ }
++
++ // Apply custom fields if provided
++ if ( s.xhrFields ) {
++ for ( i in s.xhrFields ) {
++ xhr[ i ] = s.xhrFields[ i ];
++ }
++ }
++
++ // Override mime type if needed
++ if ( s.mimeType && xhr.overrideMimeType ) {
++ xhr.overrideMimeType( s.mimeType );
++ }
++
++ // X-Requested-With header
++ // For cross-domain requests, seeing as conditions for a preflight are
++ // akin to a jigsaw puzzle, we simply never set it to be sure.
++ // (it can always be set on a per-request basis or even using ajaxSetup)
++ // For same-domain requests, won't change header if already provided.
++ if ( !s.crossDomain && !headers["X-Requested-With"] ) {
++ headers[ "X-Requested-With" ] = "XMLHttpRequest";
++ }
++
++ // Need an extra try/catch for cross domain requests in Firefox 3
++ try {
++ for ( i in headers ) {
++ xhr.setRequestHeader( i, headers[ i ] );
++ }
++ } catch( _ ) {}
++
++ // Do send the request
++ // This may raise an exception which is actually
++ // handled in jQuery.ajax (so no try/catch here)
++ xhr.send( ( s.hasContent && s.data ) || null );
++
++ // Listener
++ callback = function( _, isAbort ) {
++
++ var status,
++ statusText,
++ responseHeaders,
++ responses,
++ xml;
++
++ // Firefox throws exceptions when accessing properties
++ // of an xhr when a network error occured
++ // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE)
++ try {
++
++ // Was never called and is aborted or complete
++ if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
++
++ // Only called once
++ callback = undefined;
++
++ // Do not keep as active anymore
++ if ( handle ) {
++ xhr.onreadystatechange = jQuery.noop;
++ if ( xhrOnUnloadAbort ) {
++ delete xhrCallbacks[ handle ];
++ }
++ }
++
++ // If it's an abort
++ if ( isAbort ) {
++ // Abort it manually if needed
++ if ( xhr.readyState !== 4 ) {
++ xhr.abort();
++ }
++ } else {
++ status = xhr.status;
++ responseHeaders = xhr.getAllResponseHeaders();
++ responses = {};
++ xml = xhr.responseXML;
++
++ // Construct response list
++ if ( xml && xml.documentElement /* #4958 */ ) {
++ responses.xml = xml;
++ }
++ responses.text = xhr.responseText;
++
++ // Firefox throws an exception when accessing
++ // statusText for faulty cross-domain requests
++ try {
++ statusText = xhr.statusText;
++ } catch( e ) {
++ // We normalize with Webkit giving an empty statusText
++ statusText = "";
++ }
++
++ // Filter status for non standard behaviors
++
++ // If the request is local and we have data: assume a success
++ // (success with no data won't get notified, that's the best we
++ // can do given current implementations)
++ if ( !status && s.isLocal && !s.crossDomain ) {
++ status = responses.text ? 200 : 404;
++ // IE - #1450: sometimes returns 1223 when it should be 204
++ } else if ( status === 1223 ) {
++ status = 204;
++ }
++ }
++ }
++ } catch( firefoxAccessException ) {
++ if ( !isAbort ) {
++ complete( -1, firefoxAccessException );
++ }
++ }
++
++ // Call complete if needed
++ if ( responses ) {
++ complete( status, statusText, responses, responseHeaders );
++ }
++ };
++
++ // if we're in sync mode or it's in cache
++ // and has been retrieved directly (IE6 & IE7)
++ // we need to manually fire the callback
++ if ( !s.async || xhr.readyState === 4 ) {
++ callback();
++ } else {
++ handle = ++xhrId;
++ if ( xhrOnUnloadAbort ) {
++ // Create the active xhrs callbacks list if needed
++ // and attach the unload handler
++ if ( !xhrCallbacks ) {
++ xhrCallbacks = {};
++ jQuery( window ).unload( xhrOnUnloadAbort );
++ }
++ // Add to list of active xhrs callbacks
++ xhrCallbacks[ handle ] = callback;
++ }
++ xhr.onreadystatechange = callback;
++ }
++ },
++
++ abort: function() {
++ if ( callback ) {
++ callback(0,1);
++ }
++ }
++ };
++ }
++ });
++}
++
++
++
++
++var elemdisplay = {},
++ iframe, iframeDoc,
++ rfxtypes = /^(?:toggle|show|hide)$/,
++ rfxnum = /^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,
++ timerId,
++ fxAttrs = [
++ // height animations
++ [ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
++ // width animations
++ [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
++ // opacity animations
++ [ "opacity" ]
++ ],
++ fxNow;
++
++jQuery.fn.extend({
++ show: function( speed, easing, callback ) {
++ var elem, display;
++
++ if ( speed || speed === 0 ) {
++ return this.animate( genFx("show", 3), speed, easing, callback );
++
++ } else {
++ for ( var i = 0, j = this.length; i < j; i++ ) {
++ elem = this[ i ];
++
++ if ( elem.style ) {
++ display = elem.style.display;
++
++ // Reset the inline display of this element to learn if it is
++ // being hidden by cascaded rules or not
++ if ( !jQuery._data(elem, "olddisplay") && display === "none" ) {
++ display = elem.style.display = "";
++ }
++
++ // Set elements which have been overridden with display: none
++ // in a stylesheet to whatever the default browser style is
++ // for such an element
++ if ( display === "" && jQuery.css(elem, "display") === "none" ) {
++ jQuery._data( elem, "olddisplay", defaultDisplay(elem.nodeName) );
++ }
++ }
++ }
++
++ // Set the display of most of the elements in a second loop
++ // to avoid the constant reflow
++ for ( i = 0; i < j; i++ ) {
++ elem = this[ i ];
++
++ if ( elem.style ) {
++ display = elem.style.display;
++
++ if ( display === "" || display === "none" ) {
++ elem.style.display = jQuery._data( elem, "olddisplay" ) || "";
++ }
++ }
++ }
++
++ return this;
++ }
++ },
++
++ hide: function( speed, easing, callback ) {
++ if ( speed || speed === 0 ) {
++ return this.animate( genFx("hide", 3), speed, easing, callback);
++
++ } else {
++ var elem, display,
++ i = 0,
++ j = this.length;
++
++ for ( ; i < j; i++ ) {
++ elem = this[i];
++ if ( elem.style ) {
++ display = jQuery.css( elem, "display" );
++
++ if ( display !== "none" && !jQuery._data( elem, "olddisplay" ) ) {
++ jQuery._data( elem, "olddisplay", display );
++ }
++ }
++ }
++
++ // Set the display of the elements in a second loop
++ // to avoid the constant reflow
++ for ( i = 0; i < j; i++ ) {
++ if ( this[i].style ) {
++ this[i].style.display = "none";
++ }
++ }
++
++ return this;
++ }
++ },
++
++ // Save the old toggle function
++ _toggle: jQuery.fn.toggle,
++
++ toggle: function( fn, fn2, callback ) {
++ var bool = typeof fn === "boolean";
++
++ if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) {
++ this._toggle.apply( this, arguments );
++
++ } else if ( fn == null || bool ) {
++ this.each(function() {
++ var state = bool ? fn : jQuery(this).is(":hidden");
++ jQuery(this)[ state ? "show" : "hide" ]();
++ });
++
++ } else {
++ this.animate(genFx("toggle", 3), fn, fn2, callback);
++ }
++
++ return this;
++ },
++
++ fadeTo: function( speed, to, easing, callback ) {
++ return this.filter(":hidden").css("opacity", 0).show().end()
++ .animate({opacity: to}, speed, easing, callback);
++ },
++
++ animate: function( prop, speed, easing, callback ) {
++ var optall = jQuery.speed( speed, easing, callback );
++
++ if ( jQuery.isEmptyObject( prop ) ) {
++ return this.each( optall.complete, [ false ] );
++ }
++
++ // Do not change referenced properties as per-property easing will be lost
++ prop = jQuery.extend( {}, prop );
++
++ function doAnimation() {
++ // XXX 'this' does not always have a nodeName when running the
++ // test suite
++
++ if ( optall.queue === false ) {
++ jQuery._mark( this );
++ }
++
++ var opt = jQuery.extend( {}, optall ),
++ isElement = this.nodeType === 1,
++ hidden = isElement && jQuery(this).is(":hidden"),
++ name, val, p, e,
++ parts, start, end, unit,
++ method;
++
++ // will store per property easing and be used to determine when an animation is complete
++ opt.animatedProperties = {};
++
++ for ( p in prop ) {
++
++ // property name normalization
++ name = jQuery.camelCase( p );
++ if ( p !== name ) {
++ prop[ name ] = prop[ p ];
++ delete prop[ p ];
++ }
++
++ val = prop[ name ];
++
++ // easing resolution: per property > opt.specialEasing > opt.easing > 'swing' (default)
++ if ( jQuery.isArray( val ) ) {
++ opt.animatedProperties[ name ] = val[ 1 ];
++ val = prop[ name ] = val[ 0 ];
++ } else {
++ opt.animatedProperties[ name ] = opt.specialEasing && opt.specialEasing[ name ] || opt.easing || 'swing';
++ }
++
++ if ( val === "hide" && hidden || val === "show" && !hidden ) {
++ return opt.complete.call( this );
++ }
++
++ if ( isElement && ( name === "height" || name === "width" ) ) {
++ // Make sure that nothing sneaks out
++ // Record all 3 overflow attributes because IE does not
++ // change the overflow attribute when overflowX and
++ // overflowY are set to the same value
++ opt.overflow = [ this.style.overflow, this.style.overflowX, this.style.overflowY ];
++
++ // Set display property to inline-block for height/width
++ // animations on inline elements that are having width/height animated
++ if ( jQuery.css( this, "display" ) === "inline" &&
++ jQuery.css( this, "float" ) === "none" ) {
++
++ // inline-level elements accept inline-block;
++ // block-level elements need to be inline with layout
++ if ( !jQuery.support.inlineBlockNeedsLayout || defaultDisplay( this.nodeName ) === "inline" ) {
++ this.style.display = "inline-block";
++
++ } else {
++ this.style.zoom = 1;
++ }
++ }
++ }
++ }
++
++ if ( opt.overflow != null ) {
++ this.style.overflow = "hidden";
++ }
++
++ for ( p in prop ) {
++ e = new jQuery.fx( this, opt, p );
++ val = prop[ p ];
++
++ if ( rfxtypes.test( val ) ) {
++
++ // Tracks whether to show or hide based on private
++ // data attached to the element
++ method = jQuery._data( this, "toggle" + p ) || ( val === "toggle" ? hidden ? "show" : "hide" : 0 );
++ if ( method ) {
++ jQuery._data( this, "toggle" + p, method === "show" ? "hide" : "show" );
++ e[ method ]();
++ } else {
++ e[ val ]();
++ }
++
++ } else {
++ parts = rfxnum.exec( val );
++ start = e.cur();
++
++ if ( parts ) {
++ end = parseFloat( parts[2] );
++ unit = parts[3] || ( jQuery.cssNumber[ p ] ? "" : "px" );
++
++ // We need to compute starting value
++ if ( unit !== "px" ) {
++ jQuery.style( this, p, (end || 1) + unit);
++ start = ( (end || 1) / e.cur() ) * start;
++ jQuery.style( this, p, start + unit);
++ }
++
++ // If a +=/-= token was provided, we're doing a relative animation
++ if ( parts[1] ) {
++ end = ( (parts[ 1 ] === "-=" ? -1 : 1) * end ) + start;
++ }
++
++ e.custom( start, end, unit );
++
++ } else {
++ e.custom( start, val, "" );
++ }
++ }
++ }
++
++ // For JS strict compliance
++ return true;
++ }
++
++ return optall.queue === false ?
++ this.each( doAnimation ) :
++ this.queue( optall.queue, doAnimation );
++ },
++
++ stop: function( type, clearQueue, gotoEnd ) {
++ if ( typeof type !== "string" ) {
++ gotoEnd = clearQueue;
++ clearQueue = type;
++ type = undefined;
++ }
++ if ( clearQueue && type !== false ) {
++ this.queue( type || "fx", [] );
++ }
++
++ return this.each(function() {
++ var index,
++ hadTimers = false,
++ timers = jQuery.timers,
++ data = jQuery._data( this );
++
++ // clear marker counters if we know they won't be
++ if ( !gotoEnd ) {
++ jQuery._unmark( true, this );
++ }
++
++ function stopQueue( elem, data, index ) {
++ var hooks = data[ index ];
++ jQuery.removeData( elem, index, true );
++ hooks.stop( gotoEnd );
++ }
++
++ if ( type == null ) {
++ for ( index in data ) {
++ if ( data[ index ] && data[ index ].stop && index.indexOf(".run") === index.length - 4 ) {
++ stopQueue( this, data, index );
++ }
++ }
++ } else if ( data[ index = type + ".run" ] && data[ index ].stop ){
++ stopQueue( this, data, index );
++ }
++
++ for ( index = timers.length; index--; ) {
++ if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
++ if ( gotoEnd ) {
++
++ // force the next step to be the last
++ timers[ index ]( true );
++ } else {
++ timers[ index ].saveState();
++ }
++ hadTimers = true;
++ timers.splice( index, 1 );
++ }
++ }
++
++ // start the next in the queue if the last step wasn't forced
++ // timers currently will call their complete callbacks, which will dequeue
++ // but only if they were gotoEnd
++ if ( !( gotoEnd && hadTimers ) ) {
++ jQuery.dequeue( this, type );
++ }
++ });
++ }
++
++});
++
++// Animations created synchronously will run synchronously
++function createFxNow() {
++ setTimeout( clearFxNow, 0 );
++ return ( fxNow = jQuery.now() );
++}
++
++function clearFxNow() {
++ fxNow = undefined;
++}
++
++// Generate parameters to create a standard animation
++function genFx( type, num ) {
++ var obj = {};
++
++ jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice( 0, num )), function() {
++ obj[ this ] = type;
++ });
++
++ return obj;
++}
++
++// Generate shortcuts for custom animations
++jQuery.each({
++ slideDown: genFx( "show", 1 ),
++ slideUp: genFx( "hide", 1 ),
++ slideToggle: genFx( "toggle", 1 ),
++ fadeIn: { opacity: "show" },
++ fadeOut: { opacity: "hide" },
++ fadeToggle: { opacity: "toggle" }
++}, function( name, props ) {
++ jQuery.fn[ name ] = function( speed, easing, callback ) {
++ return this.animate( props, speed, easing, callback );
++ };
++});
++
++jQuery.extend({
++ speed: function( speed, easing, fn ) {
++ var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
++ complete: fn || !fn && easing ||
++ jQuery.isFunction( speed ) && speed,
++ duration: speed,
++ easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
++ };
++
++ opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
++ opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
++
++ // normalize opt.queue - true/undefined/null -> "fx"
++ if ( opt.queue == null || opt.queue === true ) {
++ opt.queue = "fx";
++ }
++
++ // Queueing
++ opt.old = opt.complete;
++
++ opt.complete = function( noUnmark ) {
++ if ( jQuery.isFunction( opt.old ) ) {
++ opt.old.call( this );
++ }
++
++ if ( opt.queue ) {
++ jQuery.dequeue( this, opt.queue );
++ } else if ( noUnmark !== false ) {
++ jQuery._unmark( this );
++ }
++ };
++
++ return opt;
++ },
++
++ easing: {
++ linear: function( p, n, firstNum, diff ) {
++ return firstNum + diff * p;
++ },
++ swing: function( p, n, firstNum, diff ) {
++ return ( ( -Math.cos( p*Math.PI ) / 2 ) + 0.5 ) * diff + firstNum;
++ }
++ },
++
++ timers: [],
++
++ fx: function( elem, options, prop ) {
++ this.options = options;
++ this.elem = elem;
++ this.prop = prop;
++
++ options.orig = options.orig || {};
++ }
++
++});
++
++jQuery.fx.prototype = {
++ // Simple function for setting a style value
++ update: function() {
++ if ( this.options.step ) {
++ this.options.step.call( this.elem, this.now, this );
++ }
++
++ ( jQuery.fx.step[ this.prop ] || jQuery.fx.step._default )( this );
++ },
++
++ // Get the current size
++ cur: function() {
++ if ( this.elem[ this.prop ] != null && (!this.elem.style || this.elem.style[ this.prop ] == null) ) {
++ return this.elem[ this.prop ];
++ }
++
++ var parsed,
++ r = jQuery.css( this.elem, this.prop );
++ // Empty strings, null, undefined and "auto" are converted to 0,
++ // complex values such as "rotate(1rad)" are returned as is,
++ // simple values such as "10px" are parsed to Float.
++ return isNaN( parsed = parseFloat( r ) ) ? !r || r === "auto" ? 0 : r : parsed;
++ },
++
++ // Start an animation from one number to another
++ custom: function( from, to, unit ) {
++ var self = this,
++ fx = jQuery.fx;
++
++ this.startTime = fxNow || createFxNow();
++ this.end = to;
++ this.now = this.start = from;
++ this.pos = this.state = 0;
++ this.unit = unit || this.unit || ( jQuery.cssNumber[ this.prop ] ? "" : "px" );
++
++ function t( gotoEnd ) {
++ return self.step( gotoEnd );
++ }
++
++ t.queue = this.options.queue;
++ t.elem = this.elem;
++ t.saveState = function() {
++ if ( self.options.hide && jQuery._data( self.elem, "fxshow" + self.prop ) === undefined ) {
++ jQuery._data( self.elem, "fxshow" + self.prop, self.start );
++ }
++ };
++
++ if ( t() && jQuery.timers.push(t) && !timerId ) {
++ timerId = setInterval( fx.tick, fx.interval );
++ }
++ },
++
++ // Simple 'show' function
++ show: function() {
++ var dataShow = jQuery._data( this.elem, "fxshow" + this.prop );
++
++ // Remember where we started, so that we can go back to it later
++ this.options.orig[ this.prop ] = dataShow || jQuery.style( this.elem, this.prop );
++ this.options.show = true;
++
++ // Begin the animation
++ // Make sure that we start at a small width/height to avoid any flash of content
++ if ( dataShow !== undefined ) {
++ // This show is picking up where a previous hide or show left off
++ this.custom( this.cur(), dataShow );
++ } else {
++ this.custom( this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur() );
++ }
++
++ // Start by showing the element
++ jQuery( this.elem ).show();
++ },
++
++ // Simple 'hide' function
++ hide: function() {
++ // Remember where we started, so that we can go back to it later
++ this.options.orig[ this.prop ] = jQuery._data( this.elem, "fxshow" + this.prop ) || jQuery.style( this.elem, this.prop );
++ this.options.hide = true;
++
++ // Begin the animation
++ this.custom( this.cur(), 0 );
++ },
++
++ // Each step of an animation
++ step: function( gotoEnd ) {
++ var p, n, complete,
++ t = fxNow || createFxNow(),
++ done = true,
++ elem = this.elem,
++ options = this.options;
++
++ if ( gotoEnd || t >= options.duration + this.startTime ) {
++ this.now = this.end;
++ this.pos = this.state = 1;
++ this.update();
++
++ options.animatedProperties[ this.prop ] = true;
++
++ for ( p in options.animatedProperties ) {
++ if ( options.animatedProperties[ p ] !== true ) {
++ done = false;
++ }
++ }
++
++ if ( done ) {
++ // Reset the overflow
++ if ( options.overflow != null && !jQuery.support.shrinkWrapBlocks ) {
++
++ jQuery.each( [ "", "X", "Y" ], function( index, value ) {
++ elem.style[ "overflow" + value ] = options.overflow[ index ];
++ });
++ }
++
++ // Hide the element if the "hide" operation was done
++ if ( options.hide ) {
++ jQuery( elem ).hide();
++ }
++
++ // Reset the properties, if the item has been hidden or shown
++ if ( options.hide || options.show ) {
++ for ( p in options.animatedProperties ) {
++ jQuery.style( elem, p, options.orig[ p ] );
++ jQuery.removeData( elem, "fxshow" + p, true );
++ // Toggle data is no longer needed
++ jQuery.removeData( elem, "toggle" + p, true );
++ }
++ }
++
++ // Execute the complete function
++ // in the event that the complete function throws an exception
++ // we must ensure it won't be called twice. #5684
++
++ complete = options.complete;
++ if ( complete ) {
++
++ options.complete = false;
++ complete.call( elem );
++ }
++ }
++
++ return false;
++
++ } else {
++ // classical easing cannot be used with an Infinity duration
++ if ( options.duration == Infinity ) {
++ this.now = t;
++ } else {
++ n = t - this.startTime;
++ this.state = n / options.duration;
++
++ // Perform the easing function, defaults to swing
++ this.pos = jQuery.easing[ options.animatedProperties[this.prop] ]( this.state, n, 0, 1, options.duration );
++ this.now = this.start + ( (this.end - this.start) * this.pos );
++ }
++ // Perform the next step of the animation
++ this.update();
++ }
++
++ return true;
++ }
++};
++
++jQuery.extend( jQuery.fx, {
++ tick: function() {
++ var timer,
++ timers = jQuery.timers,
++ i = 0;
++
++ for ( ; i < timers.length; i++ ) {
++ timer = timers[ i ];
++ // Checks the timer has not already been removed
++ if ( !timer() && timers[ i ] === timer ) {
++ timers.splice( i--, 1 );
++ }
++ }
++
++ if ( !timers.length ) {
++ jQuery.fx.stop();
++ }
++ },
++
++ interval: 13,
++
++ stop: function() {
++ clearInterval( timerId );
++ timerId = null;
++ },
++
++ speeds: {
++ slow: 600,
++ fast: 200,
++ // Default speed
++ _default: 400
++ },
++
++ step: {
++ opacity: function( fx ) {
++ jQuery.style( fx.elem, "opacity", fx.now );
++ },
++
++ _default: function( fx ) {
++ if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) {
++ fx.elem.style[ fx.prop ] = fx.now + fx.unit;
++ } else {
++ fx.elem[ fx.prop ] = fx.now;
++ }
++ }
++ }
++});
++
++// Adds width/height step functions
++// Do not set anything below 0
++jQuery.each([ "width", "height" ], function( i, prop ) {
++ jQuery.fx.step[ prop ] = function( fx ) {
++ jQuery.style( fx.elem, prop, Math.max(0, fx.now) + fx.unit );
++ };
++});
++
++if ( jQuery.expr && jQuery.expr.filters ) {
++ jQuery.expr.filters.animated = function( elem ) {
++ return jQuery.grep(jQuery.timers, function( fn ) {
++ return elem === fn.elem;
++ }).length;
++ };
++}
++
++// Try to restore the default display value of an element
++function defaultDisplay( nodeName ) {
++
++ if ( !elemdisplay[ nodeName ] ) {
++
++ var body = document.body,
++ elem = jQuery( "<" + nodeName + ">" ).appendTo( body ),
++ display = elem.css( "display" );
++ elem.remove();
++
++ // If the simple way fails,
++ // get element's real default display by attaching it to a temp iframe
++ if ( display === "none" || display === "" ) {
++ // No iframe to use yet, so create it
++ if ( !iframe ) {
++ iframe = document.createElement( "iframe" );
++ iframe.frameBorder = iframe.width = iframe.height = 0;
++ }
++
++ body.appendChild( iframe );
++
++ // Create a cacheable copy of the iframe document on first call.
++ // IE and Opera will allow us to reuse the iframeDoc without re-writing the fake HTML
++ // document to it; WebKit & Firefox won't allow reusing the iframe document.
++ if ( !iframeDoc || !iframe.createElement ) {
++ iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document;
++ iframeDoc.write( ( document.compatMode === "CSS1Compat" ? "<!doctype html>" : "" ) + "<html><body>" );
++ iframeDoc.close();
++ }
++
++ elem = iframeDoc.createElement( nodeName );
++
++ iframeDoc.body.appendChild( elem );
++
++ display = jQuery.css( elem, "display" );
++ body.removeChild( iframe );
++ }
++
++ // Store the correct default display
++ elemdisplay[ nodeName ] = display;
++ }
++
++ return elemdisplay[ nodeName ];
++}
++
++
++
++
++var rtable = /^t(?:able|d|h)$/i,
++ rroot = /^(?:body|html)$/i;
++
++if ( "getBoundingClientRect" in document.documentElement ) {
++ jQuery.fn.offset = function( options ) {
++ var elem = this[0], box;
++
++ if ( options ) {
++ return this.each(function( i ) {
++ jQuery.offset.setOffset( this, options, i );
++ });
++ }
++
++ if ( !elem || !elem.ownerDocument ) {
++ return null;
++ }
++
++ if ( elem === elem.ownerDocument.body ) {
++ return jQuery.offset.bodyOffset( elem );
++ }
++
++ try {
++ box = elem.getBoundingClientRect();
++ } catch(e) {}
++
++ var doc = elem.ownerDocument,
++ docElem = doc.documentElement;
++
++ // Make sure we're not dealing with a disconnected DOM node
++ if ( !box || !jQuery.contains( docElem, elem ) ) {
++ return box ? { top: box.top, left: box.left } : { top: 0, left: 0 };
++ }
++
++ var body = doc.body,
++ win = getWindow(doc),
++ clientTop = docElem.clientTop || body.clientTop || 0,
++ clientLeft = docElem.clientLeft || body.clientLeft || 0,
++ scrollTop = win.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop,
++ scrollLeft = win.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft,
++ top = box.top + scrollTop - clientTop,
++ left = box.left + scrollLeft - clientLeft;
++
++ return { top: top, left: left };
++ };
++
++} else {
++ jQuery.fn.offset = function( options ) {
++ var elem = this[0];
++
++ if ( options ) {
++ return this.each(function( i ) {
++ jQuery.offset.setOffset( this, options, i );
++ });
++ }
++
++ if ( !elem || !elem.ownerDocument ) {
++ return null;
++ }
++
++ if ( elem === elem.ownerDocument.body ) {
++ return jQuery.offset.bodyOffset( elem );
++ }
++
++ var computedStyle,
++ offsetParent = elem.offsetParent,
++ prevOffsetParent = elem,
++ doc = elem.ownerDocument,
++ docElem = doc.documentElement,
++ body = doc.body,
++ defaultView = doc.defaultView,
++ prevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) : elem.currentStyle,
++ top = elem.offsetTop,
++ left = elem.offsetLeft;
++
++ while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
++ if ( jQuery.support.fixedPosition && prevComputedStyle.position === "fixed" ) {
++ break;
++ }
++
++ computedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle;
++ top -= elem.scrollTop;
++ left -= elem.scrollLeft;
++
++ if ( elem === offsetParent ) {
++ top += elem.offsetTop;
++ left += elem.offsetLeft;
++
++ if ( jQuery.support.doesNotAddBorder && !(jQuery.support.doesAddBorderForTableAndCells && rtable.test(elem.nodeName)) ) {
++ top += parseFloat( computedStyle.borderTopWidth ) || 0;
++ left += parseFloat( computedStyle.borderLeftWidth ) || 0;
++ }
++
++ prevOffsetParent = offsetParent;
++ offsetParent = elem.offsetParent;
++ }
++
++ if ( jQuery.support.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) {
++ top += parseFloat( computedStyle.borderTopWidth ) || 0;
++ left += parseFloat( computedStyle.borderLeftWidth ) || 0;
++ }
++
++ prevComputedStyle = computedStyle;
++ }
++
++ if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) {
++ top += body.offsetTop;
++ left += body.offsetLeft;
++ }
++
++ if ( jQuery.support.fixedPosition && prevComputedStyle.position === "fixed" ) {
++ top += Math.max( docElem.scrollTop, body.scrollTop );
++ left += Math.max( docElem.scrollLeft, body.scrollLeft );
++ }
++
++ return { top: top, left: left };
++ };
++}
++
++jQuery.offset = {
++
++ bodyOffset: function( body ) {
++ var top = body.offsetTop,
++ left = body.offsetLeft;
++
++ if ( jQuery.support.doesNotIncludeMarginInBodyOffset ) {
++ top += parseFloat( jQuery.css(body, "marginTop") ) || 0;
++ left += parseFloat( jQuery.css(body, "marginLeft") ) || 0;
++ }
++
++ return { top: top, left: left };
++ },
++
++ setOffset: function( elem, options, i ) {
++ var position = jQuery.css( elem, "position" );
++
++ // set position first, in-case top/left are set even on static elem
++ if ( position === "static" ) {
++ elem.style.position = "relative";
++ }
++
++ var curElem = jQuery( elem ),
++ curOffset = curElem.offset(),
++ curCSSTop = jQuery.css( elem, "top" ),
++ curCSSLeft = jQuery.css( elem, "left" ),
++ calculatePosition = ( position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1,
++ props = {}, curPosition = {}, curTop, curLeft;
++
++ // need to be able to calculate position if either top or left is auto and position is either absolute or fixed
++ if ( calculatePosition ) {
++ curPosition = curElem.position();
++ curTop = curPosition.top;
++ curLeft = curPosition.left;
++ } else {
++ curTop = parseFloat( curCSSTop ) || 0;
++ curLeft = parseFloat( curCSSLeft ) || 0;
++ }
++
++ if ( jQuery.isFunction( options ) ) {
++ options = options.call( elem, i, curOffset );
++ }
++
++ if ( options.top != null ) {
++ props.top = ( options.top - curOffset.top ) + curTop;
++ }
++ if ( options.left != null ) {
++ props.left = ( options.left - curOffset.left ) + curLeft;
++ }
++
++ if ( "using" in options ) {
++ options.using.call( elem, props );
++ } else {
++ curElem.css( props );
++ }
++ }
++};
++
++
++jQuery.fn.extend({
++
++ position: function() {
++ if ( !this[0] ) {
++ return null;
++ }
++
++ var elem = this[0],
++
++ // Get *real* offsetParent
++ offsetParent = this.offsetParent(),
++
++ // Get correct offsets
++ offset = this.offset(),
++ parentOffset = rroot.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset();
++
++ // Subtract element margins
++ // note: when an element has margin: auto the offsetLeft and marginLeft
++ // are the same in Safari causing offset.left to incorrectly be 0
++ offset.top -= parseFloat( jQuery.css(elem, "marginTop") ) || 0;
++ offset.left -= parseFloat( jQuery.css(elem, "marginLeft") ) || 0;
++
++ // Add offsetParent borders
++ parentOffset.top += parseFloat( jQuery.css(offsetParent[0], "borderTopWidth") ) || 0;
++ parentOffset.left += parseFloat( jQuery.css(offsetParent[0], "borderLeftWidth") ) || 0;
++
++ // Subtract the two offsets
++ return {
++ top: offset.top - parentOffset.top,
++ left: offset.left - parentOffset.left
++ };
++ },
++
++ offsetParent: function() {
++ return this.map(function() {
++ var offsetParent = this.offsetParent || document.body;
++ while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) {
++ offsetParent = offsetParent.offsetParent;
++ }
++ return offsetParent;
++ });
++ }
++});
++
++
++// Create scrollLeft and scrollTop methods
++jQuery.each( ["Left", "Top"], function( i, name ) {
++ var method = "scroll" + name;
++
++ jQuery.fn[ method ] = function( val ) {
++ var elem, win;
++
++ if ( val === undefined ) {
++ elem = this[ 0 ];
++
++ if ( !elem ) {
++ return null;
++ }
++
++ win = getWindow( elem );
++
++ // Return the scroll offset
++ return win ? ("pageXOffset" in win) ? win[ i ? "pageYOffset" : "pageXOffset" ] :
++ jQuery.support.boxModel && win.document.documentElement[ method ] ||
++ win.document.body[ method ] :
++ elem[ method ];
++ }
++
++ // Set the scroll offset
++ return this.each(function() {
++ win = getWindow( this );
++
++ if ( win ) {
++ win.scrollTo(
++ !i ? val : jQuery( win ).scrollLeft(),
++ i ? val : jQuery( win ).scrollTop()
++ );
++
++ } else {
++ this[ method ] = val;
++ }
++ });
++ };
++});
++
++function getWindow( elem ) {
++ return jQuery.isWindow( elem ) ?
++ elem :
++ elem.nodeType === 9 ?
++ elem.defaultView || elem.parentWindow :
++ false;
++}
++
++
++
++
++// Create width, height, innerHeight, innerWidth, outerHeight and outerWidth methods
++jQuery.each([ "Height", "Width" ], function( i, name ) {
++
++ var type = name.toLowerCase();
++
++ // innerHeight and innerWidth
++ jQuery.fn[ "inner" + name ] = function() {
++ var elem = this[0];
++ return elem ?
++ elem.style ?
++ parseFloat( jQuery.css( elem, type, "padding" ) ) :
++ this[ type ]() :
++ null;
++ };
++
++ // outerHeight and outerWidth
++ jQuery.fn[ "outer" + name ] = function( margin ) {
++ var elem = this[0];
++ return elem ?
++ elem.style ?
++ parseFloat( jQuery.css( elem, type, margin ? "margin" : "border" ) ) :
++ this[ type ]() :
++ null;
++ };
++
++ jQuery.fn[ type ] = function( size ) {
++ // Get window width or height
++ var elem = this[0];
++ if ( !elem ) {
++ return size == null ? null : this;
++ }
++
++ if ( jQuery.isFunction( size ) ) {
++ return this.each(function( i ) {
++ var self = jQuery( this );
++ self[ type ]( size.call( this, i, self[ type ]() ) );
++ });
++ }
++
++ if ( jQuery.isWindow( elem ) ) {
++ // Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
++ // 3rd condition allows Nokia support, as it supports the docElem prop but not CSS1Compat
++ var docElemProp = elem.document.documentElement[ "client" + name ],
++ body = elem.document.body;
++ return elem.document.compatMode === "CSS1Compat" && docElemProp ||
++ body && body[ "client" + name ] || docElemProp;
++
++ // Get document width or height
++ } else if ( elem.nodeType === 9 ) {
++ // Either scroll[Width/Height] or offset[Width/Height], whichever is greater
++ return Math.max(
++ elem.documentElement["client" + name],
++ elem.body["scroll" + name], elem.documentElement["scroll" + name],
++ elem.body["offset" + name], elem.documentElement["offset" + name]
++ );
++
++ // Get or set width or height on the element
++ } else if ( size === undefined ) {
++ var orig = jQuery.css( elem, type ),
++ ret = parseFloat( orig );
++
++ return jQuery.isNumeric( ret ) ? ret : orig;
++
++ // Set the width or height on the element (default to pixels if value is unitless)
++ } else {
++ return this.css( type, typeof size === "string" ? size : size + "px" );
++ }
++ };
++
++});
++
++
++
++
++// Expose jQuery to the global object
++window.jQuery = window.$ = jQuery;
++
++// Expose jQuery as an AMD module, but only for AMD loaders that
++// understand the issues with loading multiple versions of jQuery
++// in a page that all might call define(). The loader will indicate
++// they have special allowances for multiple jQuery versions by
++// specifying define.amd.jQuery = true. Register as a named module,
++// since jQuery can be concatenated with other files that may use define,
++// but not use a proper concatenation script that understands anonymous
++// AMD modules. A named AMD is safest and most robust way to register.
++// Lowercase jquery is used because AMD module names are derived from
++// file names, and jQuery is normally delivered in a lowercase file name.
++// Do this after creating the global so that if an AMD module wants to call
++// noConflict to hide this version of jQuery, it will work.
++if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
++ define( "jquery", [], function () { return jQuery; } );
++}
++
++
++
++})( window );
--- /dev/null
--- /dev/null
++/*!
++ * Bootstrap v3.3.6 (http://getbootstrap.com)
++ * Copyright 2011-2015 Twitter, Inc.
++ * Licensed under the MIT license
++ */
++
++if (typeof jQuery === 'undefined') {
++ throw new Error('Bootstrap\'s JavaScript requires jQuery')
++}
++
+++function ($) {
++ 'use strict';
++ var version = $.fn.jquery.split(' ')[0].split('.')
++ if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1) || (version[0] > 2)) {
++ throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher, but lower than version 3')
++ }
++}(jQuery);
++
++/* ========================================================================
++ * Bootstrap: transition.js v3.3.6
++ * http://getbootstrap.com/javascript/#transitions
++ * ========================================================================
++ * Copyright 2011-2015 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
++ * ======================================================================== */
++
++
+++function ($) {
++ 'use strict';
++
++ // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
++ // ============================================================
++
++ function transitionEnd() {
++ var el = document.createElement('bootstrap')
++
++ var transEndEventNames = {
++ WebkitTransition : 'webkitTransitionEnd',
++ MozTransition : 'transitionend',
++ OTransition : 'oTransitionEnd otransitionend',
++ transition : 'transitionend'
++ }
++
++ for (var name in transEndEventNames) {
++ if (el.style[name] !== undefined) {
++ return { end: transEndEventNames[name] }
++ }
++ }
++
++ return false // explicit for ie8 ( ._.)
++ }
++
++ // http://blog.alexmaccaw.com/css-transitions
++ $.fn.emulateTransitionEnd = function (duration) {
++ var called = false
++ var $el = this
++ $(this).one('bsTransitionEnd', function () { called = true })
++ var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
++ setTimeout(callback, duration)
++ return this
++ }
++
++ $(function () {
++ $.support.transition = transitionEnd()
++
++ if (!$.support.transition) return
++
++ $.event.special.bsTransitionEnd = {
++ bindType: $.support.transition.end,
++ delegateType: $.support.transition.end,
++ handle: function (e) {
++ if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments)
++ }
++ }
++ })
++
++}(jQuery);
++
++/* ========================================================================
++ * Bootstrap: alert.js v3.3.6
++ * http://getbootstrap.com/javascript/#alerts
++ * ========================================================================
++ * Copyright 2011-2015 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
++ * ======================================================================== */
++
++
+++function ($) {
++ 'use strict';
++
++ // ALERT CLASS DEFINITION
++ // ======================
++
++ var dismiss = '[data-dismiss="alert"]'
++ var Alert = function (el) {
++ $(el).on('click', dismiss, this.close)
++ }
++
++ Alert.VERSION = '3.3.6'
++
++ Alert.TRANSITION_DURATION = 150
++
++ Alert.prototype.close = function (e) {
++ var $this = $(this)
++ var selector = $this.attr('data-target')
++
++ if (!selector) {
++ selector = $this.attr('href')
++ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
++ }
++
++ var $parent = $(selector)
++
++ if (e) e.preventDefault()
++
++ if (!$parent.length) {
++ $parent = $this.closest('.alert')
++ }
++
++ $parent.trigger(e = $.Event('close.bs.alert'))
++
++ if (e.isDefaultPrevented()) return
++
++ $parent.removeClass('in')
++
++ function removeElement() {
++ // detach from parent, fire event then clean up data
++ $parent.detach().trigger('closed.bs.alert').remove()
++ }
++
++ $.support.transition && $parent.hasClass('fade') ?
++ $parent
++ .one('bsTransitionEnd', removeElement)
++ .emulateTransitionEnd(Alert.TRANSITION_DURATION) :
++ removeElement()
++ }
++
++
++ // ALERT PLUGIN DEFINITION
++ // =======================
++
++ function Plugin(option) {
++ return this.each(function () {
++ var $this = $(this)
++ var data = $this.data('bs.alert')
++
++ if (!data) $this.data('bs.alert', (data = new Alert(this)))
++ if (typeof option == 'string') data[option].call($this)
++ })
++ }
++
++ var old = $.fn.alert
++
++ $.fn.alert = Plugin
++ $.fn.alert.Constructor = Alert
++
++
++ // ALERT NO CONFLICT
++ // =================
++
++ $.fn.alert.noConflict = function () {
++ $.fn.alert = old
++ return this
++ }
++
++
++ // ALERT DATA-API
++ // ==============
++
++ $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close)
++
++}(jQuery);
++
++/* ========================================================================
++ * Bootstrap: button.js v3.3.6
++ * http://getbootstrap.com/javascript/#buttons
++ * ========================================================================
++ * Copyright 2011-2015 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
++ * ======================================================================== */
++
++
+++function ($) {
++ 'use strict';
++
++ // BUTTON PUBLIC CLASS DEFINITION
++ // ==============================
++
++ var Button = function (element, options) {
++ this.$element = $(element)
++ this.options = $.extend({}, Button.DEFAULTS, options)
++ this.isLoading = false
++ }
++
++ Button.VERSION = '3.3.6'
++
++ Button.DEFAULTS = {
++ loadingText: 'loading...'
++ }
++
++ Button.prototype.setState = function (state) {
++ var d = 'disabled'
++ var $el = this.$element
++ var val = $el.is('input') ? 'val' : 'html'
++ var data = $el.data()
++
++ state += 'Text'
++
++ if (data.resetText == null) $el.data('resetText', $el[val]())
++
++ // push to event loop to allow forms to submit
++ setTimeout($.proxy(function () {
++ $el[val](data[state] == null ? this.options[state] : data[state])
++
++ if (state == 'loadingText') {
++ this.isLoading = true
++ $el.addClass(d).attr(d, d)
++ } else if (this.isLoading) {
++ this.isLoading = false
++ $el.removeClass(d).removeAttr(d)
++ }
++ }, this), 0)
++ }
++
++ Button.prototype.toggle = function () {
++ var changed = true
++ var $parent = this.$element.closest('[data-toggle="buttons"]')
++
++ if ($parent.length) {
++ var $input = this.$element.find('input')
++ if ($input.prop('type') == 'radio') {
++ if ($input.prop('checked')) changed = false
++ $parent.find('.active').removeClass('active')
++ this.$element.addClass('active')
++ } else if ($input.prop('type') == 'checkbox') {
++ if (($input.prop('checked')) !== this.$element.hasClass('active')) changed = false
++ this.$element.toggleClass('active')
++ }
++ $input.prop('checked', this.$element.hasClass('active'))
++ if (changed) $input.trigger('change')
++ } else {
++ this.$element.attr('aria-pressed', !this.$element.hasClass('active'))
++ this.$element.toggleClass('active')
++ }
++ }
++
++
++ // BUTTON PLUGIN DEFINITION
++ // ========================
++
++ function Plugin(option) {
++ return this.each(function () {
++ var $this = $(this)
++ var data = $this.data('bs.button')
++ var options = typeof option == 'object' && option
++
++ if (!data) $this.data('bs.button', (data = new Button(this, options)))
++
++ if (option == 'toggle') data.toggle()
++ else if (option) data.setState(option)
++ })
++ }
++
++ var old = $.fn.button
++
++ $.fn.button = Plugin
++ $.fn.button.Constructor = Button
++
++
++ // BUTTON NO CONFLICT
++ // ==================
++
++ $.fn.button.noConflict = function () {
++ $.fn.button = old
++ return this
++ }
++
++
++ // BUTTON DATA-API
++ // ===============
++
++ $(document)
++ .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) {
++ var $btn = $(e.target)
++ if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
++ Plugin.call($btn, 'toggle')
++ if (!($(e.target).is('input[type="radio"]') || $(e.target).is('input[type="checkbox"]'))) e.preventDefault()
++ })
++ .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) {
++ $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type))
++ })
++
++}(jQuery);
++
++/* ========================================================================
++ * Bootstrap: carousel.js v3.3.6
++ * http://getbootstrap.com/javascript/#carousel
++ * ========================================================================
++ * Copyright 2011-2015 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
++ * ======================================================================== */
++
++
+++function ($) {
++ 'use strict';
++
++ // CAROUSEL CLASS DEFINITION
++ // =========================
++
++ var Carousel = function (element, options) {
++ this.$element = $(element)
++ this.$indicators = this.$element.find('.carousel-indicators')
++ this.options = options
++ this.paused = null
++ this.sliding = null
++ this.interval = null
++ this.$active = null
++ this.$items = null
++
++ this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this))
++
++ this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element
++ .on('mouseenter.bs.carousel', $.proxy(this.pause, this))
++ .on('mouseleave.bs.carousel', $.proxy(this.cycle, this))
++ }
++
++ Carousel.VERSION = '3.3.6'
++
++ Carousel.TRANSITION_DURATION = 600
++
++ Carousel.DEFAULTS = {
++ interval: 5000,
++ pause: 'hover',
++ wrap: true,
++ keyboard: true
++ }
++
++ Carousel.prototype.keydown = function (e) {
++ if (/input|textarea/i.test(e.target.tagName)) return
++ switch (e.which) {
++ case 37: this.prev(); break
++ case 39: this.next(); break
++ default: return
++ }
++
++ e.preventDefault()
++ }
++
++ Carousel.prototype.cycle = function (e) {
++ e || (this.paused = false)
++
++ this.interval && clearInterval(this.interval)
++
++ this.options.interval
++ && !this.paused
++ && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
++
++ return this
++ }
++
++ Carousel.prototype.getItemIndex = function (item) {
++ this.$items = item.parent().children('.item')
++ return this.$items.index(item || this.$active)
++ }
++
++ Carousel.prototype.getItemForDirection = function (direction, active) {
++ var activeIndex = this.getItemIndex(active)
++ var willWrap = (direction == 'prev' && activeIndex === 0)
++ || (direction == 'next' && activeIndex == (this.$items.length - 1))
++ if (willWrap && !this.options.wrap) return active
++ var delta = direction == 'prev' ? -1 : 1
++ var itemIndex = (activeIndex + delta) % this.$items.length
++ return this.$items.eq(itemIndex)
++ }
++
++ Carousel.prototype.to = function (pos) {
++ var that = this
++ var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active'))
++
++ if (pos > (this.$items.length - 1) || pos < 0) return
++
++ if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid"
++ if (activeIndex == pos) return this.pause().cycle()
++
++ return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos))
++ }
++
++ Carousel.prototype.pause = function (e) {
++ e || (this.paused = true)
++
++ if (this.$element.find('.next, .prev').length && $.support.transition) {
++ this.$element.trigger($.support.transition.end)
++ this.cycle(true)
++ }
++
++ this.interval = clearInterval(this.interval)
++
++ return this
++ }
++
++ Carousel.prototype.next = function () {
++ if (this.sliding) return
++ return this.slide('next')
++ }
++
++ Carousel.prototype.prev = function () {
++ if (this.sliding) return
++ return this.slide('prev')
++ }
++
++ Carousel.prototype.slide = function (type, next) {
++ var $active = this.$element.find('.item.active')
++ var $next = next || this.getItemForDirection(type, $active)
++ var isCycling = this.interval
++ var direction = type == 'next' ? 'left' : 'right'
++ var that = this
++
++ if ($next.hasClass('active')) return (this.sliding = false)
++
++ var relatedTarget = $next[0]
++ var slideEvent = $.Event('slide.bs.carousel', {
++ relatedTarget: relatedTarget,
++ direction: direction
++ })
++ this.$element.trigger(slideEvent)
++ if (slideEvent.isDefaultPrevented()) return
++
++ this.sliding = true
++
++ isCycling && this.pause()
++
++ if (this.$indicators.length) {
++ this.$indicators.find('.active').removeClass('active')
++ var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)])
++ $nextIndicator && $nextIndicator.addClass('active')
++ }
++
++ var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid"
++ if ($.support.transition && this.$element.hasClass('slide')) {
++ $next.addClass(type)
++ $next[0].offsetWidth // force reflow
++ $active.addClass(direction)
++ $next.addClass(direction)
++ $active
++ .one('bsTransitionEnd', function () {
++ $next.removeClass([type, direction].join(' ')).addClass('active')
++ $active.removeClass(['active', direction].join(' '))
++ that.sliding = false
++ setTimeout(function () {
++ that.$element.trigger(slidEvent)
++ }, 0)
++ })
++ .emulateTransitionEnd(Carousel.TRANSITION_DURATION)
++ } else {
++ $active.removeClass('active')
++ $next.addClass('active')
++ this.sliding = false
++ this.$element.trigger(slidEvent)
++ }
++
++ isCycling && this.cycle()
++
++ return this
++ }
++
++
++ // CAROUSEL PLUGIN DEFINITION
++ // ==========================
++
++ function Plugin(option) {
++ return this.each(function () {
++ var $this = $(this)
++ var data = $this.data('bs.carousel')
++ var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option)
++ var action = typeof option == 'string' ? option : options.slide
++
++ if (!data) $this.data('bs.carousel', (data = new Carousel(this, options)))
++ if (typeof option == 'number') data.to(option)
++ else if (action) data[action]()
++ else if (options.interval) data.pause().cycle()
++ })
++ }
++
++ var old = $.fn.carousel
++
++ $.fn.carousel = Plugin
++ $.fn.carousel.Constructor = Carousel
++
++
++ // CAROUSEL NO CONFLICT
++ // ====================
++
++ $.fn.carousel.noConflict = function () {
++ $.fn.carousel = old
++ return this
++ }
++
++
++ // CAROUSEL DATA-API
++ // =================
++
++ var clickHandler = function (e) {
++ var href
++ var $this = $(this)
++ var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7
++ if (!$target.hasClass('carousel')) return
++ var options = $.extend({}, $target.data(), $this.data())
++ var slideIndex = $this.attr('data-slide-to')
++ if (slideIndex) options.interval = false
++
++ Plugin.call($target, options)
++
++ if (slideIndex) {
++ $target.data('bs.carousel').to(slideIndex)
++ }
++
++ e.preventDefault()
++ }
++
++ $(document)
++ .on('click.bs.carousel.data-api', '[data-slide]', clickHandler)
++ .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler)
++
++ $(window).on('load', function () {
++ $('[data-ride="carousel"]').each(function () {
++ var $carousel = $(this)
++ Plugin.call($carousel, $carousel.data())
++ })
++ })
++
++}(jQuery);
++
++/* ========================================================================
++ * Bootstrap: collapse.js v3.3.6
++ * http://getbootstrap.com/javascript/#collapse
++ * ========================================================================
++ * Copyright 2011-2015 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
++ * ======================================================================== */
++
++
+++function ($) {
++ 'use strict';
++
++ // COLLAPSE PUBLIC CLASS DEFINITION
++ // ================================
++
++ var Collapse = function (element, options) {
++ this.$element = $(element)
++ this.options = $.extend({}, Collapse.DEFAULTS, options)
++ this.$trigger = $('[data-toggle="collapse"][href="#' + element.id + '"],' +
++ '[data-toggle="collapse"][data-target="#' + element.id + '"]')
++ this.transitioning = null
++
++ if (this.options.parent) {
++ this.$parent = this.getParent()
++ } else {
++ this.addAriaAndCollapsedClass(this.$element, this.$trigger)
++ }
++
++ if (this.options.toggle) this.toggle()
++ }
++
++ Collapse.VERSION = '3.3.6'
++
++ Collapse.TRANSITION_DURATION = 350
++
++ Collapse.DEFAULTS = {
++ toggle: true
++ }
++
++ Collapse.prototype.dimension = function () {
++ var hasWidth = this.$element.hasClass('width')
++ return hasWidth ? 'width' : 'height'
++ }
++
++ Collapse.prototype.show = function () {
++ if (this.transitioning || this.$element.hasClass('in')) return
++
++ var activesData
++ var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing')
++
++ if (actives && actives.length) {
++ activesData = actives.data('bs.collapse')
++ if (activesData && activesData.transitioning) return
++ }
++
++ var startEvent = $.Event('show.bs.collapse')
++ this.$element.trigger(startEvent)
++ if (startEvent.isDefaultPrevented()) return
++
++ if (actives && actives.length) {
++ Plugin.call(actives, 'hide')
++ activesData || actives.data('bs.collapse', null)
++ }
++
++ var dimension = this.dimension()
++
++ this.$element
++ .removeClass('collapse')
++ .addClass('collapsing')[dimension](0)
++ .attr('aria-expanded', true)
++
++ this.$trigger
++ .removeClass('collapsed')
++ .attr('aria-expanded', true)
++
++ this.transitioning = 1
++
++ var complete = function () {
++ this.$element
++ .removeClass('collapsing')
++ .addClass('collapse in')[dimension]('')
++ this.transitioning = 0
++ this.$element
++ .trigger('shown.bs.collapse')
++ }
++
++ if (!$.support.transition) return complete.call(this)
++
++ var scrollSize = $.camelCase(['scroll', dimension].join('-'))
++
++ this.$element
++ .one('bsTransitionEnd', $.proxy(complete, this))
++ .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize])
++ }
++
++ Collapse.prototype.hide = function () {
++ if (this.transitioning || !this.$element.hasClass('in')) return
++
++ var startEvent = $.Event('hide.bs.collapse')
++ this.$element.trigger(startEvent)
++ if (startEvent.isDefaultPrevented()) return
++
++ var dimension = this.dimension()
++
++ this.$element[dimension](this.$element[dimension]())[0].offsetHeight
++
++ this.$element
++ .addClass('collapsing')
++ .removeClass('collapse in')
++ .attr('aria-expanded', false)
++
++ this.$trigger
++ .addClass('collapsed')
++ .attr('aria-expanded', false)
++
++ this.transitioning = 1
++
++ var complete = function () {
++ this.transitioning = 0
++ this.$element
++ .removeClass('collapsing')
++ .addClass('collapse')
++ .trigger('hidden.bs.collapse')
++ }
++
++ if (!$.support.transition) return complete.call(this)
++
++ this.$element
++ [dimension](0)
++ .one('bsTransitionEnd', $.proxy(complete, this))
++ .emulateTransitionEnd(Collapse.TRANSITION_DURATION)
++ }
++
++ Collapse.prototype.toggle = function () {
++ this[this.$element.hasClass('in') ? 'hide' : 'show']()
++ }
++
++ Collapse.prototype.getParent = function () {
++ return $(this.options.parent)
++ .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]')
++ .each($.proxy(function (i, element) {
++ var $element = $(element)
++ this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element)
++ }, this))
++ .end()
++ }
++
++ Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) {
++ var isOpen = $element.hasClass('in')
++
++ $element.attr('aria-expanded', isOpen)
++ $trigger
++ .toggleClass('collapsed', !isOpen)
++ .attr('aria-expanded', isOpen)
++ }
++
++ function getTargetFromTrigger($trigger) {
++ var href
++ var target = $trigger.attr('data-target')
++ || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
++
++ return $(target)
++ }
++
++
++ // COLLAPSE PLUGIN DEFINITION
++ // ==========================
++
++ function Plugin(option) {
++ return this.each(function () {
++ var $this = $(this)
++ var data = $this.data('bs.collapse')
++ var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)
++
++ if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false
++ if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
++ if (typeof option == 'string') data[option]()
++ })
++ }
++
++ var old = $.fn.collapse
++
++ $.fn.collapse = Plugin
++ $.fn.collapse.Constructor = Collapse
++
++
++ // COLLAPSE NO CONFLICT
++ // ====================
++
++ $.fn.collapse.noConflict = function () {
++ $.fn.collapse = old
++ return this
++ }
++
++
++ // COLLAPSE DATA-API
++ // =================
++
++ $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) {
++ var $this = $(this)
++
++ if (!$this.attr('data-target')) e.preventDefault()
++
++ var $target = getTargetFromTrigger($this)
++ var data = $target.data('bs.collapse')
++ var option = data ? 'toggle' : $this.data()
++
++ Plugin.call($target, option)
++ })
++
++}(jQuery);
++
++/* ========================================================================
++ * Bootstrap: dropdown.js v3.3.6
++ * http://getbootstrap.com/javascript/#dropdowns
++ * ========================================================================
++ * Copyright 2011-2015 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
++ * ======================================================================== */
++
++
+++function ($) {
++ 'use strict';
++
++ // DROPDOWN CLASS DEFINITION
++ // =========================
++
++ var backdrop = '.dropdown-backdrop'
++ var toggle = '[data-toggle="dropdown"]'
++ var Dropdown = function (element) {
++ $(element).on('click.bs.dropdown', this.toggle)
++ }
++
++ Dropdown.VERSION = '3.3.6'
++
++ function getParent($this) {
++ var selector = $this.attr('data-target')
++
++ if (!selector) {
++ selector = $this.attr('href')
++ selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
++ }
++
++ var $parent = selector && $(selector)
++
++ return $parent && $parent.length ? $parent : $this.parent()
++ }
++
++ function clearMenus(e) {
++ if (e && e.which === 3) return
++ $(backdrop).remove()
++ $(toggle).each(function () {
++ var $this = $(this)
++ var $parent = getParent($this)
++ var relatedTarget = { relatedTarget: this }
++
++ if (!$parent.hasClass('open')) return
++
++ if (e && e.type == 'click' && /input|textarea/i.test(e.target.tagName) && $.contains($parent[0], e.target)) return
++
++ $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
++
++ if (e.isDefaultPrevented()) return
++
++ $this.attr('aria-expanded', 'false')
++ $parent.removeClass('open').trigger($.Event('hidden.bs.dropdown', relatedTarget))
++ })
++ }
++
++ Dropdown.prototype.toggle = function (e) {
++ var $this = $(this)
++
++ if ($this.is('.disabled, :disabled')) return
++
++ var $parent = getParent($this)
++ var isActive = $parent.hasClass('open')
++
++ clearMenus()
++
++ if (!isActive) {
++ if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
++ // if mobile we use a backdrop because click events don't delegate
++ $(document.createElement('div'))
++ .addClass('dropdown-backdrop')
++ .insertAfter($(this))
++ .on('click', clearMenus)
++ }
++
++ var relatedTarget = { relatedTarget: this }
++ $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget))
++
++ if (e.isDefaultPrevented()) return
++
++ $this
++ .trigger('focus')
++ .attr('aria-expanded', 'true')
++
++ $parent
++ .toggleClass('open')
++ .trigger($.Event('shown.bs.dropdown', relatedTarget))
++ }
++
++ return false
++ }
++
++ Dropdown.prototype.keydown = function (e) {
++ if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return
++
++ var $this = $(this)
++
++ e.preventDefault()
++ e.stopPropagation()
++
++ if ($this.is('.disabled, :disabled')) return
++
++ var $parent = getParent($this)
++ var isActive = $parent.hasClass('open')
++
++ if (!isActive && e.which != 27 || isActive && e.which == 27) {
++ if (e.which == 27) $parent.find(toggle).trigger('focus')
++ return $this.trigger('click')
++ }
++
++ var desc = ' li:not(.disabled):visible a'
++ var $items = $parent.find('.dropdown-menu' + desc)
++
++ if (!$items.length) return
++
++ var index = $items.index(e.target)
++
++ if (e.which == 38 && index > 0) index-- // up
++ if (e.which == 40 && index < $items.length - 1) index++ // down
++ if (!~index) index = 0
++
++ $items.eq(index).trigger('focus')
++ }
++
++
++ // DROPDOWN PLUGIN DEFINITION
++ // ==========================
++
++ function Plugin(option) {
++ return this.each(function () {
++ var $this = $(this)
++ var data = $this.data('bs.dropdown')
++
++ if (!data) $this.data('bs.dropdown', (data = new Dropdown(this)))
++ if (typeof option == 'string') data[option].call($this)
++ })
++ }
++
++ var old = $.fn.dropdown
++
++ $.fn.dropdown = Plugin
++ $.fn.dropdown.Constructor = Dropdown
++
++
++ // DROPDOWN NO CONFLICT
++ // ====================
++
++ $.fn.dropdown.noConflict = function () {
++ $.fn.dropdown = old
++ return this
++ }
++
++
++ // APPLY TO STANDARD DROPDOWN ELEMENTS
++ // ===================================
++
++ $(document)
++ .on('click.bs.dropdown.data-api', clearMenus)
++ .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
++ .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle)
++ .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown)
++ .on('keydown.bs.dropdown.data-api', '.dropdown-menu', Dropdown.prototype.keydown)
++
++}(jQuery);
++
++/* ========================================================================
++ * Bootstrap: modal.js v3.3.6
++ * http://getbootstrap.com/javascript/#modals
++ * ========================================================================
++ * Copyright 2011-2015 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
++ * ======================================================================== */
++
++
+++function ($) {
++ 'use strict';
++
++ // MODAL CLASS DEFINITION
++ // ======================
++
++ var Modal = function (element, options) {
++ this.options = options
++ this.$body = $(document.body)
++ this.$element = $(element)
++ this.$dialog = this.$element.find('.modal-dialog')
++ this.$backdrop = null
++ this.isShown = null
++ this.originalBodyPad = null
++ this.scrollbarWidth = 0
++ this.ignoreBackdropClick = false
++
++ if (this.options.remote) {
++ this.$element
++ .find('.modal-content')
++ .load(this.options.remote, $.proxy(function () {
++ this.$element.trigger('loaded.bs.modal')
++ }, this))
++ }
++ }
++
++ Modal.VERSION = '3.3.6'
++
++ Modal.TRANSITION_DURATION = 300
++ Modal.BACKDROP_TRANSITION_DURATION = 150
++
++ Modal.DEFAULTS = {
++ backdrop: true,
++ keyboard: true,
++ show: true
++ }
++
++ Modal.prototype.toggle = function (_relatedTarget) {
++ return this.isShown ? this.hide() : this.show(_relatedTarget)
++ }
++
++ Modal.prototype.show = function (_relatedTarget) {
++ var that = this
++ var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
++
++ this.$element.trigger(e)
++
++ if (this.isShown || e.isDefaultPrevented()) return
++
++ this.isShown = true
++
++ this.checkScrollbar()
++ this.setScrollbar()
++ this.$body.addClass('modal-open')
++
++ this.escape()
++ this.resize()
++
++ this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
++
++ this.$dialog.on('mousedown.dismiss.bs.modal', function () {
++ that.$element.one('mouseup.dismiss.bs.modal', function (e) {
++ if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true
++ })
++ })
++
++ this.backdrop(function () {
++ var transition = $.support.transition && that.$element.hasClass('fade')
++
++ if (!that.$element.parent().length) {
++ that.$element.appendTo(that.$body) // don't move modals dom position
++ }
++
++ that.$element
++ .show()
++ .scrollTop(0)
++
++ that.adjustDialog()
++
++ if (transition) {
++ that.$element[0].offsetWidth // force reflow
++ }
++
++ that.$element.addClass('in')
++
++ that.enforceFocus()
++
++ var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
++
++ transition ?
++ that.$dialog // wait for modal to slide in
++ .one('bsTransitionEnd', function () {
++ that.$element.trigger('focus').trigger(e)
++ })
++ .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
++ that.$element.trigger('focus').trigger(e)
++ })
++ }
++
++ Modal.prototype.hide = function (e) {
++ if (e) e.preventDefault()
++
++ e = $.Event('hide.bs.modal')
++
++ this.$element.trigger(e)
++
++ if (!this.isShown || e.isDefaultPrevented()) return
++
++ this.isShown = false
++
++ this.escape()
++ this.resize()
++
++ $(document).off('focusin.bs.modal')
++
++ this.$element
++ .removeClass('in')
++ .off('click.dismiss.bs.modal')
++ .off('mouseup.dismiss.bs.modal')
++
++ this.$dialog.off('mousedown.dismiss.bs.modal')
++
++ $.support.transition && this.$element.hasClass('fade') ?
++ this.$element
++ .one('bsTransitionEnd', $.proxy(this.hideModal, this))
++ .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
++ this.hideModal()
++ }
++
++ Modal.prototype.enforceFocus = function () {
++ $(document)
++ .off('focusin.bs.modal') // guard against infinite focus loop
++ .on('focusin.bs.modal', $.proxy(function (e) {
++ if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
++ this.$element.trigger('focus')
++ }
++ }, this))
++ }
++
++ Modal.prototype.escape = function () {
++ if (this.isShown && this.options.keyboard) {
++ this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) {
++ e.which == 27 && this.hide()
++ }, this))
++ } else if (!this.isShown) {
++ this.$element.off('keydown.dismiss.bs.modal')
++ }
++ }
++
++ Modal.prototype.resize = function () {
++ if (this.isShown) {
++ $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this))
++ } else {
++ $(window).off('resize.bs.modal')
++ }
++ }
++
++ Modal.prototype.hideModal = function () {
++ var that = this
++ this.$element.hide()
++ this.backdrop(function () {
++ that.$body.removeClass('modal-open')
++ that.resetAdjustments()
++ that.resetScrollbar()
++ that.$element.trigger('hidden.bs.modal')
++ })
++ }
++
++ Modal.prototype.removeBackdrop = function () {
++ this.$backdrop && this.$backdrop.remove()
++ this.$backdrop = null
++ }
++
++ Modal.prototype.backdrop = function (callback) {
++ var that = this
++ var animate = this.$element.hasClass('fade') ? 'fade' : ''
++
++ if (this.isShown && this.options.backdrop) {
++ var doAnimate = $.support.transition && animate
++
++ this.$backdrop = $(document.createElement('div'))
++ .addClass('modal-backdrop ' + animate)
++ .appendTo(this.$body)
++
++ this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {
++ if (this.ignoreBackdropClick) {
++ this.ignoreBackdropClick = false
++ return
++ }
++ if (e.target !== e.currentTarget) return
++ this.options.backdrop == 'static'
++ ? this.$element[0].focus()
++ : this.hide()
++ }, this))
++
++ if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
++
++ this.$backdrop.addClass('in')
++
++ if (!callback) return
++
++ doAnimate ?
++ this.$backdrop
++ .one('bsTransitionEnd', callback)
++ .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
++ callback()
++
++ } else if (!this.isShown && this.$backdrop) {
++ this.$backdrop.removeClass('in')
++
++ var callbackRemove = function () {
++ that.removeBackdrop()
++ callback && callback()
++ }
++ $.support.transition && this.$element.hasClass('fade') ?
++ this.$backdrop
++ .one('bsTransitionEnd', callbackRemove)
++ .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
++ callbackRemove()
++
++ } else if (callback) {
++ callback()
++ }
++ }
++
++ // these following methods are used to handle overflowing modals
++
++ Modal.prototype.handleUpdate = function () {
++ this.adjustDialog()
++ }
++
++ Modal.prototype.adjustDialog = function () {
++ var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight
++
++ this.$element.css({
++ paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '',
++ paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : ''
++ })
++ }
++
++ Modal.prototype.resetAdjustments = function () {
++ this.$element.css({
++ paddingLeft: '',
++ paddingRight: ''
++ })
++ }
++
++ Modal.prototype.checkScrollbar = function () {
++ var fullWindowWidth = window.innerWidth
++ if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8
++ var documentElementRect = document.documentElement.getBoundingClientRect()
++ fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left)
++ }
++ this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth
++ this.scrollbarWidth = this.measureScrollbar()
++ }
++
++ Modal.prototype.setScrollbar = function () {
++ var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
++ this.originalBodyPad = document.body.style.paddingRight || ''
++ if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth)
++ }
++
++ Modal.prototype.resetScrollbar = function () {
++ this.$body.css('padding-right', this.originalBodyPad)
++ }
++
++ Modal.prototype.measureScrollbar = function () { // thx walsh
++ var scrollDiv = document.createElement('div')
++ scrollDiv.className = 'modal-scrollbar-measure'
++ this.$body.append(scrollDiv)
++ var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
++ this.$body[0].removeChild(scrollDiv)
++ return scrollbarWidth
++ }
++
++
++ // MODAL PLUGIN DEFINITION
++ // =======================
++
++ function Plugin(option, _relatedTarget) {
++ return this.each(function () {
++ var $this = $(this)
++ var data = $this.data('bs.modal')
++ var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
++
++ if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
++ if (typeof option == 'string') data[option](_relatedTarget)
++ else if (options.show) data.show(_relatedTarget)
++ })
++ }
++
++ var old = $.fn.modal
++
++ $.fn.modal = Plugin
++ $.fn.modal.Constructor = Modal
++
++
++ // MODAL NO CONFLICT
++ // =================
++
++ $.fn.modal.noConflict = function () {
++ $.fn.modal = old
++ return this
++ }
++
++
++ // MODAL DATA-API
++ // ==============
++
++ $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
++ var $this = $(this)
++ var href = $this.attr('href')
++ var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7
++ var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
++
++ if ($this.is('a')) e.preventDefault()
++
++ $target.one('show.bs.modal', function (showEvent) {
++ if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown
++ $target.one('hidden.bs.modal', function () {
++ $this.is(':visible') && $this.trigger('focus')
++ })
++ })
++ Plugin.call($target, option, this)
++ })
++
++}(jQuery);
++
++/* ========================================================================
++ * Bootstrap: tooltip.js v3.3.6
++ * http://getbootstrap.com/javascript/#tooltip
++ * Inspired by the original jQuery.tipsy by Jason Frame
++ * ========================================================================
++ * Copyright 2011-2015 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
++ * ======================================================================== */
++
++
+++function ($) {
++ 'use strict';
++
++ // TOOLTIP PUBLIC CLASS DEFINITION
++ // ===============================
++
++ var Tooltip = function (element, options) {
++ this.type = null
++ this.options = null
++ this.enabled = null
++ this.timeout = null
++ this.hoverState = null
++ this.$element = null
++ this.inState = null
++
++ this.init('tooltip', element, options)
++ }
++
++ Tooltip.VERSION = '3.3.6'
++
++ Tooltip.TRANSITION_DURATION = 150
++
++ Tooltip.DEFAULTS = {
++ animation: true,
++ placement: 'top',
++ selector: false,
++ template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
++ trigger: 'hover focus',
++ title: '',
++ delay: 0,
++ html: false,
++ container: false,
++ viewport: {
++ selector: 'body',
++ padding: 0
++ }
++ }
++
++ Tooltip.prototype.init = function (type, element, options) {
++ this.enabled = true
++ this.type = type
++ this.$element = $(element)
++ this.options = this.getOptions(options)
++ this.$viewport = this.options.viewport && $($.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : (this.options.viewport.selector || this.options.viewport))
++ this.inState = { click: false, hover: false, focus: false }
++
++ if (this.$element[0] instanceof document.constructor && !this.options.selector) {
++ throw new Error('`selector` option must be specified when initializing ' + this.type + ' on the window.document object!')
++ }
++
++ var triggers = this.options.trigger.split(' ')
++
++ for (var i = triggers.length; i--;) {
++ var trigger = triggers[i]
++
++ if (trigger == 'click') {
++ this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
++ } else if (trigger != 'manual') {
++ var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
++ var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
++
++ this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
++ this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
++ }
++ }
++
++ this.options.selector ?
++ (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
++ this.fixTitle()
++ }
++
++ Tooltip.prototype.getDefaults = function () {
++ return Tooltip.DEFAULTS
++ }
++
++ Tooltip.prototype.getOptions = function (options) {
++ options = $.extend({}, this.getDefaults(), this.$element.data(), options)
++
++ if (options.delay && typeof options.delay == 'number') {
++ options.delay = {
++ show: options.delay,
++ hide: options.delay
++ }
++ }
++
++ return options
++ }
++
++ Tooltip.prototype.getDelegateOptions = function () {
++ var options = {}
++ var defaults = this.getDefaults()
++
++ this._options && $.each(this._options, function (key, value) {
++ if (defaults[key] != value) options[key] = value
++ })
++
++ return options
++ }
++
++ Tooltip.prototype.enter = function (obj) {
++ var self = obj instanceof this.constructor ?
++ obj : $(obj.currentTarget).data('bs.' + this.type)
++
++ if (!self) {
++ self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
++ $(obj.currentTarget).data('bs.' + this.type, self)
++ }
++
++ if (obj instanceof $.Event) {
++ self.inState[obj.type == 'focusin' ? 'focus' : 'hover'] = true
++ }
++
++ if (self.tip().hasClass('in') || self.hoverState == 'in') {
++ self.hoverState = 'in'
++ return
++ }
++
++ clearTimeout(self.timeout)
++
++ self.hoverState = 'in'
++
++ if (!self.options.delay || !self.options.delay.show) return self.show()
++
++ self.timeout = setTimeout(function () {
++ if (self.hoverState == 'in') self.show()
++ }, self.options.delay.show)
++ }
++
++ Tooltip.prototype.isInStateTrue = function () {
++ for (var key in this.inState) {
++ if (this.inState[key]) return true
++ }
++
++ return false
++ }
++
++ Tooltip.prototype.leave = function (obj) {
++ var self = obj instanceof this.constructor ?
++ obj : $(obj.currentTarget).data('bs.' + this.type)
++
++ if (!self) {
++ self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
++ $(obj.currentTarget).data('bs.' + this.type, self)
++ }
++
++ if (obj instanceof $.Event) {
++ self.inState[obj.type == 'focusout' ? 'focus' : 'hover'] = false
++ }
++
++ if (self.isInStateTrue()) return
++
++ clearTimeout(self.timeout)
++
++ self.hoverState = 'out'
++
++ if (!self.options.delay || !self.options.delay.hide) return self.hide()
++
++ self.timeout = setTimeout(function () {
++ if (self.hoverState == 'out') self.hide()
++ }, self.options.delay.hide)
++ }
++
++ Tooltip.prototype.show = function () {
++ var e = $.Event('show.bs.' + this.type)
++
++ if (this.hasContent() && this.enabled) {
++ this.$element.trigger(e)
++
++ var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0])
++ if (e.isDefaultPrevented() || !inDom) return
++ var that = this
++
++ var $tip = this.tip()
++
++ var tipId = this.getUID(this.type)
++
++ this.setContent()
++ $tip.attr('id', tipId)
++ this.$element.attr('aria-describedby', tipId)
++
++ if (this.options.animation) $tip.addClass('fade')
++
++ var placement = typeof this.options.placement == 'function' ?
++ this.options.placement.call(this, $tip[0], this.$element[0]) :
++ this.options.placement
++
++ var autoToken = /\s?auto?\s?/i
++ var autoPlace = autoToken.test(placement)
++ if (autoPlace) placement = placement.replace(autoToken, '') || 'top'
++
++ $tip
++ .detach()
++ .css({ top: 0, left: 0, display: 'block' })
++ .addClass(placement)
++ .data('bs.' + this.type, this)
++
++ this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
++ this.$element.trigger('inserted.bs.' + this.type)
++
++ var pos = this.getPosition()
++ var actualWidth = $tip[0].offsetWidth
++ var actualHeight = $tip[0].offsetHeight
++
++ if (autoPlace) {
++ var orgPlacement = placement
++ var viewportDim = this.getPosition(this.$viewport)
++
++ placement = placement == 'bottom' && pos.bottom + actualHeight > viewportDim.bottom ? 'top' :
++ placement == 'top' && pos.top - actualHeight < viewportDim.top ? 'bottom' :
++ placement == 'right' && pos.right + actualWidth > viewportDim.width ? 'left' :
++ placement == 'left' && pos.left - actualWidth < viewportDim.left ? 'right' :
++ placement
++
++ $tip
++ .removeClass(orgPlacement)
++ .addClass(placement)
++ }
++
++ var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
++
++ this.applyPlacement(calculatedOffset, placement)
++
++ var complete = function () {
++ var prevHoverState = that.hoverState
++ that.$element.trigger('shown.bs.' + that.type)
++ that.hoverState = null
++
++ if (prevHoverState == 'out') that.leave(that)
++ }
++
++ $.support.transition && this.$tip.hasClass('fade') ?
++ $tip
++ .one('bsTransitionEnd', complete)
++ .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
++ complete()
++ }
++ }
++
++ Tooltip.prototype.applyPlacement = function (offset, placement) {
++ var $tip = this.tip()
++ var width = $tip[0].offsetWidth
++ var height = $tip[0].offsetHeight
++
++ // manually read margins because getBoundingClientRect includes difference
++ var marginTop = parseInt($tip.css('margin-top'), 10)
++ var marginLeft = parseInt($tip.css('margin-left'), 10)
++
++ // we must check for NaN for ie 8/9
++ if (isNaN(marginTop)) marginTop = 0
++ if (isNaN(marginLeft)) marginLeft = 0
++
++ offset.top += marginTop
++ offset.left += marginLeft
++
++ // $.fn.offset doesn't round pixel values
++ // so we use setOffset directly with our own function B-0
++ $.offset.setOffset($tip[0], $.extend({
++ using: function (props) {
++ $tip.css({
++ top: Math.round(props.top),
++ left: Math.round(props.left)
++ })
++ }
++ }, offset), 0)
++
++ $tip.addClass('in')
++
++ // check to see if placing tip in new offset caused the tip to resize itself
++ var actualWidth = $tip[0].offsetWidth
++ var actualHeight = $tip[0].offsetHeight
++
++ if (placement == 'top' && actualHeight != height) {
++ offset.top = offset.top + height - actualHeight
++ }
++
++ var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)
++
++ if (delta.left) offset.left += delta.left
++ else offset.top += delta.top
++
++ var isVertical = /top|bottom/.test(placement)
++ var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
++ var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight'
++
++ $tip.offset(offset)
++ this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical)
++ }
++
++ Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) {
++ this.arrow()
++ .css(isVertical ? 'left' : 'top', 50 * (1 - delta / dimension) + '%')
++ .css(isVertical ? 'top' : 'left', '')
++ }
++
++ Tooltip.prototype.setContent = function () {
++ var $tip = this.tip()
++ var title = this.getTitle()
++
++ $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
++ $tip.removeClass('fade in top bottom left right')
++ }
++
++ Tooltip.prototype.hide = function (callback) {
++ var that = this
++ var $tip = $(this.$tip)
++ var e = $.Event('hide.bs.' + this.type)
++
++ function complete() {
++ if (that.hoverState != 'in') $tip.detach()
++ that.$element
++ .removeAttr('aria-describedby')
++ .trigger('hidden.bs.' + that.type)
++ callback && callback()
++ }
++
++ this.$element.trigger(e)
++
++ if (e.isDefaultPrevented()) return
++
++ $tip.removeClass('in')
++
++ $.support.transition && $tip.hasClass('fade') ?
++ $tip
++ .one('bsTransitionEnd', complete)
++ .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
++ complete()
++
++ this.hoverState = null
++
++ return this
++ }
++
++ Tooltip.prototype.fixTitle = function () {
++ var $e = this.$element
++ if ($e.attr('title') || typeof $e.attr('data-original-title') != 'string') {
++ $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
++ }
++ }
++
++ Tooltip.prototype.hasContent = function () {
++ return this.getTitle()
++ }
++
++ Tooltip.prototype.getPosition = function ($element) {
++ $element = $element || this.$element
++
++ var el = $element[0]
++ var isBody = el.tagName == 'BODY'
++
++ var elRect = el.getBoundingClientRect()
++ if (elRect.width == null) {
++ // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093
++ elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top })
++ }
++ var elOffset = isBody ? { top: 0, left: 0 } : $element.offset()
++ var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() }
++ var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null
++
++ return $.extend({}, elRect, scroll, outerDims, elOffset)
++ }
++
++ Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
++ return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } :
++ placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } :
++ placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
++ /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }
++
++ }
++
++ Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) {
++ var delta = { top: 0, left: 0 }
++ if (!this.$viewport) return delta
++
++ var viewportPadding = this.options.viewport && this.options.viewport.padding || 0
++ var viewportDimensions = this.getPosition(this.$viewport)
++
++ if (/right|left/.test(placement)) {
++ var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll
++ var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight
++ if (topEdgeOffset < viewportDimensions.top) { // top overflow
++ delta.top = viewportDimensions.top - topEdgeOffset
++ } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
++ delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset
++ }
++ } else {
++ var leftEdgeOffset = pos.left - viewportPadding
++ var rightEdgeOffset = pos.left + viewportPadding + actualWidth
++ if (leftEdgeOffset < viewportDimensions.left) { // left overflow
++ delta.left = viewportDimensions.left - leftEdgeOffset
++ } else if (rightEdgeOffset > viewportDimensions.right) { // right overflow
++ delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
++ }
++ }
++
++ return delta
++ }
++
++ Tooltip.prototype.getTitle = function () {
++ var title
++ var $e = this.$element
++ var o = this.options
++
++ title = $e.attr('data-original-title')
++ || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
++
++ return title
++ }
++
++ Tooltip.prototype.getUID = function (prefix) {
++ do prefix += ~~(Math.random() * 1000000)
++ while (document.getElementById(prefix))
++ return prefix
++ }
++
++ Tooltip.prototype.tip = function () {
++ if (!this.$tip) {
++ this.$tip = $(this.options.template)
++ if (this.$tip.length != 1) {
++ throw new Error(this.type + ' `template` option must consist of exactly 1 top-level element!')
++ }
++ }
++ return this.$tip
++ }
++
++ Tooltip.prototype.arrow = function () {
++ return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow'))
++ }
++
++ Tooltip.prototype.enable = function () {
++ this.enabled = true
++ }
++
++ Tooltip.prototype.disable = function () {
++ this.enabled = false
++ }
++
++ Tooltip.prototype.toggleEnabled = function () {
++ this.enabled = !this.enabled
++ }
++
++ Tooltip.prototype.toggle = function (e) {
++ var self = this
++ if (e) {
++ self = $(e.currentTarget).data('bs.' + this.type)
++ if (!self) {
++ self = new this.constructor(e.currentTarget, this.getDelegateOptions())
++ $(e.currentTarget).data('bs.' + this.type, self)
++ }
++ }
++
++ if (e) {
++ self.inState.click = !self.inState.click
++ if (self.isInStateTrue()) self.enter(self)
++ else self.leave(self)
++ } else {
++ self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
++ }
++ }
++
++ Tooltip.prototype.destroy = function () {
++ var that = this
++ clearTimeout(this.timeout)
++ this.hide(function () {
++ that.$element.off('.' + that.type).removeData('bs.' + that.type)
++ if (that.$tip) {
++ that.$tip.detach()
++ }
++ that.$tip = null
++ that.$arrow = null
++ that.$viewport = null
++ })
++ }
++
++
++ // TOOLTIP PLUGIN DEFINITION
++ // =========================
++
++ function Plugin(option) {
++ return this.each(function () {
++ var $this = $(this)
++ var data = $this.data('bs.tooltip')
++ var options = typeof option == 'object' && option
++
++ if (!data && /destroy|hide/.test(option)) return
++ if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
++ if (typeof option == 'string') data[option]()
++ })
++ }
++
++ var old = $.fn.tooltip
++
++ $.fn.tooltip = Plugin
++ $.fn.tooltip.Constructor = Tooltip
++
++
++ // TOOLTIP NO CONFLICT
++ // ===================
++
++ $.fn.tooltip.noConflict = function () {
++ $.fn.tooltip = old
++ return this
++ }
++
++}(jQuery);
++
++/* ========================================================================
++ * Bootstrap: popover.js v3.3.6
++ * http://getbootstrap.com/javascript/#popovers
++ * ========================================================================
++ * Copyright 2011-2015 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
++ * ======================================================================== */
++
++
+++function ($) {
++ 'use strict';
++
++ // POPOVER PUBLIC CLASS DEFINITION
++ // ===============================
++
++ var Popover = function (element, options) {
++ this.init('popover', element, options)
++ }
++
++ if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
++
++ Popover.VERSION = '3.3.6'
++
++ Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {
++ placement: 'right',
++ trigger: 'click',
++ content: '',
++ template: '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
++ })
++
++
++ // NOTE: POPOVER EXTENDS tooltip.js
++ // ================================
++
++ Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype)
++
++ Popover.prototype.constructor = Popover
++
++ Popover.prototype.getDefaults = function () {
++ return Popover.DEFAULTS
++ }
++
++ Popover.prototype.setContent = function () {
++ var $tip = this.tip()
++ var title = this.getTitle()
++ var content = this.getContent()
++
++ $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
++ $tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events
++ this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text'
++ ](content)
++
++ $tip.removeClass('fade top bottom left right in')
++
++ // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do
++ // this manually by checking the contents.
++ if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide()
++ }
++
++ Popover.prototype.hasContent = function () {
++ return this.getTitle() || this.getContent()
++ }
++
++ Popover.prototype.getContent = function () {
++ var $e = this.$element
++ var o = this.options
++
++ return $e.attr('data-content')
++ || (typeof o.content == 'function' ?
++ o.content.call($e[0]) :
++ o.content)
++ }
++
++ Popover.prototype.arrow = function () {
++ return (this.$arrow = this.$arrow || this.tip().find('.arrow'))
++ }
++
++
++ // POPOVER PLUGIN DEFINITION
++ // =========================
++
++ function Plugin(option) {
++ return this.each(function () {
++ var $this = $(this)
++ var data = $this.data('bs.popover')
++ var options = typeof option == 'object' && option
++
++ if (!data && /destroy|hide/.test(option)) return
++ if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
++ if (typeof option == 'string') data[option]()
++ })
++ }
++
++ var old = $.fn.popover
++
++ $.fn.popover = Plugin
++ $.fn.popover.Constructor = Popover
++
++
++ // POPOVER NO CONFLICT
++ // ===================
++
++ $.fn.popover.noConflict = function () {
++ $.fn.popover = old
++ return this
++ }
++
++}(jQuery);
++
++/* ========================================================================
++ * Bootstrap: scrollspy.js v3.3.6
++ * http://getbootstrap.com/javascript/#scrollspy
++ * ========================================================================
++ * Copyright 2011-2015 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
++ * ======================================================================== */
++
++
+++function ($) {
++ 'use strict';
++
++ // SCROLLSPY CLASS DEFINITION
++ // ==========================
++
++ function ScrollSpy(element, options) {
++ this.$body = $(document.body)
++ this.$scrollElement = $(element).is(document.body) ? $(window) : $(element)
++ this.options = $.extend({}, ScrollSpy.DEFAULTS, options)
++ this.selector = (this.options.target || '') + ' .nav li > a'
++ this.offsets = []
++ this.targets = []
++ this.activeTarget = null
++ this.scrollHeight = 0
++
++ this.$scrollElement.on('scroll.bs.scrollspy', $.proxy(this.process, this))
++ this.refresh()
++ this.process()
++ }
++
++ ScrollSpy.VERSION = '3.3.6'
++
++ ScrollSpy.DEFAULTS = {
++ offset: 10
++ }
++
++ ScrollSpy.prototype.getScrollHeight = function () {
++ return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight)
++ }
++
++ ScrollSpy.prototype.refresh = function () {
++ var that = this
++ var offsetMethod = 'offset'
++ var offsetBase = 0
++
++ this.offsets = []
++ this.targets = []
++ this.scrollHeight = this.getScrollHeight()
++
++ if (!$.isWindow(this.$scrollElement[0])) {
++ offsetMethod = 'position'
++ offsetBase = this.$scrollElement.scrollTop()
++ }
++
++ this.$body
++ .find(this.selector)
++ .map(function () {
++ var $el = $(this)
++ var href = $el.data('target') || $el.attr('href')
++ var $href = /^#./.test(href) && $(href)
++
++ return ($href
++ && $href.length
++ && $href.is(':visible')
++ && [[$href[offsetMethod]().top + offsetBase, href]]) || null
++ })
++ .sort(function (a, b) { return a[0] - b[0] })
++ .each(function () {
++ that.offsets.push(this[0])
++ that.targets.push(this[1])
++ })
++ }
++
++ ScrollSpy.prototype.process = function () {
++ var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
++ var scrollHeight = this.getScrollHeight()
++ var maxScroll = this.options.offset + scrollHeight - this.$scrollElement.height()
++ var offsets = this.offsets
++ var targets = this.targets
++ var activeTarget = this.activeTarget
++ var i
++
++ if (this.scrollHeight != scrollHeight) {
++ this.refresh()
++ }
++
++ if (scrollTop >= maxScroll) {
++ return activeTarget != (i = targets[targets.length - 1]) && this.activate(i)
++ }
++
++ if (activeTarget && scrollTop < offsets[0]) {
++ this.activeTarget = null
++ return this.clear()
++ }
++
++ for (i = offsets.length; i--;) {
++ activeTarget != targets[i]
++ && scrollTop >= offsets[i]
++ && (offsets[i + 1] === undefined || scrollTop < offsets[i + 1])
++ && this.activate(targets[i])
++ }
++ }
++
++ ScrollSpy.prototype.activate = function (target) {
++ this.activeTarget = target
++
++ this.clear()
++
++ var selector = this.selector +
++ '[data-target="' + target + '"],' +
++ this.selector + '[href="' + target + '"]'
++
++ var active = $(selector)
++ .parents('li')
++ .addClass('active')
++
++ if (active.parent('.dropdown-menu').length) {
++ active = active
++ .closest('li.dropdown')
++ .addClass('active')
++ }
++
++ active.trigger('activate.bs.scrollspy')
++ }
++
++ ScrollSpy.prototype.clear = function () {
++ $(this.selector)
++ .parentsUntil(this.options.target, '.active')
++ .removeClass('active')
++ }
++
++
++ // SCROLLSPY PLUGIN DEFINITION
++ // ===========================
++
++ function Plugin(option) {
++ return this.each(function () {
++ var $this = $(this)
++ var data = $this.data('bs.scrollspy')
++ var options = typeof option == 'object' && option
++
++ if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options)))
++ if (typeof option == 'string') data[option]()
++ })
++ }
++
++ var old = $.fn.scrollspy
++
++ $.fn.scrollspy = Plugin
++ $.fn.scrollspy.Constructor = ScrollSpy
++
++
++ // SCROLLSPY NO CONFLICT
++ // =====================
++
++ $.fn.scrollspy.noConflict = function () {
++ $.fn.scrollspy = old
++ return this
++ }
++
++
++ // SCROLLSPY DATA-API
++ // ==================
++
++ $(window).on('load.bs.scrollspy.data-api', function () {
++ $('[data-spy="scroll"]').each(function () {
++ var $spy = $(this)
++ Plugin.call($spy, $spy.data())
++ })
++ })
++
++}(jQuery);
++
++/* ========================================================================
++ * Bootstrap: tab.js v3.3.6
++ * http://getbootstrap.com/javascript/#tabs
++ * ========================================================================
++ * Copyright 2011-2015 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
++ * ======================================================================== */
++
++
+++function ($) {
++ 'use strict';
++
++ // TAB CLASS DEFINITION
++ // ====================
++
++ var Tab = function (element) {
++ // jscs:disable requireDollarBeforejQueryAssignment
++ this.element = $(element)
++ // jscs:enable requireDollarBeforejQueryAssignment
++ }
++
++ Tab.VERSION = '3.3.6'
++
++ Tab.TRANSITION_DURATION = 150
++
++ Tab.prototype.show = function () {
++ var $this = this.element
++ var $ul = $this.closest('ul:not(.dropdown-menu)')
++ var selector = $this.data('target')
++
++ if (!selector) {
++ selector = $this.attr('href')
++ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
++ }
++
++ if ($this.parent('li').hasClass('active')) return
++
++ var $previous = $ul.find('.active:last a')
++ var hideEvent = $.Event('hide.bs.tab', {
++ relatedTarget: $this[0]
++ })
++ var showEvent = $.Event('show.bs.tab', {
++ relatedTarget: $previous[0]
++ })
++
++ $previous.trigger(hideEvent)
++ $this.trigger(showEvent)
++
++ if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return
++
++ var $target = $(selector)
++
++ this.activate($this.closest('li'), $ul)
++ this.activate($target, $target.parent(), function () {
++ $previous.trigger({
++ type: 'hidden.bs.tab',
++ relatedTarget: $this[0]
++ })
++ $this.trigger({
++ type: 'shown.bs.tab',
++ relatedTarget: $previous[0]
++ })
++ })
++ }
++
++ Tab.prototype.activate = function (element, container, callback) {
++ var $active = container.find('> .active')
++ var transition = callback
++ && $.support.transition
++ && ($active.length && $active.hasClass('fade') || !!container.find('> .fade').length)
++
++ function next() {
++ $active
++ .removeClass('active')
++ .find('> .dropdown-menu > .active')
++ .removeClass('active')
++ .end()
++ .find('[data-toggle="tab"]')
++ .attr('aria-expanded', false)
++
++ element
++ .addClass('active')
++ .find('[data-toggle="tab"]')
++ .attr('aria-expanded', true)
++
++ if (transition) {
++ element[0].offsetWidth // reflow for transition
++ element.addClass('in')
++ } else {
++ element.removeClass('fade')
++ }
++
++ if (element.parent('.dropdown-menu').length) {
++ element
++ .closest('li.dropdown')
++ .addClass('active')
++ .end()
++ .find('[data-toggle="tab"]')
++ .attr('aria-expanded', true)
++ }
++
++ callback && callback()
++ }
++
++ $active.length && transition ?
++ $active
++ .one('bsTransitionEnd', next)
++ .emulateTransitionEnd(Tab.TRANSITION_DURATION) :
++ next()
++
++ $active.removeClass('in')
++ }
++
++
++ // TAB PLUGIN DEFINITION
++ // =====================
++
++ function Plugin(option) {
++ return this.each(function () {
++ var $this = $(this)
++ var data = $this.data('bs.tab')
++
++ if (!data) $this.data('bs.tab', (data = new Tab(this)))
++ if (typeof option == 'string') data[option]()
++ })
++ }
++
++ var old = $.fn.tab
++
++ $.fn.tab = Plugin
++ $.fn.tab.Constructor = Tab
++
++
++ // TAB NO CONFLICT
++ // ===============
++
++ $.fn.tab.noConflict = function () {
++ $.fn.tab = old
++ return this
++ }
++
++
++ // TAB DATA-API
++ // ============
++
++ var clickHandler = function (e) {
++ e.preventDefault()
++ Plugin.call($(this), 'show')
++ }
++
++ $(document)
++ .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler)
++ .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler)
++
++}(jQuery);
++
++/* ========================================================================
++ * Bootstrap: affix.js v3.3.6
++ * http://getbootstrap.com/javascript/#affix
++ * ========================================================================
++ * Copyright 2011-2015 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
++ * ======================================================================== */
++
++
+++function ($) {
++ 'use strict';
++
++ // AFFIX CLASS DEFINITION
++ // ======================
++
++ var Affix = function (element, options) {
++ this.options = $.extend({}, Affix.DEFAULTS, options)
++
++ this.$target = $(this.options.target)
++ .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
++ .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this))
++
++ this.$element = $(element)
++ this.affixed = null
++ this.unpin = null
++ this.pinnedOffset = null
++
++ this.checkPosition()
++ }
++
++ Affix.VERSION = '3.3.6'
++
++ Affix.RESET = 'affix affix-top affix-bottom'
++
++ Affix.DEFAULTS = {
++ offset: 0,
++ target: window
++ }
++
++ Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) {
++ var scrollTop = this.$target.scrollTop()
++ var position = this.$element.offset()
++ var targetHeight = this.$target.height()
++
++ if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false
++
++ if (this.affixed == 'bottom') {
++ if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom'
++ return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom'
++ }
++
++ var initializing = this.affixed == null
++ var colliderTop = initializing ? scrollTop : position.top
++ var colliderHeight = initializing ? targetHeight : height
++
++ if (offsetTop != null && scrollTop <= offsetTop) return 'top'
++ if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom'
++
++ return false
++ }
++
++ Affix.prototype.getPinnedOffset = function () {
++ if (this.pinnedOffset) return this.pinnedOffset
++ this.$element.removeClass(Affix.RESET).addClass('affix')
++ var scrollTop = this.$target.scrollTop()
++ var position = this.$element.offset()
++ return (this.pinnedOffset = position.top - scrollTop)
++ }
++
++ Affix.prototype.checkPositionWithEventLoop = function () {
++ setTimeout($.proxy(this.checkPosition, this), 1)
++ }
++
++ Affix.prototype.checkPosition = function () {
++ if (!this.$element.is(':visible')) return
++
++ var height = this.$element.height()
++ var offset = this.options.offset
++ var offsetTop = offset.top
++ var offsetBottom = offset.bottom
++ var scrollHeight = Math.max($(document).height(), $(document.body).height())
++
++ if (typeof offset != 'object') offsetBottom = offsetTop = offset
++ if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element)
++ if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element)
++
++ var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom)
++
++ if (this.affixed != affix) {
++ if (this.unpin != null) this.$element.css('top', '')
++
++ var affixType = 'affix' + (affix ? '-' + affix : '')
++ var e = $.Event(affixType + '.bs.affix')
++
++ this.$element.trigger(e)
++
++ if (e.isDefaultPrevented()) return
++
++ this.affixed = affix
++ this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null
++
++ this.$element
++ .removeClass(Affix.RESET)
++ .addClass(affixType)
++ .trigger(affixType.replace('affix', 'affixed') + '.bs.affix')
++ }
++
++ if (affix == 'bottom') {
++ this.$element.offset({
++ top: scrollHeight - height - offsetBottom
++ })
++ }
++ }
++
++
++ // AFFIX PLUGIN DEFINITION
++ // =======================
++
++ function Plugin(option) {
++ return this.each(function () {
++ var $this = $(this)
++ var data = $this.data('bs.affix')
++ var options = typeof option == 'object' && option
++
++ if (!data) $this.data('bs.affix', (data = new Affix(this, options)))
++ if (typeof option == 'string') data[option]()
++ })
++ }
++
++ var old = $.fn.affix
++
++ $.fn.affix = Plugin
++ $.fn.affix.Constructor = Affix
++
++
++ // AFFIX NO CONFLICT
++ // =================
++
++ $.fn.affix.noConflict = function () {
++ $.fn.affix = old
++ return this
++ }
++
++
++ // AFFIX DATA-API
++ // ==============
++
++ $(window).on('load', function () {
++ $('[data-spy="affix"]').each(function () {
++ var $spy = $(this)
++ var data = $spy.data()
++
++ data.offset = data.offset || {}
++
++ if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom
++ if (data.offsetTop != null) data.offset.top = data.offsetTop
++
++ Plugin.call($spy, data)
++ })
++ })
++
++}(jQuery);
--- /dev/null
--- /dev/null
++/*! AdminLTE app.js
++ * ================
++ * Main JS application file for AdminLTE v2. This file
++ * should be included in all pages. It controls some layout
++ * options and implements exclusive AdminLTE plugins.
++ *
++ * @Author Almsaeed Studio
++ * @Support <http://www.almsaeedstudio.com>
++ * @Email <abdullah@almsaeedstudio.com>
++ * @version 2.3.7
++ * @license MIT <http://opensource.org/licenses/MIT>
++ */
++
++//Make sure jQuery has been loaded before app.js
++if (typeof jQuery === "undefined") {
++ throw new Error("AdminLTE requires jQuery");
++}
++
++/* AdminLTE
++ *
++ * @type Object
++ * @description $.AdminLTE is the main object for the template's app.
++ * It's used for implementing functions and options related
++ * to the template. Keeping everything wrapped in an object
++ * prevents conflict with other plugins and is a better
++ * way to organize our code.
++ */
++$.AdminLTE = {};
++
++/* --------------------
++ * - AdminLTE Options -
++ * --------------------
++ * Modify these options to suit your implementation
++ */
++$.AdminLTE.options = {
++ //Add slimscroll to navbar menus
++ //This requires you to load the slimscroll plugin
++ //in every page before app.js
++ navbarMenuSlimscroll: true,
++ navbarMenuSlimscrollWidth: "3px", //The width of the scroll bar
++ navbarMenuHeight: "200px", //The height of the inner menu
++ //General animation speed for JS animated elements such as box collapse/expand and
++ //sidebar treeview slide up/down. This options accepts an integer as milliseconds,
++ //'fast', 'normal', or 'slow'
++ animationSpeed: 500,
++ //Sidebar push menu toggle button selector
++ sidebarToggleSelector: "[data-toggle='offcanvas']",
++ //Activate sidebar push menu
++ sidebarPushMenu: true,
++ //Activate sidebar slimscroll if the fixed layout is set (requires SlimScroll Plugin)
++ sidebarSlimScroll: true,
++ //Enable sidebar expand on hover effect for sidebar mini
++ //This option is forced to true if both the fixed layout and sidebar mini
++ //are used together
++ sidebarExpandOnHover: false,
++ //BoxRefresh Plugin
++ enableBoxRefresh: true,
++ //Bootstrap.js tooltip
++ enableBSToppltip: true,
++ BSTooltipSelector: "[data-toggle='tooltip']",
++ //Enable Fast Click. Fastclick.js creates a more
++ //native touch experience with touch devices. If you
++ //choose to enable the plugin, make sure you load the script
++ //before AdminLTE's app.js
++ enableFastclick: false,
++ //Control Sidebar Options
++ enableControlSidebar: true,
++ controlSidebarOptions: {
++ //Which button should trigger the open/close event
++ toggleBtnSelector: "[data-toggle='control-sidebar']",
++ //The sidebar selector
++ selector: ".control-sidebar",
++ //Enable slide over content
++ slide: true
++ },
++ //Box Widget Plugin. Enable this plugin
++ //to allow boxes to be collapsed and/or removed
++ enableBoxWidget: true,
++ //Box Widget plugin options
++ boxWidgetOptions: {
++ boxWidgetIcons: {
++ //Collapse icon
++ collapse: 'fa-minus',
++ //Open icon
++ open: 'fa-plus',
++ //Remove icon
++ remove: 'fa-times'
++ },
++ boxWidgetSelectors: {
++ //Remove button selector
++ remove: '[data-widget="remove"]',
++ //Collapse button selector
++ collapse: '[data-widget="collapse"]'
++ }
++ },
++ //Direct Chat plugin options
++ directChat: {
++ //Enable direct chat by default
++ enable: true,
++ //The button to open and close the chat contacts pane
++ contactToggleSelector: '[data-widget="chat-pane-toggle"]'
++ },
++ //Define the set of colors to use globally around the website
++ colors: {
++ lightBlue: "#3c8dbc",
++ red: "#f56954",
++ green: "#00a65a",
++ aqua: "#00c0ef",
++ yellow: "#f39c12",
++ blue: "#0073b7",
++ navy: "#001F3F",
++ teal: "#39CCCC",
++ olive: "#3D9970",
++ lime: "#01FF70",
++ orange: "#FF851B",
++ fuchsia: "#F012BE",
++ purple: "#8E24AA",
++ maroon: "#D81B60",
++ black: "#222222",
++ gray: "#d2d6de"
++ },
++ //The standard screen sizes that bootstrap uses.
++ //If you change these in the variables.less file, change
++ //them here too.
++ screenSizes: {
++ xs: 480,
++ sm: 768,
++ md: 992,
++ lg: 1200
++ }
++};
++
++/* ------------------
++ * - Implementation -
++ * ------------------
++ * The next block of code implements AdminLTE's
++ * functions and plugins as specified by the
++ * options above.
++ */
++$(function () {
++ "use strict";
++
++ //Fix for IE page transitions
++ $("body").removeClass("hold-transition");
++
++ //Extend options if external options exist
++ if (typeof AdminLTEOptions !== "undefined") {
++ $.extend(true,
++ $.AdminLTE.options,
++ AdminLTEOptions);
++ }
++
++ //Easy access to options
++ var o = $.AdminLTE.options;
++
++ //Set up the object
++ _init();
++
++ //Activate the layout maker
++ $.AdminLTE.layout.activate();
++
++ //Enable sidebar tree view controls
++ $.AdminLTE.tree('.sidebar');
++
++ //Enable control sidebar
++ if (o.enableControlSidebar) {
++ $.AdminLTE.controlSidebar.activate();
++ }
++
++ //Add slimscroll to navbar dropdown
++ if (o.navbarMenuSlimscroll && typeof $.fn.slimscroll != 'undefined') {
++ $(".navbar .menu").slimscroll({
++ height: o.navbarMenuHeight,
++ alwaysVisible: false,
++ size: o.navbarMenuSlimscrollWidth
++ }).css("width", "100%");
++ }
++
++ //Activate sidebar push menu
++ if (o.sidebarPushMenu) {
++ $.AdminLTE.pushMenu.activate(o.sidebarToggleSelector);
++ }
++
++ //Activate Bootstrap tooltip
++ if (o.enableBSToppltip) {
++ $('body').tooltip({
++ selector: o.BSTooltipSelector
++ });
++ }
++
++ //Activate box widget
++ if (o.enableBoxWidget) {
++ $.AdminLTE.boxWidget.activate();
++ }
++
++ //Activate fast click
++ if (o.enableFastclick && typeof FastClick != 'undefined') {
++ FastClick.attach(document.body);
++ }
++
++ //Activate direct chat widget
++ if (o.directChat.enable) {
++ $(document).on('click', o.directChat.contactToggleSelector, function () {
++ var box = $(this).parents('.direct-chat').first();
++ box.toggleClass('direct-chat-contacts-open');
++ });
++ }
++
++ /*
++ * INITIALIZE BUTTON TOGGLE
++ * ------------------------
++ */
++ $('.btn-group[data-toggle="btn-toggle"]').each(function () {
++ var group = $(this);
++ $(this).find(".btn").on('click', function (e) {
++ group.find(".btn.active").removeClass("active");
++ $(this).addClass("active");
++ e.preventDefault();
++ });
++
++ });
++});
++
++/* ----------------------------------
++ * - Initialize the AdminLTE Object -
++ * ----------------------------------
++ * All AdminLTE functions are implemented below.
++ */
++function _init() {
++ 'use strict';
++ /* Layout
++ * ======
++ * Fixes the layout height in case min-height fails.
++ *
++ * @type Object
++ * @usage $.AdminLTE.layout.activate()
++ * $.AdminLTE.layout.fix()
++ * $.AdminLTE.layout.fixSidebar()
++ */
++ $.AdminLTE.layout = {
++ activate: function () {
++ var _this = this;
++ _this.fix();
++ _this.fixSidebar();
++ $(window, ".wrapper").resize(function () {
++ _this.fix();
++ _this.fixSidebar();
++ });
++ },
++ fix: function () {
++ //Get window height and the wrapper height
++ var neg = $('.main-header').outerHeight() + $('.main-footer').outerHeight();
++ var window_height = $(window).height();
++ var sidebar_height = $(".sidebar").height();
++ //Set the min-height of the content and sidebar based on the
++ //the height of the document.
++ if ($("body").hasClass("fixed")) {
++ $(".content-wrapper, .right-side").css('min-height', window_height - $('.main-footer').outerHeight());
++ } else {
++ var postSetWidth;
++ if (window_height >= sidebar_height) {
++ $(".content-wrapper, .right-side").css('min-height', window_height - neg);
++ postSetWidth = window_height - neg;
++ } else {
++ $(".content-wrapper, .right-side").css('min-height', sidebar_height);
++ postSetWidth = sidebar_height;
++ }
++
++ //Fix for the control sidebar height
++ var controlSidebar = $($.AdminLTE.options.controlSidebarOptions.selector);
++ if (typeof controlSidebar !== "undefined") {
++ if (controlSidebar.height() > postSetWidth)
++ $(".content-wrapper, .right-side").css('min-height', controlSidebar.height());
++ }
++
++ }
++ },
++ fixSidebar: function () {
++ //Make sure the body tag has the .fixed class
++ if (!$("body").hasClass("fixed")) {
++ if (typeof $.fn.slimScroll != 'undefined') {
++ $(".sidebar").slimScroll({destroy: true}).height("auto");
++ }
++ return;
++ } else if (typeof $.fn.slimScroll == 'undefined' && window.console) {
++ window.console.error("Error: the fixed layout requires the slimscroll plugin!");
++ }
++ //Enable slimscroll for fixed layout
++ if ($.AdminLTE.options.sidebarSlimScroll) {
++ if (typeof $.fn.slimScroll != 'undefined') {
++ //Destroy if it exists
++ $(".sidebar").slimScroll({destroy: true}).height("auto");
++ //Add slimscroll
++ $(".sidebar").slimscroll({
++ height: ($(window).height() - $(".main-header").height()) + "px",
++ color: "rgba(0,0,0,0.2)",
++ size: "3px"
++ });
++ }
++ }
++ }
++ };
++
++ /* PushMenu()
++ * ==========
++ * Adds the push menu functionality to the sidebar.
++ *
++ * @type Function
++ * @usage: $.AdminLTE.pushMenu("[data-toggle='offcanvas']")
++ */
++ $.AdminLTE.pushMenu = {
++ activate: function (toggleBtn) {
++ //Get the screen sizes
++ var screenSizes = $.AdminLTE.options.screenSizes;
++
++ //Enable sidebar toggle
++ $(document).on('click', toggleBtn, function (e) {
++ e.preventDefault();
++
++ //Enable sidebar push menu
++ if ($(window).width() > (screenSizes.sm - 1)) {
++ if ($("body").hasClass('sidebar-collapse')) {
++ $("body").removeClass('sidebar-collapse').trigger('expanded.pushMenu');
++ } else {
++ $("body").addClass('sidebar-collapse').trigger('collapsed.pushMenu');
++ }
++ }
++ //Handle sidebar push menu for small screens
++ else {
++ if ($("body").hasClass('sidebar-open')) {
++ $("body").removeClass('sidebar-open').removeClass('sidebar-collapse').trigger('collapsed.pushMenu');
++ } else {
++ $("body").addClass('sidebar-open').trigger('expanded.pushMenu');
++ }
++ }
++ });
++
++ $(".content-wrapper").click(function () {
++ //Enable hide menu when clicking on the content-wrapper on small screens
++ if ($(window).width() <= (screenSizes.sm - 1) && $("body").hasClass("sidebar-open")) {
++ $("body").removeClass('sidebar-open');
++ }
++ });
++
++ //Enable expand on hover for sidebar mini
++ if ($.AdminLTE.options.sidebarExpandOnHover
++ || ($('body').hasClass('fixed')
++ && $('body').hasClass('sidebar-mini'))) {
++ this.expandOnHover();
++ }
++ },
++ expandOnHover: function () {
++ var _this = this;
++ var screenWidth = $.AdminLTE.options.screenSizes.sm - 1;
++ //Expand sidebar on hover
++ $('.main-sidebar').hover(function () {
++ if ($('body').hasClass('sidebar-mini')
++ && $("body").hasClass('sidebar-collapse')
++ && $(window).width() > screenWidth) {
++ _this.expand();
++ }
++ }, function () {
++ if ($('body').hasClass('sidebar-mini')
++ && $('body').hasClass('sidebar-expanded-on-hover')
++ && $(window).width() > screenWidth) {
++ _this.collapse();
++ }
++ });
++ },
++ expand: function () {
++ $("body").removeClass('sidebar-collapse').addClass('sidebar-expanded-on-hover');
++ },
++ collapse: function () {
++ if ($('body').hasClass('sidebar-expanded-on-hover')) {
++ $('body').removeClass('sidebar-expanded-on-hover').addClass('sidebar-collapse');
++ }
++ }
++ };
++
++ /* Tree()
++ * ======
++ * Converts the sidebar into a multilevel
++ * tree view menu.
++ *
++ * @type Function
++ * @Usage: $.AdminLTE.tree('.sidebar')
++ */
++ $.AdminLTE.tree = function (menu) {
++ var _this = this;
++ var animationSpeed = $.AdminLTE.options.animationSpeed;
++ $(document).off('click', menu + ' li a')
++ .on('click', menu + ' li a', function (e) {
++ //Get the clicked link and the next element
++ var $this = $(this);
++ var checkElement = $this.next();
++
++ //Check if the next element is a menu and is visible
++ if ((checkElement.is('.treeview-menu')) && (checkElement.is(':visible')) && (!$('body').hasClass('sidebar-collapse'))) {
++ //Close the menu
++ checkElement.slideUp(animationSpeed, function () {
++ checkElement.removeClass('menu-open');
++ //Fix the layout in case the sidebar stretches over the height of the window
++ //_this.layout.fix();
++ });
++ checkElement.parent("li").removeClass("active");
++ }
++ //If the menu is not visible
++ else if ((checkElement.is('.treeview-menu')) && (!checkElement.is(':visible'))) {
++ //Get the parent menu
++ var parent = $this.parents('ul').first();
++ //Close all open menus within the parent
++ var ul = parent.find('ul:visible').slideUp(animationSpeed);
++ //Remove the menu-open class from the parent
++ ul.removeClass('menu-open');
++ //Get the parent li
++ var parent_li = $this.parent("li");
++
++ //Open the target menu and add the menu-open class
++ checkElement.slideDown(animationSpeed, function () {
++ //Add the class active to the parent li
++ checkElement.addClass('menu-open');
++ parent.find('li.active').removeClass('active');
++ parent_li.addClass('active');
++ //Fix the layout in case the sidebar stretches over the height of the window
++ _this.layout.fix();
++ });
++ }
++ //if this isn't a link, prevent the page from being redirected
++ if (checkElement.is('.treeview-menu')) {
++ e.preventDefault();
++ }
++ });
++ };
++
++ /* ControlSidebar
++ * ==============
++ * Adds functionality to the right sidebar
++ *
++ * @type Object
++ * @usage $.AdminLTE.controlSidebar.activate(options)
++ */
++ $.AdminLTE.controlSidebar = {
++ //instantiate the object
++ activate: function () {
++ //Get the object
++ var _this = this;
++ //Update options
++ var o = $.AdminLTE.options.controlSidebarOptions;
++ //Get the sidebar
++ var sidebar = $(o.selector);
++ //The toggle button
++ var btn = $(o.toggleBtnSelector);
++
++ //Listen to the click event
++ btn.on('click', function (e) {
++ e.preventDefault();
++ //If the sidebar is not open
++ if (!sidebar.hasClass('control-sidebar-open')
++ && !$('body').hasClass('control-sidebar-open')) {
++ //Open the sidebar
++ _this.open(sidebar, o.slide);
++ } else {
++ _this.close(sidebar, o.slide);
++ }
++ });
++
++ //If the body has a boxed layout, fix the sidebar bg position
++ var bg = $(".control-sidebar-bg");
++ _this._fix(bg);
++
++ //If the body has a fixed layout, make the control sidebar fixed
++ if ($('body').hasClass('fixed')) {
++ _this._fixForFixed(sidebar);
++ } else {
++ //If the content height is less than the sidebar's height, force max height
++ if ($('.content-wrapper, .right-side').height() < sidebar.height()) {
++ _this._fixForContent(sidebar);
++ }
++ }
++ },
++ //Open the control sidebar
++ open: function (sidebar, slide) {
++ //Slide over content
++ if (slide) {
++ sidebar.addClass('control-sidebar-open');
++ } else {
++ //Push the content by adding the open class to the body instead
++ //of the sidebar itself
++ $('body').addClass('control-sidebar-open');
++ }
++ },
++ //Close the control sidebar
++ close: function (sidebar, slide) {
++ if (slide) {
++ sidebar.removeClass('control-sidebar-open');
++ } else {
++ $('body').removeClass('control-sidebar-open');
++ }
++ },
++ _fix: function (sidebar) {
++ var _this = this;
++ if ($("body").hasClass('layout-boxed')) {
++ sidebar.css('position', 'absolute');
++ sidebar.height($(".wrapper").height());
++ if (_this.hasBindedResize) {
++ return;
++ }
++ $(window).resize(function () {
++ _this._fix(sidebar);
++ });
++ _this.hasBindedResize = true;
++ } else {
++ sidebar.css({
++ 'position': 'fixed',
++ 'height': 'auto'
++ });
++ }
++ },
++ _fixForFixed: function (sidebar) {
++ sidebar.css({
++ 'position': 'fixed',
++ 'max-height': '100%',
++ 'overflow': 'auto',
++ 'padding-bottom': '50px'
++ });
++ },
++ _fixForContent: function (sidebar) {
++ $(".content-wrapper, .right-side").css('min-height', sidebar.height());
++ }
++ };
++
++ /* BoxWidget
++ * =========
++ * BoxWidget is a plugin to handle collapsing and
++ * removing boxes from the screen.
++ *
++ * @type Object
++ * @usage $.AdminLTE.boxWidget.activate()
++ * Set all your options in the main $.AdminLTE.options object
++ */
++ $.AdminLTE.boxWidget = {
++ selectors: $.AdminLTE.options.boxWidgetOptions.boxWidgetSelectors,
++ icons: $.AdminLTE.options.boxWidgetOptions.boxWidgetIcons,
++ animationSpeed: $.AdminLTE.options.animationSpeed,
++ activate: function (_box) {
++ var _this = this;
++ if (!_box) {
++ _box = document; // activate all boxes per default
++ }
++ //Listen for collapse event triggers
++ $(_box).on('click', _this.selectors.collapse, function (e) {
++ e.preventDefault();
++ _this.collapse($(this));
++ });
++
++ //Listen for remove event triggers
++ $(_box).on('click', _this.selectors.remove, function (e) {
++ e.preventDefault();
++ _this.remove($(this));
++ });
++ },
++ collapse: function (element) {
++ var _this = this;
++ //Find the box parent
++ var box = element.parents(".box").first();
++ //Find the body and the footer
++ var box_content = box.find("> .box-body, > .box-footer, > form >.box-body, > form > .box-footer");
++ if (!box.hasClass("collapsed-box")) {
++ //Convert minus into plus
++ element.children(":first")
++ .removeClass(_this.icons.collapse)
++ .addClass(_this.icons.open);
++ //Hide the content
++ box_content.slideUp(_this.animationSpeed, function () {
++ box.addClass("collapsed-box");
++ });
++ } else {
++ //Convert plus into minus
++ element.children(":first")
++ .removeClass(_this.icons.open)
++ .addClass(_this.icons.collapse);
++ //Show the content
++ box_content.slideDown(_this.animationSpeed, function () {
++ box.removeClass("collapsed-box");
++ });
++ }
++ },
++ remove: function (element) {
++ //Find the box parent
++ var box = element.parents(".box").first();
++ box.slideUp(this.animationSpeed);
++ }
++ };
++}
++
++/* ------------------
++ * - Custom Plugins -
++ * ------------------
++ * All custom plugins are defined below.
++ */
++
++/*
++ * BOX REFRESH BUTTON
++ * ------------------
++ * This is a custom plugin to use with the component BOX. It allows you to add
++ * a refresh button to the box. It converts the box's state to a loading state.
++ *
++ * @type plugin
++ * @usage $("#box-widget").boxRefresh( options );
++ */
++(function ($) {
++
++ "use strict";
++
++ $.fn.boxRefresh = function (options) {
++
++ // Render options
++ var settings = $.extend({
++ //Refresh button selector
++ trigger: ".refresh-btn",
++ //File source to be loaded (e.g: ajax/src.php)
++ source: "",
++ //Callbacks
++ onLoadStart: function (box) {
++ return box;
++ }, //Right after the button has been clicked
++ onLoadDone: function (box) {
++ return box;
++ } //When the source has been loaded
++
++ }, options);
++
++ //The overlay
++ var overlay = $('<div class="overlay"><div class="fa fa-refresh fa-spin"></div></div>');
++
++ return this.each(function () {
++ //if a source is specified
++ if (settings.source === "") {
++ if (window.console) {
++ window.console.log("Please specify a source first - boxRefresh()");
++ }
++ return;
++ }
++ //the box
++ var box = $(this);
++ //the button
++ var rBtn = box.find(settings.trigger).first();
++
++ //On trigger click
++ rBtn.on('click', function (e) {
++ e.preventDefault();
++ //Add loading overlay
++ start(box);
++
++ //Perform ajax call
++ box.find(".box-body").load(settings.source, function () {
++ done(box);
++ });
++ });
++ });
++
++ function start(box) {
++ //Add overlay and loading img
++ box.append(overlay);
++
++ settings.onLoadStart.call(box);
++ }
++
++ function done(box) {
++ //Remove overlay and loading img
++ box.find(overlay).remove();
++
++ settings.onLoadDone.call(box);
++ }
++
++ };
++
++})(jQuery);
++
++/*
++ * EXPLICIT BOX CONTROLS
++ * -----------------------
++ * This is a custom plugin to use with the component BOX. It allows you to activate
++ * a box inserted in the DOM after the app.js was loaded, toggle and remove box.
++ *
++ * @type plugin
++ * @usage $("#box-widget").activateBox();
++ * @usage $("#box-widget").toggleBox();
++ * @usage $("#box-widget").removeBox();
++ */
++(function ($) {
++
++ 'use strict';
++
++ $.fn.activateBox = function () {
++ $.AdminLTE.boxWidget.activate(this);
++ };
++
++ $.fn.toggleBox = function () {
++ var button = $($.AdminLTE.boxWidget.selectors.collapse, this);
++ $.AdminLTE.boxWidget.collapse(button);
++ };
++
++ $.fn.removeBox = function () {
++ var button = $($.AdminLTE.boxWidget.selectors.remove, this);
++ $.AdminLTE.boxWidget.remove(button);
++ };
++
++})(jQuery);
++
++/*
++ * TODO LIST CUSTOM PLUGIN
++ * -----------------------
++ * This plugin depends on iCheck plugin for checkbox and radio inputs
++ *
++ * @type plugin
++ * @usage $("#todo-widget").todolist( options );
++ */
++(function ($) {
++
++ 'use strict';
++
++ $.fn.todolist = function (options) {
++ // Render options
++ var settings = $.extend({
++ //When the user checks the input
++ onCheck: function (ele) {
++ return ele;
++ },
++ //When the user unchecks the input
++ onUncheck: function (ele) {
++ return ele;
++ }
++ }, options);
++
++ return this.each(function () {
++
++ if (typeof $.fn.iCheck != 'undefined') {
++ $('input', this).on('ifChecked', function () {
++ var ele = $(this).parents("li").first();
++ ele.toggleClass("done");
++ settings.onCheck.call(ele);
++ });
++
++ $('input', this).on('ifUnchecked', function () {
++ var ele = $(this).parents("li").first();
++ ele.toggleClass("done");
++ settings.onUncheck.call(ele);
++ });
++ } else {
++ $('input', this).on('change', function () {
++ var ele = $(this).parents("li").first();
++ ele.toggleClass("done");
++ if ($('input', ele).is(":checked")) {
++ settings.onCheck.call(ele);
++ } else {
++ settings.onUncheck.call(ele);
++ }
++ });
++ }
++ });
++ };
++}(jQuery));
--- /dev/null
--- /dev/null
++/*! DataTables Bootstrap 3 integration
++ * ©2011-2014 SpryMedia Ltd - datatables.net/license
++ */
++
++/**
++ * DataTables integration for Bootstrap 3. This requires Bootstrap 3 and
++ * DataTables 1.10 or newer.
++ *
++ * This file sets the defaults and adds options to DataTables to style its
++ * controls using Bootstrap. See http://datatables.net/manual/styling/bootstrap
++ * for further information.
++ */
++(function(window, document, undefined){
++
++var factory = function( $, DataTable ) {
++"use strict";
++
++
++/* Set the defaults for DataTables initialisation */
++$.extend( true, DataTable.defaults, {
++ dom:
++ "<'row'<'col-sm-6'l><'col-sm-6'f>>" +
++ "<'row'<'col-sm-12'tr>>" +
++ "<'row'<'col-sm-5'i><'col-sm-7'p>>",
++ renderer: 'bootstrap'
++} );
++
++
++/* Default class modification */
++$.extend( DataTable.ext.classes, {
++ sWrapper: "dataTables_wrapper form-inline dt-bootstrap",
++ sFilterInput: "form-control input-sm",
++ sLengthSelect: "form-control input-sm"
++} );
++
++
++/* Bootstrap paging button renderer */
++DataTable.ext.renderer.pageButton.bootstrap = function ( settings, host, idx, buttons, page, pages ) {
++ var api = new DataTable.Api( settings );
++ var classes = settings.oClasses;
++ var lang = settings.oLanguage.oPaginate;
++ var btnDisplay, btnClass, counter=0;
++
++ var attach = function( container, buttons ) {
++ var i, ien, node, button;
++ var clickHandler = function ( e ) {
++ e.preventDefault();
++ if ( !$(e.currentTarget).hasClass('disabled') ) {
++ api.page( e.data.action ).draw( false );
++ }
++ };
++
++ for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
++ button = buttons[i];
++
++ if ( $.isArray( button ) ) {
++ attach( container, button );
++ }
++ else {
++ btnDisplay = '';
++ btnClass = '';
++
++ switch ( button ) {
++ case 'ellipsis':
++ btnDisplay = '…';
++ btnClass = 'disabled';
++ break;
++
++ case 'first':
++ btnDisplay = lang.sFirst;
++ btnClass = button + (page > 0 ?
++ '' : ' disabled');
++ break;
++
++ case 'previous':
++ btnDisplay = lang.sPrevious;
++ btnClass = button + (page > 0 ?
++ '' : ' disabled');
++ break;
++
++ case 'next':
++ btnDisplay = lang.sNext;
++ btnClass = button + (page < pages-1 ?
++ '' : ' disabled');
++ break;
++
++ case 'last':
++ btnDisplay = lang.sLast;
++ btnClass = button + (page < pages-1 ?
++ '' : ' disabled');
++ break;
++
++ default:
++ btnDisplay = button + 1;
++ btnClass = page === button ?
++ 'active' : '';
++ break;
++ }
++
++ if ( btnDisplay ) {
++ node = $('<li>', {
++ 'class': classes.sPageButton+' '+btnClass,
++ 'id': idx === 0 && typeof button === 'string' ?
++ settings.sTableId +'_'+ button :
++ null
++ } )
++ .append( $('<a>', {
++ 'href': '#',
++ 'aria-controls': settings.sTableId,
++ 'data-dt-idx': counter,
++ 'tabindex': settings.iTabIndex
++ } )
++ .html( btnDisplay )
++ )
++ .appendTo( container );
++
++ settings.oApi._fnBindAction(
++ node, {action: button}, clickHandler
++ );
++
++ counter++;
++ }
++ }
++ }
++ };
++
++ // IE9 throws an 'unknown error' if document.activeElement is used
++ // inside an iframe or frame.
++ var activeEl;
++
++ try {
++ // Because this approach is destroying and recreating the paging
++ // elements, focus is lost on the select button which is bad for
++ // accessibility. So we want to restore focus once the draw has
++ // completed
++ activeEl = $(document.activeElement).data('dt-idx');
++ }
++ catch (e) {}
++
++ attach(
++ $(host).empty().html('<ul class="pagination"/>').children('ul'),
++ buttons
++ );
++
++ if ( activeEl ) {
++ $(host).find( '[data-dt-idx='+activeEl+']' ).focus();
++ }
++};
++
++
++/*
++ * TableTools Bootstrap compatibility
++ * Required TableTools 2.1+
++ */
++if ( DataTable.TableTools ) {
++ // Set the classes that TableTools uses to something suitable for Bootstrap
++ $.extend( true, DataTable.TableTools.classes, {
++ "container": "DTTT btn-group",
++ "buttons": {
++ "normal": "btn btn-default",
++ "disabled": "disabled"
++ },
++ "collection": {
++ "container": "DTTT_dropdown dropdown-menu",
++ "buttons": {
++ "normal": "",
++ "disabled": "disabled"
++ }
++ },
++ "print": {
++ "info": "DTTT_print_info"
++ },
++ "select": {
++ "row": "active"
++ }
++ } );
++
++ // Have the collection use a bootstrap compatible drop down
++ $.extend( true, DataTable.TableTools.DEFAULTS.oTags, {
++ "collection": {
++ "container": "ul",
++ "button": "li",
++ "liner": "a"
++ }
++ } );
++}
++
++}; // /factory
++
++
++// Define as an AMD module if possible
++if ( typeof define === 'function' && define.amd ) {
++ define( ['jquery', 'datatables'], factory );
++}
++else if ( typeof exports === 'object' ) {
++ // Node/CommonJS
++ factory( require('jquery'), require('datatables') );
++}
++else if ( jQuery ) {
++ // Otherwise simply initialise as normal, stopping multiple evaluation
++ factory( jQuery, jQuery.fn.dataTable );
++}
++
++
++})(window, document);
++
--- /dev/null
--- /dev/null
++/*! DataTables 1.10.7
++ * ©2008-2014 SpryMedia Ltd - datatables.net/license
++ */
++
++/**
++ * @summary DataTables
++ * @description Paginate, search and order HTML tables
++ * @version 1.10.7
++ * @file jquery.dataTables.js
++ * @author SpryMedia Ltd (www.sprymedia.co.uk)
++ * @contact www.sprymedia.co.uk/contact
++ * @copyright Copyright 2008-2014 SpryMedia Ltd.
++ *
++ * This source file is free software, available under the following license:
++ * MIT license - http://datatables.net/license
++ *
++ * This source file 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 license files for details.
++ *
++ * For details please refer to: http://www.datatables.net
++ */
++
++/*jslint evil: true, undef: true, browser: true */
++/*globals $,require,jQuery,define,_selector_run,_selector_opts,_selector_first,_selector_row_indexes,_ext,_Api,_api_register,_api_registerPlural,_re_new_lines,_re_html,_re_formatted_numeric,_re_escape_regex,_empty,_intVal,_numToDecimal,_isNumber,_isHtml,_htmlNumeric,_pluck,_pluck_order,_range,_stripHtml,_unique,_fnBuildAjax,_fnAjaxUpdate,_fnAjaxParameters,_fnAjaxUpdateDraw,_fnAjaxDataSrc,_fnAddColumn,_fnColumnOptions,_fnAdjustColumnSizing,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnVisbleColumns,_fnGetColumns,_fnColumnTypes,_fnApplyColumnDefs,_fnHungarianMap,_fnCamelToHungarian,_fnLanguageCompat,_fnBrowserDetect,_fnAddData,_fnAddTr,_fnNodeToDataIndex,_fnNodeToColumnIndex,_fnGetCellData,_fnSetCellData,_fnSplitObjNotation,_fnGetObjectDataFn,_fnSetObjectDataFn,_fnGetDataMaster,_fnClearTable,_fnDeleteIndex,_fnInvalidate,_fnGetRowElements,_fnCreateTr,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAddOptionsHtml,_fnDetectHeader,_fnGetUniqueThs,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnFilterCreateSearch,_fnEscapeRegex,_fnFilterData,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnInfoMacros,_fnInitialise,_fnInitComplete,_fnLengthChange,_fnFeatureHtmlLength,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnFeatureHtmlTable,_fnScrollDraw,_fnApplyToChildren,_fnCalculateColumnWidths,_fnThrottle,_fnConvertToWidth,_fnScrollingWidthAdjust,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnScrollBarWidth,_fnSortFlatten,_fnSort,_fnSortAria,_fnSortListener,_fnSortAttachListener,_fnSortingClasses,_fnSortData,_fnSaveState,_fnLoadState,_fnSettingsFromNode,_fnLog,_fnMap,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnLengthOverflow,_fnRenderer,_fnDataSource,_fnRowAttributes*/
++
++(/** @lends <global> */function( window, document, undefined ) {
++
++(function( factory ) {
++ "use strict";
++
++ if ( typeof define === 'function' && define.amd ) {
++ // Define as an AMD module if possible
++ define( 'datatables', ['jquery'], factory );
++ }
++ else if ( typeof exports === 'object' ) {
++ // Node/CommonJS
++ module.exports = factory( require( 'jquery' ) );
++ }
++ else if ( jQuery && !jQuery.fn.dataTable ) {
++ // Define using browser globals otherwise
++ // Prevent multiple instantiations if the script is loaded twice
++ factory( jQuery );
++ }
++}
++(/** @lends <global> */function( $ ) {
++ "use strict";
++
++ /**
++ * DataTables is a plug-in for the jQuery Javascript library. It is a highly
++ * flexible tool, based upon the foundations of progressive enhancement,
++ * which will add advanced interaction controls to any HTML table. For a
++ * full list of features please refer to
++ * [DataTables.net](href="http://datatables.net).
++ *
++ * Note that the `DataTable` object is not a global variable but is aliased
++ * to `jQuery.fn.DataTable` and `jQuery.fn.dataTable` through which it may
++ * be accessed.
++ *
++ * @class
++ * @param {object} [init={}] Configuration object for DataTables. Options
++ * are defined by {@link DataTable.defaults}
++ * @requires jQuery 1.7+
++ *
++ * @example
++ * // Basic initialisation
++ * $(document).ready( function {
++ * $('#example').dataTable();
++ * } );
++ *
++ * @example
++ * // Initialisation with configuration options - in this case, disable
++ * // pagination and sorting.
++ * $(document).ready( function {
++ * $('#example').dataTable( {
++ * "paginate": false,
++ * "sort": false
++ * } );
++ * } );
++ */
++ var DataTable;
++
++
++ /*
++ * It is useful to have variables which are scoped locally so only the
++ * DataTables functions can access them and they don't leak into global space.
++ * At the same time these functions are often useful over multiple files in the
++ * core and API, so we list, or at least document, all variables which are used
++ * by DataTables as private variables here. This also ensures that there is no
++ * clashing of variable names and that they can easily referenced for reuse.
++ */
++
++
++ // Defined else where
++ // _selector_run
++ // _selector_opts
++ // _selector_first
++ // _selector_row_indexes
++
++ var _ext; // DataTable.ext
++ var _Api; // DataTable.Api
++ var _api_register; // DataTable.Api.register
++ var _api_registerPlural; // DataTable.Api.registerPlural
++
++ var _re_dic = {};
++ var _re_new_lines = /[\r\n]/g;
++ var _re_html = /<.*?>/g;
++ var _re_date_start = /^[\w\+\-]/;
++ var _re_date_end = /[\w\+\-]$/;
++
++ // Escape regular expression special characters
++ var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' );
++
++ // http://en.wikipedia.org/wiki/Foreign_exchange_market
++ // - \u20BD - Russian ruble.
++ // - \u20a9 - South Korean Won
++ // - \u20BA - Turkish Lira
++ // - \u20B9 - Indian Rupee
++ // - R - Brazil (R$) and South Africa
++ // - fr - Swiss Franc
++ // - kr - Swedish krona, Norwegian krone and Danish krone
++ // - \u2009 is thin space and \u202F is narrow no-break space, both used in many
++ // standards as thousands separators.
++ var _re_formatted_numeric = /[',$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfk]/gi;
++
++
++ var _empty = function ( d ) {
++ return !d || d === true || d === '-' ? true : false;
++ };
++
++
++ var _intVal = function ( s ) {
++ var integer = parseInt( s, 10 );
++ return !isNaN(integer) && isFinite(s) ? integer : null;
++ };
++
++ // Convert from a formatted number with characters other than `.` as the
++ // decimal place, to a Javascript number
++ var _numToDecimal = function ( num, decimalPoint ) {
++ // Cache created regular expressions for speed as this function is called often
++ if ( ! _re_dic[ decimalPoint ] ) {
++ _re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' );
++ }
++ return typeof num === 'string' && decimalPoint !== '.' ?
++ num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) :
++ num;
++ };
++
++
++ var _isNumber = function ( d, decimalPoint, formatted ) {
++ var strType = typeof d === 'string';
++
++ // If empty return immediately so there must be a number if it is a
++ // formatted string (this stops the string "k", or "kr", etc being detected
++ // as a formatted number for currency
++ if ( _empty( d ) ) {
++ return true;
++ }
++
++ if ( decimalPoint && strType ) {
++ d = _numToDecimal( d, decimalPoint );
++ }
++
++ if ( formatted && strType ) {
++ d = d.replace( _re_formatted_numeric, '' );
++ }
++
++ return !isNaN( parseFloat(d) ) && isFinite( d );
++ };
++
++
++ // A string without HTML in it can be considered to be HTML still
++ var _isHtml = function ( d ) {
++ return _empty( d ) || typeof d === 'string';
++ };
++
++
++ var _htmlNumeric = function ( d, decimalPoint, formatted ) {
++ if ( _empty( d ) ) {
++ return true;
++ }
++
++ var html = _isHtml( d );
++ return ! html ?
++ null :
++ _isNumber( _stripHtml( d ), decimalPoint, formatted ) ?
++ true :
++ null;
++ };
++
++
++ var _pluck = function ( a, prop, prop2 ) {
++ var out = [];
++ var i=0, ien=a.length;
++
++ // Could have the test in the loop for slightly smaller code, but speed
++ // is essential here
++ if ( prop2 !== undefined ) {
++ for ( ; i<ien ; i++ ) {
++ if ( a[i] && a[i][ prop ] ) {
++ out.push( a[i][ prop ][ prop2 ] );
++ }
++ }
++ }
++ else {
++ for ( ; i<ien ; i++ ) {
++ if ( a[i] ) {
++ out.push( a[i][ prop ] );
++ }
++ }
++ }
++
++ return out;
++ };
++
++
++ // Basically the same as _pluck, but rather than looping over `a` we use `order`
++ // as the indexes to pick from `a`
++ var _pluck_order = function ( a, order, prop, prop2 )
++ {
++ var out = [];
++ var i=0, ien=order.length;
++
++ // Could have the test in the loop for slightly smaller code, but speed
++ // is essential here
++ if ( prop2 !== undefined ) {
++ for ( ; i<ien ; i++ ) {
++ if ( a[ order[i] ][ prop ] ) {
++ out.push( a[ order[i] ][ prop ][ prop2 ] );
++ }
++ }
++ }
++ else {
++ for ( ; i<ien ; i++ ) {
++ out.push( a[ order[i] ][ prop ] );
++ }
++ }
++
++ return out;
++ };
++
++
++ var _range = function ( len, start )
++ {
++ var out = [];
++ var end;
++
++ if ( start === undefined ) {
++ start = 0;
++ end = len;
++ }
++ else {
++ end = start;
++ start = len;
++ }
++
++ for ( var i=start ; i<end ; i++ ) {
++ out.push( i );
++ }
++
++ return out;
++ };
++
++
++ var _removeEmpty = function ( a )
++ {
++ var out = [];
++
++ for ( var i=0, ien=a.length ; i<ien ; i++ ) {
++ if ( a[i] ) { // careful - will remove all falsy values!
++ out.push( a[i] );
++ }
++ }
++
++ return out;
++ };
++
++
++ var _stripHtml = function ( d ) {
++ return d.replace( _re_html, '' );
++ };
++
++
++ /**
++ * Find the unique elements in a source array.
++ *
++ * @param {array} src Source array
++ * @return {array} Array of unique items
++ * @ignore
++ */
++ var _unique = function ( src )
++ {
++ // A faster unique method is to use object keys to identify used values,
++ // but this doesn't work with arrays or objects, which we must also
++ // consider. See jsperf.com/compare-array-unique-versions/4 for more
++ // information.
++ var
++ out = [],
++ val,
++ i, ien=src.length,
++ j, k=0;
++
++ again: for ( i=0 ; i<ien ; i++ ) {
++ val = src[i];
++
++ for ( j=0 ; j<k ; j++ ) {
++ if ( out[j] === val ) {
++ continue again;
++ }
++ }
++
++ out.push( val );
++ k++;
++ }
++
++ return out;
++ };
++
++
++
++ /**
++ * Create a mapping object that allows camel case parameters to be looked up
++ * for their Hungarian counterparts. The mapping is stored in a private
++ * parameter called `_hungarianMap` which can be accessed on the source object.
++ * @param {object} o
++ * @memberof DataTable#oApi
++ */
++ function _fnHungarianMap ( o )
++ {
++ var
++ hungarian = 'a aa ai ao as b fn i m o s ',
++ match,
++ newKey,
++ map = {};
++
++ $.each( o, function (key, val) {
++ match = key.match(/^([^A-Z]+?)([A-Z])/);
++
++ if ( match && hungarian.indexOf(match[1]+' ') !== -1 )
++ {
++ newKey = key.replace( match[0], match[2].toLowerCase() );
++ map[ newKey ] = key;
++
++ if ( match[1] === 'o' )
++ {
++ _fnHungarianMap( o[key] );
++ }
++ }
++ } );
++
++ o._hungarianMap = map;
++ }
++
++
++ /**
++ * Convert from camel case parameters to Hungarian, based on a Hungarian map
++ * created by _fnHungarianMap.
++ * @param {object} src The model object which holds all parameters that can be
++ * mapped.
++ * @param {object} user The object to convert from camel case to Hungarian.
++ * @param {boolean} force When set to `true`, properties which already have a
++ * Hungarian value in the `user` object will be overwritten. Otherwise they
++ * won't be.
++ * @memberof DataTable#oApi
++ */
++ function _fnCamelToHungarian ( src, user, force )
++ {
++ if ( ! src._hungarianMap ) {
++ _fnHungarianMap( src );
++ }
++
++ var hungarianKey;
++
++ $.each( user, function (key, val) {
++ hungarianKey = src._hungarianMap[ key ];
++
++ if ( hungarianKey !== undefined && (force || user[hungarianKey] === undefined) )
++ {
++ // For objects, we need to buzz down into the object to copy parameters
++ if ( hungarianKey.charAt(0) === 'o' )
++ {
++ // Copy the camelCase options over to the hungarian
++ if ( ! user[ hungarianKey ] ) {
++ user[ hungarianKey ] = {};
++ }
++ $.extend( true, user[hungarianKey], user[key] );
++
++ _fnCamelToHungarian( src[hungarianKey], user[hungarianKey], force );
++ }
++ else {
++ user[hungarianKey] = user[ key ];
++ }
++ }
++ } );
++ }
++
++
++ /**
++ * Language compatibility - when certain options are given, and others aren't, we
++ * need to duplicate the values over, in order to provide backwards compatibility
++ * with older language files.
++ * @param {object} oSettings dataTables settings object
++ * @memberof DataTable#oApi
++ */
++ function _fnLanguageCompat( lang )
++ {
++ var defaults = DataTable.defaults.oLanguage;
++ var zeroRecords = lang.sZeroRecords;
++
++ /* Backwards compatibility - if there is no sEmptyTable given, then use the same as
++ * sZeroRecords - assuming that is given.
++ */
++ if ( ! lang.sEmptyTable && zeroRecords &&
++ defaults.sEmptyTable === "No data available in table" )
++ {
++ _fnMap( lang, lang, 'sZeroRecords', 'sEmptyTable' );
++ }
++
++ /* Likewise with loading records */
++ if ( ! lang.sLoadingRecords && zeroRecords &&
++ defaults.sLoadingRecords === "Loading..." )
++ {
++ _fnMap( lang, lang, 'sZeroRecords', 'sLoadingRecords' );
++ }
++
++ // Old parameter name of the thousands separator mapped onto the new
++ if ( lang.sInfoThousands ) {
++ lang.sThousands = lang.sInfoThousands;
++ }
++
++ var decimal = lang.sDecimal;
++ if ( decimal ) {
++ _addNumericSort( decimal );
++ }
++ }
++
++
++ /**
++ * Map one parameter onto another
++ * @param {object} o Object to map
++ * @param {*} knew The new parameter name
++ * @param {*} old The old parameter name
++ */
++ var _fnCompatMap = function ( o, knew, old ) {
++ if ( o[ knew ] !== undefined ) {
++ o[ old ] = o[ knew ];
++ }
++ };
++
++
++ /**
++ * Provide backwards compatibility for the main DT options. Note that the new
++ * options are mapped onto the old parameters, so this is an external interface
++ * change only.
++ * @param {object} init Object to map
++ */
++ function _fnCompatOpts ( init )
++ {
++ _fnCompatMap( init, 'ordering', 'bSort' );
++ _fnCompatMap( init, 'orderMulti', 'bSortMulti' );
++ _fnCompatMap( init, 'orderClasses', 'bSortClasses' );
++ _fnCompatMap( init, 'orderCellsTop', 'bSortCellsTop' );
++ _fnCompatMap( init, 'order', 'aaSorting' );
++ _fnCompatMap( init, 'orderFixed', 'aaSortingFixed' );
++ _fnCompatMap( init, 'paging', 'bPaginate' );
++ _fnCompatMap( init, 'pagingType', 'sPaginationType' );
++ _fnCompatMap( init, 'pageLength', 'iDisplayLength' );
++ _fnCompatMap( init, 'searching', 'bFilter' );
++
++ // Column search objects are in an array, so it needs to be converted
++ // element by element
++ var searchCols = init.aoSearchCols;
++
++ if ( searchCols ) {
++ for ( var i=0, ien=searchCols.length ; i<ien ; i++ ) {
++ if ( searchCols[i] ) {
++ _fnCamelToHungarian( DataTable.models.oSearch, searchCols[i] );
++ }
++ }
++ }
++ }
++
++
++ /**
++ * Provide backwards compatibility for column options. Note that the new options
++ * are mapped onto the old parameters, so this is an external interface change
++ * only.
++ * @param {object} init Object to map
++ */
++ function _fnCompatCols ( init )
++ {
++ _fnCompatMap( init, 'orderable', 'bSortable' );
++ _fnCompatMap( init, 'orderData', 'aDataSort' );
++ _fnCompatMap( init, 'orderSequence', 'asSorting' );
++ _fnCompatMap( init, 'orderDataType', 'sortDataType' );
++
++ // orderData can be given as an integer
++ var dataSort = init.aDataSort;
++ if ( dataSort && ! $.isArray( dataSort ) ) {
++ init.aDataSort = [ dataSort ];
++ }
++ }
++
++
++ /**
++ * Browser feature detection for capabilities, quirks
++ * @param {object} settings dataTables settings object
++ * @memberof DataTable#oApi
++ */
++ function _fnBrowserDetect( settings )
++ {
++ var browser = settings.oBrowser;
++
++ // Scrolling feature / quirks detection
++ var n = $('<div/>')
++ .css( {
++ position: 'absolute',
++ top: 0,
++ left: 0,
++ height: 1,
++ width: 1,
++ overflow: 'hidden'
++ } )
++ .append(
++ $('<div/>')
++ .css( {
++ position: 'absolute',
++ top: 1,
++ left: 1,
++ width: 100,
++ overflow: 'scroll'
++ } )
++ .append(
++ $('<div class="test"/>')
++ .css( {
++ width: '100%',
++ height: 10
++ } )
++ )
++ )
++ .appendTo( 'body' );
++
++ var test = n.find('.test');
++
++ // IE6/7 will oversize a width 100% element inside a scrolling element, to
++ // include the width of the scrollbar, while other browsers ensure the inner
++ // element is contained without forcing scrolling
++ browser.bScrollOversize = test[0].offsetWidth === 100;
++
++ // In rtl text layout, some browsers (most, but not all) will place the
++ // scrollbar on the left, rather than the right.
++ browser.bScrollbarLeft = Math.round( test.offset().left ) !== 1;
++
++ n.remove();
++ }
++
++
++ /**
++ * Array.prototype reduce[Right] method, used for browsers which don't support
++ * JS 1.6. Done this way to reduce code size, since we iterate either way
++ * @param {object} settings dataTables settings object
++ * @memberof DataTable#oApi
++ */
++ function _fnReduce ( that, fn, init, start, end, inc )
++ {
++ var
++ i = start,
++ value,
++ isSet = false;
++
++ if ( init !== undefined ) {
++ value = init;
++ isSet = true;
++ }
++
++ while ( i !== end ) {
++ if ( ! that.hasOwnProperty(i) ) {
++ continue;
++ }
++
++ value = isSet ?
++ fn( value, that[i], i, that ) :
++ that[i];
++
++ isSet = true;
++ i += inc;
++ }
++
++ return value;
++ }
++
++ /**
++ * Add a column to the list used for the table with default values
++ * @param {object} oSettings dataTables settings object
++ * @param {node} nTh The th element for this column
++ * @memberof DataTable#oApi
++ */
++ function _fnAddColumn( oSettings, nTh )
++ {
++ // Add column to aoColumns array
++ var oDefaults = DataTable.defaults.column;
++ var iCol = oSettings.aoColumns.length;
++ var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, {
++ "nTh": nTh ? nTh : document.createElement('th'),
++ "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '',
++ "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol],
++ "mData": oDefaults.mData ? oDefaults.mData : iCol,
++ idx: iCol
++ } );
++ oSettings.aoColumns.push( oCol );
++
++ // Add search object for column specific search. Note that the `searchCols[ iCol ]`
++ // passed into extend can be undefined. This allows the user to give a default
++ // with only some of the parameters defined, and also not give a default
++ var searchCols = oSettings.aoPreSearchCols;
++ searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] );
++
++ // Use the default column options function to initialise classes etc
++ _fnColumnOptions( oSettings, iCol, $(nTh).data() );
++ }
++
++
++ /**
++ * Apply options for a column
++ * @param {object} oSettings dataTables settings object
++ * @param {int} iCol column index to consider
++ * @param {object} oOptions object with sType, bVisible and bSearchable etc
++ * @memberof DataTable#oApi
++ */
++ function _fnColumnOptions( oSettings, iCol, oOptions )
++ {
++ var oCol = oSettings.aoColumns[ iCol ];
++ var oClasses = oSettings.oClasses;
++ var th = $(oCol.nTh);
++
++ // Try to get width information from the DOM. We can't get it from CSS
++ // as we'd need to parse the CSS stylesheet. `width` option can override
++ if ( ! oCol.sWidthOrig ) {
++ // Width attribute
++ oCol.sWidthOrig = th.attr('width') || null;
++
++ // Style attribute
++ var t = (th.attr('style') || '').match(/width:\s*(\d+[pxem%]+)/);
++ if ( t ) {
++ oCol.sWidthOrig = t[1];
++ }
++ }
++
++ /* User specified column options */
++ if ( oOptions !== undefined && oOptions !== null )
++ {
++ // Backwards compatibility
++ _fnCompatCols( oOptions );
++
++ // Map camel case parameters to their Hungarian counterparts
++ _fnCamelToHungarian( DataTable.defaults.column, oOptions );
++
++ /* Backwards compatibility for mDataProp */
++ if ( oOptions.mDataProp !== undefined && !oOptions.mData )
++ {
++ oOptions.mData = oOptions.mDataProp;
++ }
++
++ if ( oOptions.sType )
++ {
++ oCol._sManualType = oOptions.sType;
++ }
++
++ // `class` is a reserved word in Javascript, so we need to provide
++ // the ability to use a valid name for the camel case input
++ if ( oOptions.className && ! oOptions.sClass )
++ {
++ oOptions.sClass = oOptions.className;
++ }
++
++ $.extend( oCol, oOptions );
++ _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" );
++
++ /* iDataSort to be applied (backwards compatibility), but aDataSort will take
++ * priority if defined
++ */
++ if ( oOptions.iDataSort !== undefined )
++ {
++ oCol.aDataSort = [ oOptions.iDataSort ];
++ }
++ _fnMap( oCol, oOptions, "aDataSort" );
++ }
++
++ /* Cache the data get and set functions for speed */
++ var mDataSrc = oCol.mData;
++ var mData = _fnGetObjectDataFn( mDataSrc );
++ var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null;
++
++ var attrTest = function( src ) {
++ return typeof src === 'string' && src.indexOf('@') !== -1;
++ };
++ oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && (
++ attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter)
++ );
++
++ oCol.fnGetData = function (rowData, type, meta) {
++ var innerData = mData( rowData, type, undefined, meta );
++
++ return mRender && type ?
++ mRender( innerData, type, rowData, meta ) :
++ innerData;
++ };
++ oCol.fnSetData = function ( rowData, val, meta ) {
++ return _fnSetObjectDataFn( mDataSrc )( rowData, val, meta );
++ };
++
++ // Indicate if DataTables should read DOM data as an object or array
++ // Used in _fnGetRowElements
++ if ( typeof mDataSrc !== 'number' ) {
++ oSettings._rowReadObject = true;
++ }
++
++ /* Feature sorting overrides column specific when off */
++ if ( !oSettings.oFeatures.bSort )
++ {
++ oCol.bSortable = false;
++ th.addClass( oClasses.sSortableNone ); // Have to add class here as order event isn't called
++ }
++
++ /* Check that the class assignment is correct for sorting */
++ var bAsc = $.inArray('asc', oCol.asSorting) !== -1;
++ var bDesc = $.inArray('desc', oCol.asSorting) !== -1;
++ if ( !oCol.bSortable || (!bAsc && !bDesc) )
++ {
++ oCol.sSortingClass = oClasses.sSortableNone;
++ oCol.sSortingClassJUI = "";
++ }
++ else if ( bAsc && !bDesc )
++ {
++ oCol.sSortingClass = oClasses.sSortableAsc;
++ oCol.sSortingClassJUI = oClasses.sSortJUIAscAllowed;
++ }
++ else if ( !bAsc && bDesc )
++ {
++ oCol.sSortingClass = oClasses.sSortableDesc;
++ oCol.sSortingClassJUI = oClasses.sSortJUIDescAllowed;
++ }
++ else
++ {
++ oCol.sSortingClass = oClasses.sSortable;
++ oCol.sSortingClassJUI = oClasses.sSortJUI;
++ }
++ }
++
++
++ /**
++ * Adjust the table column widths for new data. Note: you would probably want to
++ * do a redraw after calling this function!
++ * @param {object} settings dataTables settings object
++ * @memberof DataTable#oApi
++ */
++ function _fnAdjustColumnSizing ( settings )
++ {
++ /* Not interested in doing column width calculation if auto-width is disabled */
++ if ( settings.oFeatures.bAutoWidth !== false )
++ {
++ var columns = settings.aoColumns;
++
++ _fnCalculateColumnWidths( settings );
++ for ( var i=0 , iLen=columns.length ; i<iLen ; i++ )
++ {
++ columns[i].nTh.style.width = columns[i].sWidth;
++ }
++ }
++
++ var scroll = settings.oScroll;
++ if ( scroll.sY !== '' || scroll.sX !== '')
++ {
++ _fnScrollDraw( settings );
++ }
++
++ _fnCallbackFire( settings, null, 'column-sizing', [settings] );
++ }
++
++
++ /**
++ * Covert the index of a visible column to the index in the data array (take account
++ * of hidden columns)
++ * @param {object} oSettings dataTables settings object
++ * @param {int} iMatch Visible column index to lookup
++ * @returns {int} i the data index
++ * @memberof DataTable#oApi
++ */
++ function _fnVisibleToColumnIndex( oSettings, iMatch )
++ {
++ var aiVis = _fnGetColumns( oSettings, 'bVisible' );
++
++ return typeof aiVis[iMatch] === 'number' ?
++ aiVis[iMatch] :
++ null;
++ }
++
++
++ /**
++ * Covert the index of an index in the data array and convert it to the visible
++ * column index (take account of hidden columns)
++ * @param {int} iMatch Column index to lookup
++ * @param {object} oSettings dataTables settings object
++ * @returns {int} i the data index
++ * @memberof DataTable#oApi
++ */
++ function _fnColumnIndexToVisible( oSettings, iMatch )
++ {
++ var aiVis = _fnGetColumns( oSettings, 'bVisible' );
++ var iPos = $.inArray( iMatch, aiVis );
++
++ return iPos !== -1 ? iPos : null;
++ }
++
++
++ /**
++ * Get the number of visible columns
++ * @param {object} oSettings dataTables settings object
++ * @returns {int} i the number of visible columns
++ * @memberof DataTable#oApi
++ */
++ function _fnVisbleColumns( oSettings )
++ {
++ return _fnGetColumns( oSettings, 'bVisible' ).length;
++ }
++
++
++ /**
++ * Get an array of column indexes that match a given property
++ * @param {object} oSettings dataTables settings object
++ * @param {string} sParam Parameter in aoColumns to look for - typically
++ * bVisible or bSearchable
++ * @returns {array} Array of indexes with matched properties
++ * @memberof DataTable#oApi
++ */
++ function _fnGetColumns( oSettings, sParam )
++ {
++ var a = [];
++
++ $.map( oSettings.aoColumns, function(val, i) {
++ if ( val[sParam] ) {
++ a.push( i );
++ }
++ } );
++
++ return a;
++ }
++
++
++ /**
++ * Calculate the 'type' of a column
++ * @param {object} settings dataTables settings object
++ * @memberof DataTable#oApi
++ */
++ function _fnColumnTypes ( settings )
++ {
++ var columns = settings.aoColumns;
++ var data = settings.aoData;
++ var types = DataTable.ext.type.detect;
++ var i, ien, j, jen, k, ken;
++ var col, cell, detectedType, cache;
++
++ // For each column, spin over the
++ for ( i=0, ien=columns.length ; i<ien ; i++ ) {
++ col = columns[i];
++ cache = [];
++
++ if ( ! col.sType && col._sManualType ) {
++ col.sType = col._sManualType;
++ }
++ else if ( ! col.sType ) {
++ for ( j=0, jen=types.length ; j<jen ; j++ ) {
++ for ( k=0, ken=data.length ; k<ken ; k++ ) {
++ // Use a cache array so we only need to get the type data
++ // from the formatter once (when using multiple detectors)
++ if ( cache[k] === undefined ) {
++ cache[k] = _fnGetCellData( settings, k, i, 'type' );
++ }
++
++ detectedType = types[j]( cache[k], settings );
++
++ // If null, then this type can't apply to this column, so
++ // rather than testing all cells, break out. There is an
++ // exception for the last type which is `html`. We need to
++ // scan all rows since it is possible to mix string and HTML
++ // types
++ if ( ! detectedType && j !== types.length-1 ) {
++ break;
++ }
++
++ // Only a single match is needed for html type since it is
++ // bottom of the pile and very similar to string
++ if ( detectedType === 'html' ) {
++ break;
++ }
++ }
++
++ // Type is valid for all data points in the column - use this
++ // type
++ if ( detectedType ) {
++ col.sType = detectedType;
++ break;
++ }
++ }
++
++ // Fall back - if no type was detected, always use string
++ if ( ! col.sType ) {
++ col.sType = 'string';
++ }
++ }
++ }
++ }
++
++
++ /**
++ * Take the column definitions and static columns arrays and calculate how
++ * they relate to column indexes. The callback function will then apply the
++ * definition found for a column to a suitable configuration object.
++ * @param {object} oSettings dataTables settings object
++ * @param {array} aoColDefs The aoColumnDefs array that is to be applied
++ * @param {array} aoCols The aoColumns array that defines columns individually
++ * @param {function} fn Callback function - takes two parameters, the calculated
++ * column index and the definition for that column.
++ * @memberof DataTable#oApi
++ */
++ function _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, fn )
++ {
++ var i, iLen, j, jLen, k, kLen, def;
++ var columns = oSettings.aoColumns;
++
++ // Column definitions with aTargets
++ if ( aoColDefs )
++ {
++ /* Loop over the definitions array - loop in reverse so first instance has priority */
++ for ( i=aoColDefs.length-1 ; i>=0 ; i-- )
++ {
++ def = aoColDefs[i];
++
++ /* Each definition can target multiple columns, as it is an array */
++ var aTargets = def.targets !== undefined ?
++ def.targets :
++ def.aTargets;
++
++ if ( ! $.isArray( aTargets ) )
++ {
++ aTargets = [ aTargets ];
++ }
++
++ for ( j=0, jLen=aTargets.length ; j<jLen ; j++ )
++ {
++ if ( typeof aTargets[j] === 'number' && aTargets[j] >= 0 )
++ {
++ /* Add columns that we don't yet know about */
++ while( columns.length <= aTargets[j] )
++ {
++ _fnAddColumn( oSettings );
++ }
++
++ /* Integer, basic index */
++ fn( aTargets[j], def );
++ }
++ else if ( typeof aTargets[j] === 'number' && aTargets[j] < 0 )
++ {
++ /* Negative integer, right to left column counting */
++ fn( columns.length+aTargets[j], def );
++ }
++ else if ( typeof aTargets[j] === 'string' )
++ {
++ /* Class name matching on TH element */
++ for ( k=0, kLen=columns.length ; k<kLen ; k++ )
++ {
++ if ( aTargets[j] == "_all" ||
++ $(columns[k].nTh).hasClass( aTargets[j] ) )
++ {
++ fn( k, def );
++ }
++ }
++ }
++ }
++ }
++ }
++
++ // Statically defined columns array
++ if ( aoCols )
++ {
++ for ( i=0, iLen=aoCols.length ; i<iLen ; i++ )
++ {
++ fn( i, aoCols[i] );
++ }
++ }
++ }
++
++ /**
++ * Add a data array to the table, creating DOM node etc. This is the parallel to
++ * _fnGatherData, but for adding rows from a Javascript source, rather than a
++ * DOM source.
++ * @param {object} oSettings dataTables settings object
++ * @param {array} aData data array to be added
++ * @param {node} [nTr] TR element to add to the table - optional. If not given,
++ * DataTables will create a row automatically
++ * @param {array} [anTds] Array of TD|TH elements for the row - must be given
++ * if nTr is.
++ * @returns {int} >=0 if successful (index of new aoData entry), -1 if failed
++ * @memberof DataTable#oApi
++ */
++ function _fnAddData ( oSettings, aDataIn, nTr, anTds )
++ {
++ /* Create the object for storing information about this new row */
++ var iRow = oSettings.aoData.length;
++ var oData = $.extend( true, {}, DataTable.models.oRow, {
++ src: nTr ? 'dom' : 'data'
++ } );
++
++ oData._aData = aDataIn;
++ oSettings.aoData.push( oData );
++
++ /* Create the cells */
++ var nTd, sThisType;
++ var columns = oSettings.aoColumns;
++ for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
++ {
++ // When working with a row, the data source object must be populated. In
++ // all other cases, the data source object is already populated, so we
++ // don't overwrite it, which might break bindings etc
++ if ( nTr ) {
++ _fnSetCellData( oSettings, iRow, i, _fnGetCellData( oSettings, iRow, i ) );
++ }
++ columns[i].sType = null;
++ }
++
++ /* Add to the display array */
++ oSettings.aiDisplayMaster.push( iRow );
++
++ /* Create the DOM information, or register it if already present */
++ if ( nTr || ! oSettings.oFeatures.bDeferRender )
++ {
++ _fnCreateTr( oSettings, iRow, nTr, anTds );
++ }
++
++ return iRow;
++ }
++
++
++ /**
++ * Add one or more TR elements to the table. Generally we'd expect to
++ * use this for reading data from a DOM sourced table, but it could be
++ * used for an TR element. Note that if a TR is given, it is used (i.e.
++ * it is not cloned).
++ * @param {object} settings dataTables settings object
++ * @param {array|node|jQuery} trs The TR element(s) to add to the table
++ * @returns {array} Array of indexes for the added rows
++ * @memberof DataTable#oApi
++ */
++ function _fnAddTr( settings, trs )
++ {
++ var row;
++
++ // Allow an individual node to be passed in
++ if ( ! (trs instanceof $) ) {
++ trs = $(trs);
++ }
++
++ return trs.map( function (i, el) {
++ row = _fnGetRowElements( settings, el );
++ return _fnAddData( settings, row.data, el, row.cells );
++ } );
++ }
++
++
++ /**
++ * Take a TR element and convert it to an index in aoData
++ * @param {object} oSettings dataTables settings object
++ * @param {node} n the TR element to find
++ * @returns {int} index if the node is found, null if not
++ * @memberof DataTable#oApi
++ */
++ function _fnNodeToDataIndex( oSettings, n )
++ {
++ return (n._DT_RowIndex!==undefined) ? n._DT_RowIndex : null;
++ }
++
++
++ /**
++ * Take a TD element and convert it into a column data index (not the visible index)
++ * @param {object} oSettings dataTables settings object
++ * @param {int} iRow The row number the TD/TH can be found in
++ * @param {node} n The TD/TH element to find
++ * @returns {int} index if the node is found, -1 if not
++ * @memberof DataTable#oApi
++ */
++ function _fnNodeToColumnIndex( oSettings, iRow, n )
++ {
++ return $.inArray( n, oSettings.aoData[ iRow ].anCells );
++ }
++
++
++ /**
++ * Get the data for a given cell from the internal cache, taking into account data mapping
++ * @param {object} settings dataTables settings object
++ * @param {int} rowIdx aoData row id
++ * @param {int} colIdx Column index
++ * @param {string} type data get type ('display', 'type' 'filter' 'sort')
++ * @returns {*} Cell data
++ * @memberof DataTable#oApi
++ */
++ function _fnGetCellData( settings, rowIdx, colIdx, type )
++ {
++ var draw = settings.iDraw;
++ var col = settings.aoColumns[colIdx];
++ var rowData = settings.aoData[rowIdx]._aData;
++ var defaultContent = col.sDefaultContent;
++ var cellData = col.fnGetData( rowData, type, {
++ settings: settings,
++ row: rowIdx,
++ col: colIdx
++ } );
++
++ if ( cellData === undefined ) {
++ if ( settings.iDrawError != draw && defaultContent === null ) {
++ _fnLog( settings, 0, "Requested unknown parameter "+
++ (typeof col.mData=='function' ? '{function}' : "'"+col.mData+"'")+
++ " for row "+rowIdx, 4 );
++ settings.iDrawError = draw;
++ }
++ return defaultContent;
++ }
++
++ /* When the data source is null, we can use default column data */
++ if ( (cellData === rowData || cellData === null) && defaultContent !== null ) {
++ cellData = defaultContent;
++ }
++ else if ( typeof cellData === 'function' ) {
++ // If the data source is a function, then we run it and use the return,
++ // executing in the scope of the data object (for instances)
++ return cellData.call( rowData );
++ }
++
++ if ( cellData === null && type == 'display' ) {
++ return '';
++ }
++ return cellData;
++ }
++
++
++ /**
++ * Set the value for a specific cell, into the internal data cache
++ * @param {object} settings dataTables settings object
++ * @param {int} rowIdx aoData row id
++ * @param {int} colIdx Column index
++ * @param {*} val Value to set
++ * @memberof DataTable#oApi
++ */
++ function _fnSetCellData( settings, rowIdx, colIdx, val )
++ {
++ var col = settings.aoColumns[colIdx];
++ var rowData = settings.aoData[rowIdx]._aData;
++
++ col.fnSetData( rowData, val, {
++ settings: settings,
++ row: rowIdx,
++ col: colIdx
++ } );
++ }
++
++
++ // Private variable that is used to match action syntax in the data property object
++ var __reArray = /\[.*?\]$/;
++ var __reFn = /\(\)$/;
++
++ /**
++ * Split string on periods, taking into account escaped periods
++ * @param {string} str String to split
++ * @return {array} Split string
++ */
++ function _fnSplitObjNotation( str )
++ {
++ return $.map( str.match(/(\\.|[^\.])+/g), function ( s ) {
++ return s.replace(/\\./g, '.');
++ } );
++ }
++
++
++ /**
++ * Return a function that can be used to get data from a source object, taking
++ * into account the ability to use nested objects as a source
++ * @param {string|int|function} mSource The data source for the object
++ * @returns {function} Data get function
++ * @memberof DataTable#oApi
++ */
++ function _fnGetObjectDataFn( mSource )
++ {
++ if ( $.isPlainObject( mSource ) )
++ {
++ /* Build an object of get functions, and wrap them in a single call */
++ var o = {};
++ $.each( mSource, function (key, val) {
++ if ( val ) {
++ o[key] = _fnGetObjectDataFn( val );
++ }
++ } );
++
++ return function (data, type, row, meta) {
++ var t = o[type] || o._;
++ return t !== undefined ?
++ t(data, type, row, meta) :
++ data;
++ };
++ }
++ else if ( mSource === null )
++ {
++ /* Give an empty string for rendering / sorting etc */
++ return function (data) { // type, row and meta also passed, but not used
++ return data;
++ };
++ }
++ else if ( typeof mSource === 'function' )
++ {
++ return function (data, type, row, meta) {
++ return mSource( data, type, row, meta );
++ };
++ }
++ else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 ||
++ mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1) )
++ {
++ /* If there is a . in the source string then the data source is in a
++ * nested object so we loop over the data for each level to get the next
++ * level down. On each loop we test for undefined, and if found immediately
++ * return. This allows entire objects to be missing and sDefaultContent to
++ * be used if defined, rather than throwing an error
++ */
++ var fetchData = function (data, type, src) {
++ var arrayNotation, funcNotation, out, innerSrc;
++
++ if ( src !== "" )
++ {
++ var a = _fnSplitObjNotation( src );
++
++ for ( var i=0, iLen=a.length ; i<iLen ; i++ )
++ {
++ // Check if we are dealing with special notation
++ arrayNotation = a[i].match(__reArray);
++ funcNotation = a[i].match(__reFn);
++
++ if ( arrayNotation )
++ {
++ // Array notation
++ a[i] = a[i].replace(__reArray, '');
++
++ // Condition allows simply [] to be passed in
++ if ( a[i] !== "" ) {
++ data = data[ a[i] ];
++ }
++ out = [];
++
++ // Get the remainder of the nested object to get
++ a.splice( 0, i+1 );
++ innerSrc = a.join('.');
++
++ // Traverse each entry in the array getting the properties requested
++ for ( var j=0, jLen=data.length ; j<jLen ; j++ ) {
++ out.push( fetchData( data[j], type, innerSrc ) );
++ }
++
++ // If a string is given in between the array notation indicators, that
++ // is used to join the strings together, otherwise an array is returned
++ var join = arrayNotation[0].substring(1, arrayNotation[0].length-1);
++ data = (join==="") ? out : out.join(join);
++
++ // The inner call to fetchData has already traversed through the remainder
++ // of the source requested, so we exit from the loop
++ break;
++ }
++ else if ( funcNotation )
++ {
++ // Function call
++ a[i] = a[i].replace(__reFn, '');
++ data = data[ a[i] ]();
++ continue;
++ }
++
++ if ( data === null || data[ a[i] ] === undefined )
++ {
++ return undefined;
++ }
++ data = data[ a[i] ];
++ }
++ }
++
++ return data;
++ };
++
++ return function (data, type) { // row and meta also passed, but not used
++ return fetchData( data, type, mSource );
++ };
++ }
++ else
++ {
++ /* Array or flat object mapping */
++ return function (data, type) { // row and meta also passed, but not used
++ return data[mSource];
++ };
++ }
++ }
++
++
++ /**
++ * Return a function that can be used to set data from a source object, taking
++ * into account the ability to use nested objects as a source
++ * @param {string|int|function} mSource The data source for the object
++ * @returns {function} Data set function
++ * @memberof DataTable#oApi
++ */
++ function _fnSetObjectDataFn( mSource )
++ {
++ if ( $.isPlainObject( mSource ) )
++ {
++ /* Unlike get, only the underscore (global) option is used for for
++ * setting data since we don't know the type here. This is why an object
++ * option is not documented for `mData` (which is read/write), but it is
++ * for `mRender` which is read only.
++ */
++ return _fnSetObjectDataFn( mSource._ );
++ }
++ else if ( mSource === null )
++ {
++ /* Nothing to do when the data source is null */
++ return function () {};
++ }
++ else if ( typeof mSource === 'function' )
++ {
++ return function (data, val, meta) {
++ mSource( data, 'set', val, meta );
++ };
++ }
++ else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 ||
++ mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1) )
++ {
++ /* Like the get, we need to get data from a nested object */
++ var setData = function (data, val, src) {
++ var a = _fnSplitObjNotation( src ), b;
++ var aLast = a[a.length-1];
++ var arrayNotation, funcNotation, o, innerSrc;
++
++ for ( var i=0, iLen=a.length-1 ; i<iLen ; i++ )
++ {
++ // Check if we are dealing with an array notation request
++ arrayNotation = a[i].match(__reArray);
++ funcNotation = a[i].match(__reFn);
++
++ if ( arrayNotation )
++ {
++ a[i] = a[i].replace(__reArray, '');
++ data[ a[i] ] = [];
++
++ // Get the remainder of the nested object to set so we can recurse
++ b = a.slice();
++ b.splice( 0, i+1 );
++ innerSrc = b.join('.');
++
++ // Traverse each entry in the array setting the properties requested
++ for ( var j=0, jLen=val.length ; j<jLen ; j++ )
++ {
++ o = {};
++ setData( o, val[j], innerSrc );
++ data[ a[i] ].push( o );
++ }
++
++ // The inner call to setData has already traversed through the remainder
++ // of the source and has set the data, thus we can exit here
++ return;
++ }
++ else if ( funcNotation )
++ {
++ // Function call
++ a[i] = a[i].replace(__reFn, '');
++ data = data[ a[i] ]( val );
++ }
++
++ // If the nested object doesn't currently exist - since we are
++ // trying to set the value - create it
++ if ( data[ a[i] ] === null || data[ a[i] ] === undefined )
++ {
++ data[ a[i] ] = {};
++ }
++ data = data[ a[i] ];
++ }
++
++ // Last item in the input - i.e, the actual set
++ if ( aLast.match(__reFn ) )
++ {
++ // Function call
++ data = data[ aLast.replace(__reFn, '') ]( val );
++ }
++ else
++ {
++ // If array notation is used, we just want to strip it and use the property name
++ // and assign the value. If it isn't used, then we get the result we want anyway
++ data[ aLast.replace(__reArray, '') ] = val;
++ }
++ };
++
++ return function (data, val) { // meta is also passed in, but not used
++ return setData( data, val, mSource );
++ };
++ }
++ else
++ {
++ /* Array or flat object mapping */
++ return function (data, val) { // meta is also passed in, but not used
++ data[mSource] = val;
++ };
++ }
++ }
++
++
++ /**
++ * Return an array with the full table data
++ * @param {object} oSettings dataTables settings object
++ * @returns array {array} aData Master data array
++ * @memberof DataTable#oApi
++ */
++ function _fnGetDataMaster ( settings )
++ {
++ return _pluck( settings.aoData, '_aData' );
++ }
++
++
++ /**
++ * Nuke the table
++ * @param {object} oSettings dataTables settings object
++ * @memberof DataTable#oApi
++ */
++ function _fnClearTable( settings )
++ {
++ settings.aoData.length = 0;
++ settings.aiDisplayMaster.length = 0;
++ settings.aiDisplay.length = 0;
++ }
++
++
++ /**
++ * Take an array of integers (index array) and remove a target integer (value - not
++ * the key!)
++ * @param {array} a Index array to target
++ * @param {int} iTarget value to find
++ * @memberof DataTable#oApi
++ */
++ function _fnDeleteIndex( a, iTarget, splice )
++ {
++ var iTargetIndex = -1;
++
++ for ( var i=0, iLen=a.length ; i<iLen ; i++ )
++ {
++ if ( a[i] == iTarget )
++ {
++ iTargetIndex = i;
++ }
++ else if ( a[i] > iTarget )
++ {
++ a[i]--;
++ }
++ }
++
++ if ( iTargetIndex != -1 && splice === undefined )
++ {
++ a.splice( iTargetIndex, 1 );
++ }
++ }
++
++
++ /**
++ * Mark cached data as invalid such that a re-read of the data will occur when
++ * the cached data is next requested. Also update from the data source object.
++ *
++ * @param {object} settings DataTables settings object
++ * @param {int} rowIdx Row index to invalidate
++ * @param {string} [src] Source to invalidate from: undefined, 'auto', 'dom'
++ * or 'data'
++ * @param {int} [colIdx] Column index to invalidate. If undefined the whole
++ * row will be invalidated
++ * @memberof DataTable#oApi
++ *
++ * @todo For the modularisation of v1.11 this will need to become a callback, so
++ * the sort and filter methods can subscribe to it. That will required
++ * initialisation options for sorting, which is why it is not already baked in
++ */
++ function _fnInvalidate( settings, rowIdx, src, colIdx )
++ {
++ var row = settings.aoData[ rowIdx ];
++ var i, ien;
++ var cellWrite = function ( cell, col ) {
++ // This is very frustrating, but in IE if you just write directly
++ // to innerHTML, and elements that are overwritten are GC'ed,
++ // even if there is a reference to them elsewhere
++ while ( cell.childNodes.length ) {
++ cell.removeChild( cell.firstChild );
++ }
++
++ cell.innerHTML = _fnGetCellData( settings, rowIdx, col, 'display' );
++ };
++
++ // Are we reading last data from DOM or the data object?
++ if ( src === 'dom' || ((! src || src === 'auto') && row.src === 'dom') ) {
++ // Read the data from the DOM
++ row._aData = _fnGetRowElements(
++ settings, row, colIdx, colIdx === undefined ? undefined : row._aData
++ )
++ .data;
++ }
++ else {
++ // Reading from data object, update the DOM
++ var cells = row.anCells;
++
++ if ( cells ) {
++ if ( colIdx !== undefined ) {
++ cellWrite( cells[colIdx], colIdx );
++ }
++ else {
++ for ( i=0, ien=cells.length ; i<ien ; i++ ) {
++ cellWrite( cells[i], i );
++ }
++ }
++ }
++ }
++
++ // For both row and cell invalidation, the cached data for sorting and
++ // filtering is nulled out
++ row._aSortData = null;
++ row._aFilterData = null;
++
++ // Invalidate the type for a specific column (if given) or all columns since
++ // the data might have changed
++ var cols = settings.aoColumns;
++ if ( colIdx !== undefined ) {
++ cols[ colIdx ].sType = null;
++ }
++ else {
++ for ( i=0, ien=cols.length ; i<ien ; i++ ) {
++ cols[i].sType = null;
++ }
++
++ // Update DataTables special `DT_*` attributes for the row
++ _fnRowAttributes( row );
++ }
++ }
++
++
++ /**
++ * Build a data source object from an HTML row, reading the contents of the
++ * cells that are in the row.
++ *
++ * @param {object} settings DataTables settings object
++ * @param {node|object} TR element from which to read data or existing row
++ * object from which to re-read the data from the cells
++ * @param {int} [colIdx] Optional column index
++ * @param {array|object} [d] Data source object. If `colIdx` is given then this
++ * parameter should also be given and will be used to write the data into.
++ * Only the column in question will be written
++ * @returns {object} Object with two parameters: `data` the data read, in
++ * document order, and `cells` and array of nodes (they can be useful to the
++ * caller, so rather than needing a second traversal to get them, just return
++ * them from here).
++ * @memberof DataTable#oApi
++ */
++ function _fnGetRowElements( settings, row, colIdx, d )
++ {
++ var
++ tds = [],
++ td = row.firstChild,
++ name, col, o, i=0, contents,
++ columns = settings.aoColumns,
++ objectRead = settings._rowReadObject;
++
++ // Allow the data object to be passed in, or construct
++ d = d || objectRead ? {} : [];
++
++ var attr = function ( str, td ) {
++ if ( typeof str === 'string' ) {
++ var idx = str.indexOf('@');
++
++ if ( idx !== -1 ) {
++ var attr = str.substring( idx+1 );
++ var setter = _fnSetObjectDataFn( str );
++ setter( d, td.getAttribute( attr ) );
++ }
++ }
++ };
++
++ // Read data from a cell and store into the data object
++ var cellProcess = function ( cell ) {
++ if ( colIdx === undefined || colIdx === i ) {
++ col = columns[i];
++ contents = $.trim(cell.innerHTML);
++
++ if ( col && col._bAttrSrc ) {
++ var setter = _fnSetObjectDataFn( col.mData._ );
++ setter( d, contents );
++
++ attr( col.mData.sort, cell );
++ attr( col.mData.type, cell );
++ attr( col.mData.filter, cell );
++ }
++ else {
++ // Depending on the `data` option for the columns the data can
++ // be read to either an object or an array.
++ if ( objectRead ) {
++ if ( ! col._setter ) {
++ // Cache the setter function
++ col._setter = _fnSetObjectDataFn( col.mData );
++ }
++ col._setter( d, contents );
++ }
++ else {
++ d[i] = contents;
++ }
++ }
++ }
++
++ i++;
++ };
++
++ if ( td ) {
++ // `tr` element was passed in
++ while ( td ) {
++ name = td.nodeName.toUpperCase();
++
++ if ( name == "TD" || name == "TH" ) {
++ cellProcess( td );
++ tds.push( td );
++ }
++
++ td = td.nextSibling;
++ }
++ }
++ else {
++ // Existing row object passed in
++ tds = row.anCells;
++
++ for ( var j=0, jen=tds.length ; j<jen ; j++ ) {
++ cellProcess( tds[j] );
++ }
++ }
++
++ return {
++ data: d,
++ cells: tds
++ };
++ }
++ /**
++ * Create a new TR element (and it's TD children) for a row
++ * @param {object} oSettings dataTables settings object
++ * @param {int} iRow Row to consider
++ * @param {node} [nTrIn] TR element to add to the table - optional. If not given,
++ * DataTables will create a row automatically
++ * @param {array} [anTds] Array of TD|TH elements for the row - must be given
++ * if nTr is.
++ * @memberof DataTable#oApi
++ */
++ function _fnCreateTr ( oSettings, iRow, nTrIn, anTds )
++ {
++ var
++ row = oSettings.aoData[iRow],
++ rowData = row._aData,
++ cells = [],
++ nTr, nTd, oCol,
++ i, iLen;
++
++ if ( row.nTr === null )
++ {
++ nTr = nTrIn || document.createElement('tr');
++
++ row.nTr = nTr;
++ row.anCells = cells;
++
++ /* Use a private property on the node to allow reserve mapping from the node
++ * to the aoData array for fast look up
++ */
++ nTr._DT_RowIndex = iRow;
++
++ /* Special parameters can be given by the data source to be used on the row */
++ _fnRowAttributes( row );
++
++ /* Process each column */
++ for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
++ {
++ oCol = oSettings.aoColumns[i];
++
++ nTd = nTrIn ? anTds[i] : document.createElement( oCol.sCellType );
++ cells.push( nTd );
++
++ // Need to create the HTML if new, or if a rendering function is defined
++ if ( !nTrIn || oCol.mRender || oCol.mData !== i )
++ {
++ nTd.innerHTML = _fnGetCellData( oSettings, iRow, i, 'display' );
++ }
++
++ /* Add user defined class */
++ if ( oCol.sClass )
++ {
++ nTd.className += ' '+oCol.sClass;
++ }
++
++ // Visibility - add or remove as required
++ if ( oCol.bVisible && ! nTrIn )
++ {
++ nTr.appendChild( nTd );
++ }
++ else if ( ! oCol.bVisible && nTrIn )
++ {
++ nTd.parentNode.removeChild( nTd );
++ }
++
++ if ( oCol.fnCreatedCell )
++ {
++ oCol.fnCreatedCell.call( oSettings.oInstance,
++ nTd, _fnGetCellData( oSettings, iRow, i ), rowData, iRow, i
++ );
++ }
++ }
++
++ _fnCallbackFire( oSettings, 'aoRowCreatedCallback', null, [nTr, rowData, iRow] );
++ }
++
++ // Remove once webkit bug 131819 and Chromium bug 365619 have been resolved
++ // and deployed
++ row.nTr.setAttribute( 'role', 'row' );
++ }
++
++
++ /**
++ * Add attributes to a row based on the special `DT_*` parameters in a data
++ * source object.
++ * @param {object} DataTables row object for the row to be modified
++ * @memberof DataTable#oApi
++ */
++ function _fnRowAttributes( row )
++ {
++ var tr = row.nTr;
++ var data = row._aData;
++
++ if ( tr ) {
++ if ( data.DT_RowId ) {
++ tr.id = data.DT_RowId;
++ }
++
++ if ( data.DT_RowClass ) {
++ // Remove any classes added by DT_RowClass before
++ var a = data.DT_RowClass.split(' ');
++ row.__rowc = row.__rowc ?
++ _unique( row.__rowc.concat( a ) ) :
++ a;
++
++ $(tr)
++ .removeClass( row.__rowc.join(' ') )
++ .addClass( data.DT_RowClass );
++ }
++
++ if ( data.DT_RowAttr ) {
++ $(tr).attr( data.DT_RowAttr );
++ }
++
++ if ( data.DT_RowData ) {
++ $(tr).data( data.DT_RowData );
++ }
++ }
++ }
++
++
++ /**
++ * Create the HTML header for the table
++ * @param {object} oSettings dataTables settings object
++ * @memberof DataTable#oApi
++ */
++ function _fnBuildHead( oSettings )
++ {
++ var i, ien, cell, row, column;
++ var thead = oSettings.nTHead;
++ var tfoot = oSettings.nTFoot;
++ var createHeader = $('th, td', thead).length === 0;
++ var classes = oSettings.oClasses;
++ var columns = oSettings.aoColumns;
++
++ if ( createHeader ) {
++ row = $('<tr/>').appendTo( thead );
++ }
++
++ for ( i=0, ien=columns.length ; i<ien ; i++ ) {
++ column = columns[i];
++ cell = $( column.nTh ).addClass( column.sClass );
++
++ if ( createHeader ) {
++ cell.appendTo( row );
++ }
++
++ // 1.11 move into sorting
++ if ( oSettings.oFeatures.bSort ) {
++ cell.addClass( column.sSortingClass );
++
++ if ( column.bSortable !== false ) {
++ cell
++ .attr( 'tabindex', oSettings.iTabIndex )
++ .attr( 'aria-controls', oSettings.sTableId );
++
++ _fnSortAttachListener( oSettings, column.nTh, i );
++ }
++ }
++
++ if ( column.sTitle != cell.html() ) {
++ cell.html( column.sTitle );
++ }
++
++ _fnRenderer( oSettings, 'header' )(
++ oSettings, cell, column, classes
++ );
++ }
++
++ if ( createHeader ) {
++ _fnDetectHeader( oSettings.aoHeader, thead );
++ }
++
++ /* ARIA role for the rows */
++ $(thead).find('>tr').attr('role', 'row');
++
++ /* Deal with the footer - add classes if required */
++ $(thead).find('>tr>th, >tr>td').addClass( classes.sHeaderTH );
++ $(tfoot).find('>tr>th, >tr>td').addClass( classes.sFooterTH );
++
++ // Cache the footer cells. Note that we only take the cells from the first
++ // row in the footer. If there is more than one row the user wants to
++ // interact with, they need to use the table().foot() method. Note also this
++ // allows cells to be used for multiple columns using colspan
++ if ( tfoot !== null ) {
++ var cells = oSettings.aoFooter[0];
++
++ for ( i=0, ien=cells.length ; i<ien ; i++ ) {
++ column = columns[i];
++ column.nTf = cells[i].cell;
++
++ if ( column.sClass ) {
++ $(column.nTf).addClass( column.sClass );
++ }
++ }
++ }
++ }
++
++
++ /**
++ * Draw the header (or footer) element based on the column visibility states. The
++ * methodology here is to use the layout array from _fnDetectHeader, modified for
++ * the instantaneous column visibility, to construct the new layout. The grid is
++ * traversed over cell at a time in a rows x columns grid fashion, although each
++ * cell insert can cover multiple elements in the grid - which is tracks using the
++ * aApplied array. Cell inserts in the grid will only occur where there isn't
++ * already a cell in that position.
++ * @param {object} oSettings dataTables settings object
++ * @param array {objects} aoSource Layout array from _fnDetectHeader
++ * @param {boolean} [bIncludeHidden=false] If true then include the hidden columns in the calc,
++ * @memberof DataTable#oApi
++ */
++ function _fnDrawHead( oSettings, aoSource, bIncludeHidden )
++ {
++ var i, iLen, j, jLen, k, kLen, n, nLocalTr;
++ var aoLocal = [];
++ var aApplied = [];
++ var iColumns = oSettings.aoColumns.length;
++ var iRowspan, iColspan;
++
++ if ( ! aoSource )
++ {
++ return;
++ }
++
++ if ( bIncludeHidden === undefined )
++ {
++ bIncludeHidden = false;
++ }
++
++ /* Make a copy of the master layout array, but without the visible columns in it */
++ for ( i=0, iLen=aoSource.length ; i<iLen ; i++ )
++ {
++ aoLocal[i] = aoSource[i].slice();
++ aoLocal[i].nTr = aoSource[i].nTr;
++
++ /* Remove any columns which are currently hidden */
++ for ( j=iColumns-1 ; j>=0 ; j-- )
++ {
++ if ( !oSettings.aoColumns[j].bVisible && !bIncludeHidden )
++ {
++ aoLocal[i].splice( j, 1 );
++ }
++ }
++
++ /* Prep the applied array - it needs an element for each row */
++ aApplied.push( [] );
++ }
++
++ for ( i=0, iLen=aoLocal.length ; i<iLen ; i++ )
++ {
++ nLocalTr = aoLocal[i].nTr;
++
++ /* All cells are going to be replaced, so empty out the row */
++ if ( nLocalTr )
++ {
++ while( (n = nLocalTr.firstChild) )
++ {
++ nLocalTr.removeChild( n );
++ }
++ }
++
++ for ( j=0, jLen=aoLocal[i].length ; j<jLen ; j++ )
++ {
++ iRowspan = 1;
++ iColspan = 1;
++
++ /* Check to see if there is already a cell (row/colspan) covering our target
++ * insert point. If there is, then there is nothing to do.
++ */
++ if ( aApplied[i][j] === undefined )
++ {
++ nLocalTr.appendChild( aoLocal[i][j].cell );
++ aApplied[i][j] = 1;
++
++ /* Expand the cell to cover as many rows as needed */
++ while ( aoLocal[i+iRowspan] !== undefined &&
++ aoLocal[i][j].cell == aoLocal[i+iRowspan][j].cell )
++ {
++ aApplied[i+iRowspan][j] = 1;
++ iRowspan++;
++ }
++
++ /* Expand the cell to cover as many columns as needed */
++ while ( aoLocal[i][j+iColspan] !== undefined &&
++ aoLocal[i][j].cell == aoLocal[i][j+iColspan].cell )
++ {
++ /* Must update the applied array over the rows for the columns */
++ for ( k=0 ; k<iRowspan ; k++ )
++ {
++ aApplied[i+k][j+iColspan] = 1;
++ }
++ iColspan++;
++ }
++
++ /* Do the actual expansion in the DOM */
++ $(aoLocal[i][j].cell)
++ .attr('rowspan', iRowspan)
++ .attr('colspan', iColspan);
++ }
++ }
++ }
++ }
++
++
++ /**
++ * Insert the required TR nodes into the table for display
++ * @param {object} oSettings dataTables settings object
++ * @memberof DataTable#oApi
++ */
++ function _fnDraw( oSettings )
++ {
++ /* Provide a pre-callback function which can be used to cancel the draw is false is returned */
++ var aPreDraw = _fnCallbackFire( oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings] );
++ if ( $.inArray( false, aPreDraw ) !== -1 )
++ {
++ _fnProcessingDisplay( oSettings, false );
++ return;
++ }
++
++ var i, iLen, n;
++ var anRows = [];
++ var iRowCount = 0;
++ var asStripeClasses = oSettings.asStripeClasses;
++ var iStripes = asStripeClasses.length;
++ var iOpenRows = oSettings.aoOpenRows.length;
++ var oLang = oSettings.oLanguage;
++ var iInitDisplayStart = oSettings.iInitDisplayStart;
++ var bServerSide = _fnDataSource( oSettings ) == 'ssp';
++ var aiDisplay = oSettings.aiDisplay;
++
++ oSettings.bDrawing = true;
++
++ /* Check and see if we have an initial draw position from state saving */
++ if ( iInitDisplayStart !== undefined && iInitDisplayStart !== -1 )
++ {
++ oSettings._iDisplayStart = bServerSide ?
++ iInitDisplayStart :
++ iInitDisplayStart >= oSettings.fnRecordsDisplay() ?
++ 0 :
++ iInitDisplayStart;
++
++ oSettings.iInitDisplayStart = -1;
++ }
++
++ var iDisplayStart = oSettings._iDisplayStart;
++ var iDisplayEnd = oSettings.fnDisplayEnd();
++
++ /* Server-side processing draw intercept */
++ if ( oSettings.bDeferLoading )
++ {
++ oSettings.bDeferLoading = false;
++ oSettings.iDraw++;
++ _fnProcessingDisplay( oSettings, false );
++ }
++ else if ( !bServerSide )
++ {
++ oSettings.iDraw++;
++ }
++ else if ( !oSettings.bDestroying && !_fnAjaxUpdate( oSettings ) )
++ {
++ return;
++ }
++
++ if ( aiDisplay.length !== 0 )
++ {
++ var iStart = bServerSide ? 0 : iDisplayStart;
++ var iEnd = bServerSide ? oSettings.aoData.length : iDisplayEnd;
++
++ for ( var j=iStart ; j<iEnd ; j++ )
++ {
++ var iDataIndex = aiDisplay[j];
++ var aoData = oSettings.aoData[ iDataIndex ];
++ if ( aoData.nTr === null )
++ {
++ _fnCreateTr( oSettings, iDataIndex );
++ }
++
++ var nRow = aoData.nTr;
++
++ /* Remove the old striping classes and then add the new one */
++ if ( iStripes !== 0 )
++ {
++ var sStripe = asStripeClasses[ iRowCount % iStripes ];
++ if ( aoData._sRowStripe != sStripe )
++ {
++ $(nRow).removeClass( aoData._sRowStripe ).addClass( sStripe );
++ aoData._sRowStripe = sStripe;
++ }
++ }
++
++ // Row callback functions - might want to manipulate the row
++ // iRowCount and j are not currently documented. Are they at all
++ // useful?
++ _fnCallbackFire( oSettings, 'aoRowCallback', null,
++ [nRow, aoData._aData, iRowCount, j] );
++
++ anRows.push( nRow );
++ iRowCount++;
++ }
++ }
++ else
++ {
++ /* Table is empty - create a row with an empty message in it */
++ var sZero = oLang.sZeroRecords;
++ if ( oSettings.iDraw == 1 && _fnDataSource( oSettings ) == 'ajax' )
++ {
++ sZero = oLang.sLoadingRecords;
++ }
++ else if ( oLang.sEmptyTable && oSettings.fnRecordsTotal() === 0 )
++ {
++ sZero = oLang.sEmptyTable;
++ }
++
++ anRows[ 0 ] = $( '<tr/>', { 'class': iStripes ? asStripeClasses[0] : '' } )
++ .append( $('<td />', {
++ 'valign': 'top',
++ 'colSpan': _fnVisbleColumns( oSettings ),
++ 'class': oSettings.oClasses.sRowEmpty
++ } ).html( sZero ) )[0];
++ }
++
++ /* Header and footer callbacks */
++ _fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0],
++ _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
++
++ _fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0],
++ _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
++
++ var body = $(oSettings.nTBody);
++
++ body.children().detach();
++ body.append( $(anRows) );
++
++ /* Call all required callback functions for the end of a draw */
++ _fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings] );
++
++ /* Draw is complete, sorting and filtering must be as well */
++ oSettings.bSorted = false;
++ oSettings.bFiltered = false;
++ oSettings.bDrawing = false;
++ }
++
++
++ /**
++ * Redraw the table - taking account of the various features which are enabled
++ * @param {object} oSettings dataTables settings object
++ * @param {boolean} [holdPosition] Keep the current paging position. By default
++ * the paging is reset to the first page
++ * @memberof DataTable#oApi
++ */
++ function _fnReDraw( settings, holdPosition )
++ {
++ var
++ features = settings.oFeatures,
++ sort = features.bSort,
++ filter = features.bFilter;
++
++ if ( sort ) {
++ _fnSort( settings );
++ }
++
++ if ( filter ) {
++ _fnFilterComplete( settings, settings.oPreviousSearch );
++ }
++ else {
++ // No filtering, so we want to just use the display master
++ settings.aiDisplay = settings.aiDisplayMaster.slice();
++ }
++
++ if ( holdPosition !== true ) {
++ settings._iDisplayStart = 0;
++ }
++
++ // Let any modules know about the draw hold position state (used by
++ // scrolling internally)
++ settings._drawHold = holdPosition;
++
++ _fnDraw( settings );
++
++ settings._drawHold = false;
++ }
++
++
++ /**
++ * Add the options to the page HTML for the table
++ * @param {object} oSettings dataTables settings object
++ * @memberof DataTable#oApi
++ */
++ function _fnAddOptionsHtml ( oSettings )
++ {
++ var classes = oSettings.oClasses;
++ var table = $(oSettings.nTable);
++ var holding = $('<div/>').insertBefore( table ); // Holding element for speed
++ var features = oSettings.oFeatures;
++
++ // All DataTables are wrapped in a div
++ var insert = $('<div/>', {
++ id: oSettings.sTableId+'_wrapper',
++ 'class': classes.sWrapper + (oSettings.nTFoot ? '' : ' '+classes.sNoFooter)
++ } );
++
++ oSettings.nHolding = holding[0];
++ oSettings.nTableWrapper = insert[0];
++ oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling;
++
++ /* Loop over the user set positioning and place the elements as needed */
++ var aDom = oSettings.sDom.split('');
++ var featureNode, cOption, nNewNode, cNext, sAttr, j;
++ for ( var i=0 ; i<aDom.length ; i++ )
++ {
++ featureNode = null;
++ cOption = aDom[i];
++
++ if ( cOption == '<' )
++ {
++ /* New container div */
++ nNewNode = $('<div/>')[0];
++
++ /* Check to see if we should append an id and/or a class name to the container */
++ cNext = aDom[i+1];
++ if ( cNext == "'" || cNext == '"' )
++ {
++ sAttr = "";
++ j = 2;
++ while ( aDom[i+j] != cNext )
++ {
++ sAttr += aDom[i+j];
++ j++;
++ }
++
++ /* Replace jQuery UI constants @todo depreciated */
++ if ( sAttr == "H" )
++ {
++ sAttr = classes.sJUIHeader;
++ }
++ else if ( sAttr == "F" )
++ {
++ sAttr = classes.sJUIFooter;
++ }
++
++ /* The attribute can be in the format of "#id.class", "#id" or "class" This logic
++ * breaks the string into parts and applies them as needed
++ */
++ if ( sAttr.indexOf('.') != -1 )
++ {
++ var aSplit = sAttr.split('.');
++ nNewNode.id = aSplit[0].substr(1, aSplit[0].length-1);
++ nNewNode.className = aSplit[1];
++ }
++ else if ( sAttr.charAt(0) == "#" )
++ {
++ nNewNode.id = sAttr.substr(1, sAttr.length-1);
++ }
++ else
++ {
++ nNewNode.className = sAttr;
++ }
++
++ i += j; /* Move along the position array */
++ }
++
++ insert.append( nNewNode );
++ insert = $(nNewNode);
++ }
++ else if ( cOption == '>' )
++ {
++ /* End container div */
++ insert = insert.parent();
++ }
++ // @todo Move options into their own plugins?
++ else if ( cOption == 'l' && features.bPaginate && features.bLengthChange )
++ {
++ /* Length */
++ featureNode = _fnFeatureHtmlLength( oSettings );
++ }
++ else if ( cOption == 'f' && features.bFilter )
++ {
++ /* Filter */
++ featureNode = _fnFeatureHtmlFilter( oSettings );
++ }
++ else if ( cOption == 'r' && features.bProcessing )
++ {
++ /* pRocessing */
++ featureNode = _fnFeatureHtmlProcessing( oSettings );
++ }
++ else if ( cOption == 't' )
++ {
++ /* Table */
++ featureNode = _fnFeatureHtmlTable( oSettings );
++ }
++ else if ( cOption == 'i' && features.bInfo )
++ {
++ /* Info */
++ featureNode = _fnFeatureHtmlInfo( oSettings );
++ }
++ else if ( cOption == 'p' && features.bPaginate )
++ {
++ /* Pagination */
++ featureNode = _fnFeatureHtmlPaginate( oSettings );
++ }
++ else if ( DataTable.ext.feature.length !== 0 )
++ {
++ /* Plug-in features */
++ var aoFeatures = DataTable.ext.feature;
++ for ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ )
++ {
++ if ( cOption == aoFeatures[k].cFeature )
++ {
++ featureNode = aoFeatures[k].fnInit( oSettings );
++ break;
++ }
++ }
++ }
++
++ /* Add to the 2D features array */
++ if ( featureNode )
++ {
++ var aanFeatures = oSettings.aanFeatures;
++
++ if ( ! aanFeatures[cOption] )
++ {
++ aanFeatures[cOption] = [];
++ }
++
++ aanFeatures[cOption].push( featureNode );
++ insert.append( featureNode );
++ }
++ }
++
++ /* Built our DOM structure - replace the holding div with what we want */
++ holding.replaceWith( insert );
++ }
++
++
++ /**
++ * Use the DOM source to create up an array of header cells. The idea here is to
++ * create a layout grid (array) of rows x columns, which contains a reference
++ * to the cell that that point in the grid (regardless of col/rowspan), such that
++ * any column / row could be removed and the new grid constructed
++ * @param array {object} aLayout Array to store the calculated layout in
++ * @param {node} nThead The header/footer element for the table
++ * @memberof DataTable#oApi
++ */
++ function _fnDetectHeader ( aLayout, nThead )
++ {
++ var nTrs = $(nThead).children('tr');
++ var nTr, nCell;
++ var i, k, l, iLen, jLen, iColShifted, iColumn, iColspan, iRowspan;
++ var bUnique;
++ var fnShiftCol = function ( a, i, j ) {
++ var k = a[i];
++ while ( k[j] ) {
++ j++;
++ }
++ return j;
++ };
++
++ aLayout.splice( 0, aLayout.length );
++
++ /* We know how many rows there are in the layout - so prep it */
++ for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
++ {
++ aLayout.push( [] );
++ }
++
++ /* Calculate a layout array */
++ for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
++ {
++ nTr = nTrs[i];
++ iColumn = 0;
++
++ /* For every cell in the row... */
++ nCell = nTr.firstChild;
++ while ( nCell ) {
++ if ( nCell.nodeName.toUpperCase() == "TD" ||
++ nCell.nodeName.toUpperCase() == "TH" )
++ {
++ /* Get the col and rowspan attributes from the DOM and sanitise them */
++ iColspan = nCell.getAttribute('colspan') * 1;
++ iRowspan = nCell.getAttribute('rowspan') * 1;
++ iColspan = (!iColspan || iColspan===0 || iColspan===1) ? 1 : iColspan;
++ iRowspan = (!iRowspan || iRowspan===0 || iRowspan===1) ? 1 : iRowspan;
++
++ /* There might be colspan cells already in this row, so shift our target
++ * accordingly
++ */
++ iColShifted = fnShiftCol( aLayout, i, iColumn );
++
++ /* Cache calculation for unique columns */
++ bUnique = iColspan === 1 ? true : false;
++
++ /* If there is col / rowspan, copy the information into the layout grid */
++ for ( l=0 ; l<iColspan ; l++ )
++ {
++ for ( k=0 ; k<iRowspan ; k++ )
++ {
++ aLayout[i+k][iColShifted+l] = {
++ "cell": nCell,
++ "unique": bUnique
++ };
++ aLayout[i+k].nTr = nTr;
++ }
++ }
++ }
++ nCell = nCell.nextSibling;
++ }
++ }
++ }
++
++
++ /**
++ * Get an array of unique th elements, one for each column
++ * @param {object} oSettings dataTables settings object
++ * @param {node} nHeader automatically detect the layout from this node - optional
++ * @param {array} aLayout thead/tfoot layout from _fnDetectHeader - optional
++ * @returns array {node} aReturn list of unique th's
++ * @memberof DataTable#oApi
++ */
++ function _fnGetUniqueThs ( oSettings, nHeader, aLayout )
++ {
++ var aReturn = [];
++ if ( !aLayout )
++ {
++ aLayout = oSettings.aoHeader;
++ if ( nHeader )
++ {
++ aLayout = [];
++ _fnDetectHeader( aLayout, nHeader );
++ }
++ }
++
++ for ( var i=0, iLen=aLayout.length ; i<iLen ; i++ )
++ {
++ for ( var j=0, jLen=aLayout[i].length ; j<jLen ; j++ )
++ {
++ if ( aLayout[i][j].unique &&
++ (!aReturn[j] || !oSettings.bSortCellsTop) )
++ {
++ aReturn[j] = aLayout[i][j].cell;
++ }
++ }
++ }
++
++ return aReturn;
++ }
++
++ /**
++ * Create an Ajax call based on the table's settings, taking into account that
++ * parameters can have multiple forms, and backwards compatibility.
++ *
++ * @param {object} oSettings dataTables settings object
++ * @param {array} data Data to send to the server, required by
++ * DataTables - may be augmented by developer callbacks
++ * @param {function} fn Callback function to run when data is obtained
++ */
++ function _fnBuildAjax( oSettings, data, fn )
++ {
++ // Compatibility with 1.9-, allow fnServerData and event to manipulate
++ _fnCallbackFire( oSettings, 'aoServerParams', 'serverParams', [data] );
++
++ // Convert to object based for 1.10+ if using the old array scheme which can
++ // come from server-side processing or serverParams
++ if ( data && $.isArray(data) ) {
++ var tmp = {};
++ var rbracket = /(.*?)\[\]$/;
++
++ $.each( data, function (key, val) {
++ var match = val.name.match(rbracket);
++
++ if ( match ) {
++ // Support for arrays
++ var name = match[0];
++
++ if ( ! tmp[ name ] ) {
++ tmp[ name ] = [];
++ }
++ tmp[ name ].push( val.value );
++ }
++ else {
++ tmp[val.name] = val.value;
++ }
++ } );
++ data = tmp;
++ }
++
++ var ajaxData;
++ var ajax = oSettings.ajax;
++ var instance = oSettings.oInstance;
++ var callback = function ( json ) {
++ _fnCallbackFire( oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR] );
++ fn( json );
++ };
++
++ if ( $.isPlainObject( ajax ) && ajax.data )
++ {
++ ajaxData = ajax.data;
++
++ var newData = $.isFunction( ajaxData ) ?
++ ajaxData( data, oSettings ) : // fn can manipulate data or return
++ ajaxData; // an object object or array to merge
++
++ // If the function returned something, use that alone
++ data = $.isFunction( ajaxData ) && newData ?
++ newData :
++ $.extend( true, data, newData );
++
++ // Remove the data property as we've resolved it already and don't want
++ // jQuery to do it again (it is restored at the end of the function)
++ delete ajax.data;
++ }
++
++ var baseAjax = {
++ "data": data,
++ "success": function (json) {
++ var error = json.error || json.sError;
++ if ( error ) {
++ _fnLog( oSettings, 0, error );
++ }
++
++ oSettings.json = json;
++ callback( json );
++ },
++ "dataType": "json",
++ "cache": false,
++ "type": oSettings.sServerMethod,
++ "error": function (xhr, error, thrown) {
++ var ret = _fnCallbackFire( oSettings, null, 'xhr', [oSettings, null, oSettings.jqXHR] );
++
++ if ( $.inArray( true, ret ) === -1 ) {
++ if ( error == "parsererror" ) {
++ _fnLog( oSettings, 0, 'Invalid JSON response', 1 );
++ }
++ else if ( xhr.readyState === 4 ) {
++ _fnLog( oSettings, 0, 'Ajax error', 7 );
++ }
++ }
++
++ _fnProcessingDisplay( oSettings, false );
++ }
++ };
++
++ // Store the data submitted for the API
++ oSettings.oAjaxData = data;
++
++ // Allow plug-ins and external processes to modify the data
++ _fnCallbackFire( oSettings, null, 'preXhr', [oSettings, data] );
++
++ if ( oSettings.fnServerData )
++ {
++ // DataTables 1.9- compatibility
++ oSettings.fnServerData.call( instance,
++ oSettings.sAjaxSource,
++ $.map( data, function (val, key) { // Need to convert back to 1.9 trad format
++ return { name: key, value: val };
++ } ),
++ callback,
++ oSettings
++ );
++ }
++ else if ( oSettings.sAjaxSource || typeof ajax === 'string' )
++ {
++ // DataTables 1.9- compatibility
++ oSettings.jqXHR = $.ajax( $.extend( baseAjax, {
++ url: ajax || oSettings.sAjaxSource
++ } ) );
++ }
++ else if ( $.isFunction( ajax ) )
++ {
++ // Is a function - let the caller define what needs to be done
++ oSettings.jqXHR = ajax.call( instance, data, callback, oSettings );
++ }
++ else
++ {
++ // Object to extend the base settings
++ oSettings.jqXHR = $.ajax( $.extend( baseAjax, ajax ) );
++
++ // Restore for next time around
++ ajax.data = ajaxData;
++ }
++ }
++
++
++ /**
++ * Update the table using an Ajax call
++ * @param {object} settings dataTables settings object
++ * @returns {boolean} Block the table drawing or not
++ * @memberof DataTable#oApi
++ */
++ function _fnAjaxUpdate( settings )
++ {
++ if ( settings.bAjaxDataGet ) {
++ settings.iDraw++;
++ _fnProcessingDisplay( settings, true );
++
++ _fnBuildAjax(
++ settings,
++ _fnAjaxParameters( settings ),
++ function(json) {
++ _fnAjaxUpdateDraw( settings, json );
++ }
++ );
++
++ return false;
++ }
++ return true;
++ }
++
++
++ /**
++ * Build up the parameters in an object needed for a server-side processing
++ * request. Note that this is basically done twice, is different ways - a modern
++ * method which is used by default in DataTables 1.10 which uses objects and
++ * arrays, or the 1.9- method with is name / value pairs. 1.9 method is used if
++ * the sAjaxSource option is used in the initialisation, or the legacyAjax
++ * option is set.
++ * @param {object} oSettings dataTables settings object
++ * @returns {bool} block the table drawing or not
++ * @memberof DataTable#oApi
++ */
++ function _fnAjaxParameters( settings )
++ {
++ var
++ columns = settings.aoColumns,
++ columnCount = columns.length,
++ features = settings.oFeatures,
++ preSearch = settings.oPreviousSearch,
++ preColSearch = settings.aoPreSearchCols,
++ i, data = [], dataProp, column, columnSearch,
++ sort = _fnSortFlatten( settings ),
++ displayStart = settings._iDisplayStart,
++ displayLength = features.bPaginate !== false ?
++ settings._iDisplayLength :
++ -1;
++
++ var param = function ( name, value ) {
++ data.push( { 'name': name, 'value': value } );
++ };
++
++ // DataTables 1.9- compatible method
++ param( 'sEcho', settings.iDraw );
++ param( 'iColumns', columnCount );
++ param( 'sColumns', _pluck( columns, 'sName' ).join(',') );
++ param( 'iDisplayStart', displayStart );
++ param( 'iDisplayLength', displayLength );
++
++ // DataTables 1.10+ method
++ var d = {
++ draw: settings.iDraw,
++ columns: [],
++ order: [],
++ start: displayStart,
++ length: displayLength,
++ search: {
++ value: preSearch.sSearch,
++ regex: preSearch.bRegex
++ }
++ };
++
++ for ( i=0 ; i<columnCount ; i++ ) {
++ column = columns[i];
++ columnSearch = preColSearch[i];
++ dataProp = typeof column.mData=="function" ? 'function' : column.mData ;
++
++ d.columns.push( {
++ data: dataProp,
++ name: column.sName,
++ searchable: column.bSearchable,
++ orderable: column.bSortable,
++ search: {
++ value: columnSearch.sSearch,
++ regex: columnSearch.bRegex
++ }
++ } );
++
++ param( "mDataProp_"+i, dataProp );
++
++ if ( features.bFilter ) {
++ param( 'sSearch_'+i, columnSearch.sSearch );
++ param( 'bRegex_'+i, columnSearch.bRegex );
++ param( 'bSearchable_'+i, column.bSearchable );
++ }
++
++ if ( features.bSort ) {
++ param( 'bSortable_'+i, column.bSortable );
++ }
++ }
++
++ if ( features.bFilter ) {
++ param( 'sSearch', preSearch.sSearch );
++ param( 'bRegex', preSearch.bRegex );
++ }
++
++ if ( features.bSort ) {
++ $.each( sort, function ( i, val ) {
++ d.order.push( { column: val.col, dir: val.dir } );
++
++ param( 'iSortCol_'+i, val.col );
++ param( 'sSortDir_'+i, val.dir );
++ } );
++
++ param( 'iSortingCols', sort.length );
++ }
++
++ // If the legacy.ajax parameter is null, then we automatically decide which
++ // form to use, based on sAjaxSource
++ var legacy = DataTable.ext.legacy.ajax;
++ if ( legacy === null ) {
++ return settings.sAjaxSource ? data : d;
++ }
++
++ // Otherwise, if legacy has been specified then we use that to decide on the
++ // form
++ return legacy ? data : d;
++ }
++
++
++ /**
++ * Data the data from the server (nuking the old) and redraw the table
++ * @param {object} oSettings dataTables settings object
++ * @param {object} json json data return from the server.
++ * @param {string} json.sEcho Tracking flag for DataTables to match requests
++ * @param {int} json.iTotalRecords Number of records in the data set, not accounting for filtering
++ * @param {int} json.iTotalDisplayRecords Number of records in the data set, accounting for filtering
++ * @param {array} json.aaData The data to display on this page
++ * @param {string} [json.sColumns] Column ordering (sName, comma separated)
++ * @memberof DataTable#oApi
++ */
++ function _fnAjaxUpdateDraw ( settings, json )
++ {
++ // v1.10 uses camelCase variables, while 1.9 uses Hungarian notation.
++ // Support both
++ var compat = function ( old, modern ) {
++ return json[old] !== undefined ? json[old] : json[modern];
++ };
++
++ var data = _fnAjaxDataSrc( settings, json );
++ var draw = compat( 'sEcho', 'draw' );
++ var recordsTotal = compat( 'iTotalRecords', 'recordsTotal' );
++ var recordsFiltered = compat( 'iTotalDisplayRecords', 'recordsFiltered' );
++
++ if ( draw ) {
++ // Protect against out of sequence returns
++ if ( draw*1 < settings.iDraw ) {
++ return;
++ }
++ settings.iDraw = draw * 1;
++ }
++
++ _fnClearTable( settings );
++ settings._iRecordsTotal = parseInt(recordsTotal, 10);
++ settings._iRecordsDisplay = parseInt(recordsFiltered, 10);
++
++ for ( var i=0, ien=data.length ; i<ien ; i++ ) {
++ _fnAddData( settings, data[i] );
++ }
++ settings.aiDisplay = settings.aiDisplayMaster.slice();
++
++ settings.bAjaxDataGet = false;
++ _fnDraw( settings );
++
++ if ( ! settings._bInitComplete ) {
++ _fnInitComplete( settings, json );
++ }
++
++ settings.bAjaxDataGet = true;
++ _fnProcessingDisplay( settings, false );
++ }
++
++
++ /**
++ * Get the data from the JSON data source to use for drawing a table. Using
++ * `_fnGetObjectDataFn` allows the data to be sourced from a property of the
++ * source object, or from a processing function.
++ * @param {object} oSettings dataTables settings object
++ * @param {object} json Data source object / array from the server
++ * @return {array} Array of data to use
++ */
++ function _fnAjaxDataSrc ( oSettings, json )
++ {
++ var dataSrc = $.isPlainObject( oSettings.ajax ) && oSettings.ajax.dataSrc !== undefined ?
++ oSettings.ajax.dataSrc :
++ oSettings.sAjaxDataProp; // Compatibility with 1.9-.
++
++ // Compatibility with 1.9-. In order to read from aaData, check if the
++ // default has been changed, if not, check for aaData
++ if ( dataSrc === 'data' ) {
++ return json.aaData || json[dataSrc];
++ }
++
++ return dataSrc !== "" ?
++ _fnGetObjectDataFn( dataSrc )( json ) :
++ json;
++ }
++
++ /**
++ * Generate the node required for filtering text
++ * @returns {node} Filter control element
++ * @param {object} oSettings dataTables settings object
++ * @memberof DataTable#oApi
++ */
++ function _fnFeatureHtmlFilter ( settings )
++ {
++ var classes = settings.oClasses;
++ var tableId = settings.sTableId;
++ var language = settings.oLanguage;
++ var previousSearch = settings.oPreviousSearch;
++ var features = settings.aanFeatures;
++ var input = '<input type="search" class="'+classes.sFilterInput+'"/>';
++
++ var str = language.sSearch;
++ str = str.match(/_INPUT_/) ?
++ str.replace('_INPUT_', input) :
++ str+input;
++
++ var filter = $('<div/>', {
++ 'id': ! features.f ? tableId+'_filter' : null,
++ 'class': classes.sFilter
++ } )
++ .append( $('<label/>' ).append( str ) );
++
++ var searchFn = function() {
++ /* Update all other filter input elements for the new display */
++ var n = features.f;
++ var val = !this.value ? "" : this.value; // mental IE8 fix :-(
++
++ /* Now do the filter */
++ if ( val != previousSearch.sSearch ) {
++ _fnFilterComplete( settings, {
++ "sSearch": val,
++ "bRegex": previousSearch.bRegex,
++ "bSmart": previousSearch.bSmart ,
++ "bCaseInsensitive": previousSearch.bCaseInsensitive
++ } );
++
++ // Need to redraw, without resorting
++ settings._iDisplayStart = 0;
++ _fnDraw( settings );
++ }
++ };
++
++ var searchDelay = settings.searchDelay !== null ?
++ settings.searchDelay :
++ _fnDataSource( settings ) === 'ssp' ?
++ 400 :
++ 0;
++
++ var jqFilter = $('input', filter)
++ .val( previousSearch.sSearch )
++ .attr( 'placeholder', language.sSearchPlaceholder )
++ .bind(
++ 'keyup.DT search.DT input.DT paste.DT cut.DT',
++ searchDelay ?
++ _fnThrottle( searchFn, searchDelay ) :
++ searchFn
++ )
++ .bind( 'keypress.DT', function(e) {
++ /* Prevent form submission */
++ if ( e.keyCode == 13 ) {
++ return false;
++ }
++ } )
++ .attr('aria-controls', tableId);
++
++ // Update the input elements whenever the table is filtered
++ $(settings.nTable).on( 'search.dt.DT', function ( ev, s ) {
++ if ( settings === s ) {
++ // IE9 throws an 'unknown error' if document.activeElement is used
++ // inside an iframe or frame...
++ try {
++ if ( jqFilter[0] !== document.activeElement ) {
++ jqFilter.val( previousSearch.sSearch );
++ }
++ }
++ catch ( e ) {}
++ }
++ } );
++
++ return filter[0];
++ }
++
++
++ /**
++ * Filter the table using both the global filter and column based filtering
++ * @param {object} oSettings dataTables settings object
++ * @param {object} oSearch search information
++ * @param {int} [iForce] force a research of the master array (1) or not (undefined or 0)
++ * @memberof DataTable#oApi
++ */
++ function _fnFilterComplete ( oSettings, oInput, iForce )
++ {
++ var oPrevSearch = oSettings.oPreviousSearch;
++ var aoPrevSearch = oSettings.aoPreSearchCols;
++ var fnSaveFilter = function ( oFilter ) {
++ /* Save the filtering values */
++ oPrevSearch.sSearch = oFilter.sSearch;
++ oPrevSearch.bRegex = oFilter.bRegex;
++ oPrevSearch.bSmart = oFilter.bSmart;
++ oPrevSearch.bCaseInsensitive = oFilter.bCaseInsensitive;
++ };
++ var fnRegex = function ( o ) {
++ // Backwards compatibility with the bEscapeRegex option
++ return o.bEscapeRegex !== undefined ? !o.bEscapeRegex : o.bRegex;
++ };
++
++ // Resolve any column types that are unknown due to addition or invalidation
++ // @todo As per sort - can this be moved into an event handler?
++ _fnColumnTypes( oSettings );
++
++ /* In server-side processing all filtering is done by the server, so no point hanging around here */
++ if ( _fnDataSource( oSettings ) != 'ssp' )
++ {
++ /* Global filter */
++ _fnFilter( oSettings, oInput.sSearch, iForce, fnRegex(oInput), oInput.bSmart, oInput.bCaseInsensitive );
++ fnSaveFilter( oInput );
++
++ /* Now do the individual column filter */
++ for ( var i=0 ; i<aoPrevSearch.length ; i++ )
++ {
++ _fnFilterColumn( oSettings, aoPrevSearch[i].sSearch, i, fnRegex(aoPrevSearch[i]),
++ aoPrevSearch[i].bSmart, aoPrevSearch[i].bCaseInsensitive );
++ }
++
++ /* Custom filtering */
++ _fnFilterCustom( oSettings );
++ }
++ else
++ {
++ fnSaveFilter( oInput );
++ }
++
++ /* Tell the draw function we have been filtering */
++ oSettings.bFiltered = true;
++ _fnCallbackFire( oSettings, null, 'search', [oSettings] );
++ }
++
++
++ /**
++ * Apply custom filtering functions
++ * @param {object} oSettings dataTables settings object
++ * @memberof DataTable#oApi
++ */
++ function _fnFilterCustom( settings )
++ {
++ var filters = DataTable.ext.search;
++ var displayRows = settings.aiDisplay;
++ var row, rowIdx;
++
++ for ( var i=0, ien=filters.length ; i<ien ; i++ ) {
++ var rows = [];
++
++ // Loop over each row and see if it should be included
++ for ( var j=0, jen=displayRows.length ; j<jen ; j++ ) {
++ rowIdx = displayRows[ j ];
++ row = settings.aoData[ rowIdx ];
++
++ if ( filters[i]( settings, row._aFilterData, rowIdx, row._aData, j ) ) {
++ rows.push( rowIdx );
++ }
++ }
++
++ // So the array reference doesn't break set the results into the
++ // existing array
++ displayRows.length = 0;
++ displayRows.push.apply( displayRows, rows );
++ }
++ }
++
++
++ /**
++ * Filter the table on a per-column basis
++ * @param {object} oSettings dataTables settings object
++ * @param {string} sInput string to filter on
++ * @param {int} iColumn column to filter
++ * @param {bool} bRegex treat search string as a regular expression or not
++ * @param {bool} bSmart use smart filtering or not
++ * @param {bool} bCaseInsensitive Do case insenstive matching or not
++ * @memberof DataTable#oApi
++ */
++ function _fnFilterColumn ( settings, searchStr, colIdx, regex, smart, caseInsensitive )
++ {
++ if ( searchStr === '' ) {
++ return;
++ }
++
++ var data;
++ var display = settings.aiDisplay;
++ var rpSearch = _fnFilterCreateSearch( searchStr, regex, smart, caseInsensitive );
++
++ for ( var i=display.length-1 ; i>=0 ; i-- ) {
++ data = settings.aoData[ display[i] ]._aFilterData[ colIdx ];
++
++ if ( ! rpSearch.test( data ) ) {
++ display.splice( i, 1 );
++ }
++ }
++ }
++
++
++ /**
++ * Filter the data table based on user input and draw the table
++ * @param {object} settings dataTables settings object
++ * @param {string} input string to filter on
++ * @param {int} force optional - force a research of the master array (1) or not (undefined or 0)
++ * @param {bool} regex treat as a regular expression or not
++ * @param {bool} smart perform smart filtering or not
++ * @param {bool} caseInsensitive Do case insenstive matching or not
++ * @memberof DataTable#oApi
++ */
++ function _fnFilter( settings, input, force, regex, smart, caseInsensitive )
++ {
++ var rpSearch = _fnFilterCreateSearch( input, regex, smart, caseInsensitive );
++ var prevSearch = settings.oPreviousSearch.sSearch;
++ var displayMaster = settings.aiDisplayMaster;
++ var display, invalidated, i;
++
++ // Need to take account of custom filtering functions - always filter
++ if ( DataTable.ext.search.length !== 0 ) {
++ force = true;
++ }
++
++ // Check if any of the rows were invalidated
++ invalidated = _fnFilterData( settings );
++
++ // If the input is blank - we just want the full data set
++ if ( input.length <= 0 ) {
++ settings.aiDisplay = displayMaster.slice();
++ }
++ else {
++ // New search - start from the master array
++ if ( invalidated ||
++ force ||
++ prevSearch.length > input.length ||
++ input.indexOf(prevSearch) !== 0 ||
++ settings.bSorted // On resort, the display master needs to be
++ // re-filtered since indexes will have changed
++ ) {
++ settings.aiDisplay = displayMaster.slice();
++ }
++
++ // Search the display array
++ display = settings.aiDisplay;
++
++ for ( i=display.length-1 ; i>=0 ; i-- ) {
++ if ( ! rpSearch.test( settings.aoData[ display[i] ]._sFilterRow ) ) {
++ display.splice( i, 1 );
++ }
++ }
++ }
++ }
++
++
++ /**
++ * Build a regular expression object suitable for searching a table
++ * @param {string} sSearch string to search for
++ * @param {bool} bRegex treat as a regular expression or not
++ * @param {bool} bSmart perform smart filtering or not
++ * @param {bool} bCaseInsensitive Do case insensitive matching or not
++ * @returns {RegExp} constructed object
++ * @memberof DataTable#oApi
++ */
++ function _fnFilterCreateSearch( search, regex, smart, caseInsensitive )
++ {
++ search = regex ?
++ search :
++ _fnEscapeRegex( search );
++
++ if ( smart ) {
++ /* For smart filtering we want to allow the search to work regardless of
++ * word order. We also want double quoted text to be preserved, so word
++ * order is important - a la google. So this is what we want to
++ * generate:
++ *
++ * ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$
++ */
++ var a = $.map( search.match( /"[^"]+"|[^ ]+/g ) || [''], function ( word ) {
++ if ( word.charAt(0) === '"' ) {
++ var m = word.match( /^"(.*)"$/ );
++ word = m ? m[1] : word;
++ }
++
++ return word.replace('"', '');
++ } );
++
++ search = '^(?=.*?'+a.join( ')(?=.*?' )+').*$';
++ }
++
++ return new RegExp( search, caseInsensitive ? 'i' : '' );
++ }
++
++
++ /**
++ * Escape a string such that it can be used in a regular expression
++ * @param {string} sVal string to escape
++ * @returns {string} escaped string
++ * @memberof DataTable#oApi
++ */
++ function _fnEscapeRegex ( sVal )
++ {
++ return sVal.replace( _re_escape_regex, '\\$1' );
++ }
++
++
++
++ var __filter_div = $('<div>')[0];
++ var __filter_div_textContent = __filter_div.textContent !== undefined;
++
++ // Update the filtering data for each row if needed (by invalidation or first run)
++ function _fnFilterData ( settings )
++ {
++ var columns = settings.aoColumns;
++ var column;
++ var i, j, ien, jen, filterData, cellData, row;
++ var fomatters = DataTable.ext.type.search;
++ var wasInvalidated = false;
++
++ for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
++ row = settings.aoData[i];
++
++ if ( ! row._aFilterData ) {
++ filterData = [];
++
++ for ( j=0, jen=columns.length ; j<jen ; j++ ) {
++ column = columns[j];
++
++ if ( column.bSearchable ) {
++ cellData = _fnGetCellData( settings, i, j, 'filter' );
++
++ if ( fomatters[ column.sType ] ) {
++ cellData = fomatters[ column.sType ]( cellData );
++ }
++
++ // Search in DataTables 1.10 is string based. In 1.11 this
++ // should be altered to also allow strict type checking.
++ if ( cellData === null ) {
++ cellData = '';
++ }
++
++ if ( typeof cellData !== 'string' && cellData.toString ) {
++ cellData = cellData.toString();
++ }
++ }
++ else {
++ cellData = '';
++ }
++
++ // If it looks like there is an HTML entity in the string,
++ // attempt to decode it so sorting works as expected. Note that
++ // we could use a single line of jQuery to do this, but the DOM
++ // method used here is much faster http://jsperf.com/html-decode
++ if ( cellData.indexOf && cellData.indexOf('&') !== -1 ) {
++ __filter_div.innerHTML = cellData;
++ cellData = __filter_div_textContent ?
++ __filter_div.textContent :
++ __filter_div.innerText;
++ }
++
++ if ( cellData.replace ) {
++ cellData = cellData.replace(/[\r\n]/g, '');
++ }
++
++ filterData.push( cellData );
++ }
++
++ row._aFilterData = filterData;
++ row._sFilterRow = filterData.join(' ');
++ wasInvalidated = true;
++ }
++ }
++
++ return wasInvalidated;
++ }
++
++
++ /**
++ * Convert from the internal Hungarian notation to camelCase for external
++ * interaction
++ * @param {object} obj Object to convert
++ * @returns {object} Inverted object
++ * @memberof DataTable#oApi
++ */
++ function _fnSearchToCamel ( obj )
++ {
++ return {
++ search: obj.sSearch,
++ smart: obj.bSmart,
++ regex: obj.bRegex,
++ caseInsensitive: obj.bCaseInsensitive
++ };
++ }
++
++
++
++ /**
++ * Convert from camelCase notation to the internal Hungarian. We could use the
++ * Hungarian convert function here, but this is cleaner
++ * @param {object} obj Object to convert
++ * @returns {object} Inverted object
++ * @memberof DataTable#oApi
++ */
++ function _fnSearchToHung ( obj )
++ {
++ return {
++ sSearch: obj.search,
++ bSmart: obj.smart,
++ bRegex: obj.regex,
++ bCaseInsensitive: obj.caseInsensitive
++ };
++ }
++
++ /**
++ * Generate the node required for the info display
++ * @param {object} oSettings dataTables settings object
++ * @returns {node} Information element
++ * @memberof DataTable#oApi
++ */
++ function _fnFeatureHtmlInfo ( settings )
++ {
++ var
++ tid = settings.sTableId,
++ nodes = settings.aanFeatures.i,
++ n = $('<div/>', {
++ 'class': settings.oClasses.sInfo,
++ 'id': ! nodes ? tid+'_info' : null
++ } );
++
++ if ( ! nodes ) {
++ // Update display on each draw
++ settings.aoDrawCallback.push( {
++ "fn": _fnUpdateInfo,
++ "sName": "information"
++ } );
++
++ n
++ .attr( 'role', 'status' )
++ .attr( 'aria-live', 'polite' );
++
++ // Table is described by our info div
++ $(settings.nTable).attr( 'aria-describedby', tid+'_info' );
++ }
++
++ return n[0];
++ }
++
++
++ /**
++ * Update the information elements in the display
++ * @param {object} settings dataTables settings object
++ * @memberof DataTable#oApi
++ */
++ function _fnUpdateInfo ( settings )
++ {
++ /* Show information about the table */
++ var nodes = settings.aanFeatures.i;
++ if ( nodes.length === 0 ) {
++ return;
++ }
++
++ var
++ lang = settings.oLanguage,
++ start = settings._iDisplayStart+1,
++ end = settings.fnDisplayEnd(),
++ max = settings.fnRecordsTotal(),
++ total = settings.fnRecordsDisplay(),
++ out = total ?
++ lang.sInfo :
++ lang.sInfoEmpty;
++
++ if ( total !== max ) {
++ /* Record set after filtering */
++ out += ' ' + lang.sInfoFiltered;
++ }
++
++ // Convert the macros
++ out += lang.sInfoPostFix;
++ out = _fnInfoMacros( settings, out );
++
++ var callback = lang.fnInfoCallback;
++ if ( callback !== null ) {
++ out = callback.call( settings.oInstance,
++ settings, start, end, max, total, out
++ );
++ }
++
++ $(nodes).html( out );
++ }
++
++
++ function _fnInfoMacros ( settings, str )
++ {
++ // When infinite scrolling, we are always starting at 1. _iDisplayStart is used only
++ // internally
++ var
++ formatter = settings.fnFormatNumber,
++ start = settings._iDisplayStart+1,
++ len = settings._iDisplayLength,
++ vis = settings.fnRecordsDisplay(),
++ all = len === -1;
++
++ return str.
++ replace(/_START_/g, formatter.call( settings, start ) ).
++ replace(/_END_/g, formatter.call( settings, settings.fnDisplayEnd() ) ).
++ replace(/_MAX_/g, formatter.call( settings, settings.fnRecordsTotal() ) ).
++ replace(/_TOTAL_/g, formatter.call( settings, vis ) ).
++ replace(/_PAGE_/g, formatter.call( settings, all ? 1 : Math.ceil( start / len ) ) ).
++ replace(/_PAGES_/g, formatter.call( settings, all ? 1 : Math.ceil( vis / len ) ) );
++ }
++
++
++
++ /**
++ * Draw the table for the first time, adding all required features
++ * @param {object} settings dataTables settings object
++ * @memberof DataTable#oApi
++ */
++ function _fnInitialise ( settings )
++ {
++ var i, iLen, iAjaxStart=settings.iInitDisplayStart;
++ var columns = settings.aoColumns, column;
++ var features = settings.oFeatures;
++
++ /* Ensure that the table data is fully initialised */
++ if ( ! settings.bInitialised ) {
++ setTimeout( function(){ _fnInitialise( settings ); }, 200 );
++ return;
++ }
++
++ /* Show the display HTML options */
++ _fnAddOptionsHtml( settings );
++
++ /* Build and draw the header / footer for the table */
++ _fnBuildHead( settings );
++ _fnDrawHead( settings, settings.aoHeader );
++ _fnDrawHead( settings, settings.aoFooter );
++
++ /* Okay to show that something is going on now */
++ _fnProcessingDisplay( settings, true );
++
++ /* Calculate sizes for columns */
++ if ( features.bAutoWidth ) {
++ _fnCalculateColumnWidths( settings );
++ }
++
++ for ( i=0, iLen=columns.length ; i<iLen ; i++ ) {
++ column = columns[i];
++
++ if ( column.sWidth ) {
++ column.nTh.style.width = _fnStringToCss( column.sWidth );
++ }
++ }
++
++ // If there is default sorting required - let's do it. The sort function
++ // will do the drawing for us. Otherwise we draw the table regardless of the
++ // Ajax source - this allows the table to look initialised for Ajax sourcing
++ // data (show 'loading' message possibly)
++ _fnReDraw( settings );
++
++ // Server-side processing init complete is done by _fnAjaxUpdateDraw
++ var dataSrc = _fnDataSource( settings );
++ if ( dataSrc != 'ssp' ) {
++ // if there is an ajax source load the data
++ if ( dataSrc == 'ajax' ) {
++ _fnBuildAjax( settings, [], function(json) {
++ var aData = _fnAjaxDataSrc( settings, json );
++
++ // Got the data - add it to the table
++ for ( i=0 ; i<aData.length ; i++ ) {
++ _fnAddData( settings, aData[i] );
++ }
++
++ // Reset the init display for cookie saving. We've already done
++ // a filter, and therefore cleared it before. So we need to make
++ // it appear 'fresh'
++ settings.iInitDisplayStart = iAjaxStart;
++
++ _fnReDraw( settings );
++
++ _fnProcessingDisplay( settings, false );
++ _fnInitComplete( settings, json );
++ }, settings );
++ }
++ else {
++ _fnProcessingDisplay( settings, false );
++ _fnInitComplete( settings );
++ }
++ }
++ }
++
++
++ /**
++ * Draw the table for the first time, adding all required features
++ * @param {object} oSettings dataTables settings object
++ * @param {object} [json] JSON from the server that completed the table, if using Ajax source
++ * with client-side processing (optional)
++ * @memberof DataTable#oApi
++ */
++ function _fnInitComplete ( settings, json )
++ {
++ settings._bInitComplete = true;
++
++ // On an Ajax load we now have data and therefore want to apply the column
++ // sizing
++ if ( json ) {
++ _fnAdjustColumnSizing( settings );
++ }
++
++ _fnCallbackFire( settings, 'aoInitComplete', 'init', [settings, json] );
++ }
++
++
++ function _fnLengthChange ( settings, val )
++ {
++ var len = parseInt( val, 10 );
++ settings._iDisplayLength = len;
++
++ _fnLengthOverflow( settings );
++
++ // Fire length change event
++ _fnCallbackFire( settings, null, 'length', [settings, len] );
++ }
++
++
++ /**
++ * Generate the node required for user display length changing
++ * @param {object} settings dataTables settings object
++ * @returns {node} Display length feature node
++ * @memberof DataTable#oApi
++ */
++ function _fnFeatureHtmlLength ( settings )
++ {
++ var
++ classes = settings.oClasses,
++ tableId = settings.sTableId,
++ menu = settings.aLengthMenu,
++ d2 = $.isArray( menu[0] ),
++ lengths = d2 ? menu[0] : menu,
++ language = d2 ? menu[1] : menu;
++
++ var select = $('<select/>', {
++ 'name': tableId+'_length',
++ 'aria-controls': tableId,
++ 'class': classes.sLengthSelect
++ } );
++
++ for ( var i=0, ien=lengths.length ; i<ien ; i++ ) {
++ select[0][ i ] = new Option( language[i], lengths[i] );
++ }
++
++ var div = $('<div><label/></div>').addClass( classes.sLength );
++ if ( ! settings.aanFeatures.l ) {
++ div[0].id = tableId+'_length';
++ }
++
++ div.children().append(
++ settings.oLanguage.sLengthMenu.replace( '_MENU_', select[0].outerHTML )
++ );
++
++ // Can't use `select` variable as user might provide their own and the
++ // reference is broken by the use of outerHTML
++ $('select', div)
++ .val( settings._iDisplayLength )
++ .bind( 'change.DT', function(e) {
++ _fnLengthChange( settings, $(this).val() );
++ _fnDraw( settings );
++ } );
++
++ // Update node value whenever anything changes the table's length
++ $(settings.nTable).bind( 'length.dt.DT', function (e, s, len) {
++ if ( settings === s ) {
++ $('select', div).val( len );
++ }
++ } );
++
++ return div[0];
++ }
++
++
++
++ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
++ * Note that most of the paging logic is done in
++ * DataTable.ext.pager
++ */
++
++ /**
++ * Generate the node required for default pagination
++ * @param {object} oSettings dataTables settings object
++ * @returns {node} Pagination feature node
++ * @memberof DataTable#oApi
++ */
++ function _fnFeatureHtmlPaginate ( settings )
++ {
++ var
++ type = settings.sPaginationType,
++ plugin = DataTable.ext.pager[ type ],
++ modern = typeof plugin === 'function',
++ redraw = function( settings ) {
++ _fnDraw( settings );
++ },
++ node = $('<div/>').addClass( settings.oClasses.sPaging + type )[0],
++ features = settings.aanFeatures;
++
++ if ( ! modern ) {
++ plugin.fnInit( settings, node, redraw );
++ }
++
++ /* Add a draw callback for the pagination on first instance, to update the paging display */
++ if ( ! features.p )
++ {
++ node.id = settings.sTableId+'_paginate';
++
++ settings.aoDrawCallback.push( {
++ "fn": function( settings ) {
++ if ( modern ) {
++ var
++ start = settings._iDisplayStart,
++ len = settings._iDisplayLength,
++ visRecords = settings.fnRecordsDisplay(),
++ all = len === -1,
++ page = all ? 0 : Math.ceil( start / len ),
++ pages = all ? 1 : Math.ceil( visRecords / len ),
++ buttons = plugin(page, pages),
++ i, ien;
++
++ for ( i=0, ien=features.p.length ; i<ien ; i++ ) {
++ _fnRenderer( settings, 'pageButton' )(
++ settings, features.p[i], i, buttons, page, pages
++ );
++ }
++ }
++ else {
++ plugin.fnUpdate( settings, redraw );
++ }
++ },
++ "sName": "pagination"
++ } );
++ }
++
++ return node;
++ }
++
++
++ /**
++ * Alter the display settings to change the page
++ * @param {object} settings DataTables settings object
++ * @param {string|int} action Paging action to take: "first", "previous",
++ * "next" or "last" or page number to jump to (integer)
++ * @param [bool] redraw Automatically draw the update or not
++ * @returns {bool} true page has changed, false - no change
++ * @memberof DataTable#oApi
++ */
++ function _fnPageChange ( settings, action, redraw )
++ {
++ var
++ start = settings._iDisplayStart,
++ len = settings._iDisplayLength,
++ records = settings.fnRecordsDisplay();
++
++ if ( records === 0 || len === -1 )
++ {
++ start = 0;
++ }
++ else if ( typeof action === "number" )
++ {
++ start = action * len;
++
++ if ( start > records )
++ {
++ start = 0;
++ }
++ }
++ else if ( action == "first" )
++ {
++ start = 0;
++ }
++ else if ( action == "previous" )
++ {
++ start = len >= 0 ?
++ start - len :
++ 0;
++
++ if ( start < 0 )
++ {
++ start = 0;
++ }
++ }
++ else if ( action == "next" )
++ {
++ if ( start + len < records )
++ {
++ start += len;
++ }
++ }
++ else if ( action == "last" )
++ {
++ start = Math.floor( (records-1) / len) * len;
++ }
++ else
++ {
++ _fnLog( settings, 0, "Unknown paging action: "+action, 5 );
++ }
++
++ var changed = settings._iDisplayStart !== start;
++ settings._iDisplayStart = start;
++
++ if ( changed ) {
++ _fnCallbackFire( settings, null, 'page', [settings] );
++
++ if ( redraw ) {
++ _fnDraw( settings );
++ }
++ }
++
++ return changed;
++ }
++
++
++
++ /**
++ * Generate the node required for the processing node
++ * @param {object} settings dataTables settings object
++ * @returns {node} Processing element
++ * @memberof DataTable#oApi
++ */
++ function _fnFeatureHtmlProcessing ( settings )
++ {
++ return $('<div/>', {
++ 'id': ! settings.aanFeatures.r ? settings.sTableId+'_processing' : null,
++ 'class': settings.oClasses.sProcessing
++ } )
++ .html( settings.oLanguage.sProcessing )
++ .insertBefore( settings.nTable )[0];
++ }
++
++
++ /**
++ * Display or hide the processing indicator
++ * @param {object} settings dataTables settings object
++ * @param {bool} show Show the processing indicator (true) or not (false)
++ * @memberof DataTable#oApi
++ */
++ function _fnProcessingDisplay ( settings, show )
++ {
++ if ( settings.oFeatures.bProcessing ) {
++ $(settings.aanFeatures.r).css( 'display', show ? 'block' : 'none' );
++ }
++
++ _fnCallbackFire( settings, null, 'processing', [settings, show] );
++ }
++
++ /**
++ * Add any control elements for the table - specifically scrolling
++ * @param {object} settings dataTables settings object
++ * @returns {node} Node to add to the DOM
++ * @memberof DataTable#oApi
++ */
++ function _fnFeatureHtmlTable ( settings )
++ {
++ var table = $(settings.nTable);
++
++ // Add the ARIA grid role to the table
++ table.attr( 'role', 'grid' );
++
++ // Scrolling from here on in
++ var scroll = settings.oScroll;
++
++ if ( scroll.sX === '' && scroll.sY === '' ) {
++ return settings.nTable;
++ }
++
++ var scrollX = scroll.sX;
++ var scrollY = scroll.sY;
++ var classes = settings.oClasses;
++ var caption = table.children('caption');
++ var captionSide = caption.length ? caption[0]._captionSide : null;
++ var headerClone = $( table[0].cloneNode(false) );
++ var footerClone = $( table[0].cloneNode(false) );
++ var footer = table.children('tfoot');
++ var _div = '<div/>';
++ var size = function ( s ) {
++ return !s ? null : _fnStringToCss( s );
++ };
++
++ // This is fairly messy, but with x scrolling enabled, if the table has a
++ // width attribute, regardless of any width applied using the column width
++ // options, the browser will shrink or grow the table as needed to fit into
++ // that 100%. That would make the width options useless. So we remove it.
++ // This is okay, under the assumption that width:100% is applied to the
++ // table in CSS (it is in the default stylesheet) which will set the table
++ // width as appropriate (the attribute and css behave differently...)
++ if ( scroll.sX && table.attr('width') === '100%' ) {
++ table.removeAttr('width');
++ }
++
++ if ( ! footer.length ) {
++ footer = null;
++ }
++
++ /*
++ * The HTML structure that we want to generate in this function is:
++ * div - scroller
++ * div - scroll head
++ * div - scroll head inner
++ * table - scroll head table
++ * thead - thead
++ * div - scroll body
++ * table - table (master table)
++ * thead - thead clone for sizing
++ * tbody - tbody
++ * div - scroll foot
++ * div - scroll foot inner
++ * table - scroll foot table
++ * tfoot - tfoot
++ */
++ var scroller = $( _div, { 'class': classes.sScrollWrapper } )
++ .append(
++ $(_div, { 'class': classes.sScrollHead } )
++ .css( {
++ overflow: 'hidden',
++ position: 'relative',
++ border: 0,
++ width: scrollX ? size(scrollX) : '100%'
++ } )
++ .append(
++ $(_div, { 'class': classes.sScrollHeadInner } )
++ .css( {
++ 'box-sizing': 'content-box',
++ width: scroll.sXInner || '100%'
++ } )
++ .append(
++ headerClone
++ .removeAttr('id')
++ .css( 'margin-left', 0 )
++ .append( captionSide === 'top' ? caption : null )
++ .append(
++ table.children('thead')
++ )
++ )
++ )
++ )
++ .append(
++ $(_div, { 'class': classes.sScrollBody } )
++ .css( {
++ overflow: 'auto',
++ height: size( scrollY ),
++ width: size( scrollX )
++ } )
++ .append( table )
++ );
++
++ if ( footer ) {
++ scroller.append(
++ $(_div, { 'class': classes.sScrollFoot } )
++ .css( {
++ overflow: 'hidden',
++ border: 0,
++ width: scrollX ? size(scrollX) : '100%'
++ } )
++ .append(
++ $(_div, { 'class': classes.sScrollFootInner } )
++ .append(
++ footerClone
++ .removeAttr('id')
++ .css( 'margin-left', 0 )
++ .append( captionSide === 'bottom' ? caption : null )
++ .append(
++ table.children('tfoot')
++ )
++ )
++ )
++ );
++ }
++
++ var children = scroller.children();
++ var scrollHead = children[0];
++ var scrollBody = children[1];
++ var scrollFoot = footer ? children[2] : null;
++
++ // When the body is scrolled, then we also want to scroll the headers
++ if ( scrollX ) {
++ $(scrollBody).on( 'scroll.DT', function (e) {
++ var scrollLeft = this.scrollLeft;
++
++ scrollHead.scrollLeft = scrollLeft;
++
++ if ( footer ) {
++ scrollFoot.scrollLeft = scrollLeft;
++ }
++ } );
++ }
++
++ settings.nScrollHead = scrollHead;
++ settings.nScrollBody = scrollBody;
++ settings.nScrollFoot = scrollFoot;
++
++ // On redraw - align columns
++ settings.aoDrawCallback.push( {
++ "fn": _fnScrollDraw,
++ "sName": "scrolling"
++ } );
++
++ return scroller[0];
++ }
++
++
++
++ /**
++ * Update the header, footer and body tables for resizing - i.e. column
++ * alignment.
++ *
++ * Welcome to the most horrible function DataTables. The process that this
++ * function follows is basically:
++ * 1. Re-create the table inside the scrolling div
++ * 2. Take live measurements from the DOM
++ * 3. Apply the measurements to align the columns
++ * 4. Clean up
++ *
++ * @param {object} settings dataTables settings object
++ * @memberof DataTable#oApi
++ */
++ function _fnScrollDraw ( settings )
++ {
++ // Given that this is such a monster function, a lot of variables are use
++ // to try and keep the minimised size as small as possible
++ var
++ scroll = settings.oScroll,
++ scrollX = scroll.sX,
++ scrollXInner = scroll.sXInner,
++ scrollY = scroll.sY,
++ barWidth = scroll.iBarWidth,
++ divHeader = $(settings.nScrollHead),
++ divHeaderStyle = divHeader[0].style,
++ divHeaderInner = divHeader.children('div'),
++ divHeaderInnerStyle = divHeaderInner[0].style,
++ divHeaderTable = divHeaderInner.children('table'),
++ divBodyEl = settings.nScrollBody,
++ divBody = $(divBodyEl),
++ divBodyStyle = divBodyEl.style,
++ divFooter = $(settings.nScrollFoot),
++ divFooterInner = divFooter.children('div'),
++ divFooterTable = divFooterInner.children('table'),
++ header = $(settings.nTHead),
++ table = $(settings.nTable),
++ tableEl = table[0],
++ tableStyle = tableEl.style,
++ footer = settings.nTFoot ? $(settings.nTFoot) : null,
++ browser = settings.oBrowser,
++ ie67 = browser.bScrollOversize,
++ headerTrgEls, footerTrgEls,
++ headerSrcEls, footerSrcEls,
++ headerCopy, footerCopy,
++ headerWidths=[], footerWidths=[],
++ headerContent=[],
++ idx, correction, sanityWidth,
++ zeroOut = function(nSizer) {
++ var style = nSizer.style;
++ style.paddingTop = "0";
++ style.paddingBottom = "0";
++ style.borderTopWidth = "0";
++ style.borderBottomWidth = "0";
++ style.height = 0;
++ };
++
++ /*
++ * 1. Re-create the table inside the scrolling div
++ */
++
++ // Remove the old minimised thead and tfoot elements in the inner table
++ table.children('thead, tfoot').remove();
++
++ // Clone the current header and footer elements and then place it into the inner table
++ headerCopy = header.clone().prependTo( table );
++ headerTrgEls = header.find('tr'); // original header is in its own table
++ headerSrcEls = headerCopy.find('tr');
++ headerCopy.find('th, td').removeAttr('tabindex');
++
++ if ( footer ) {
++ footerCopy = footer.clone().prependTo( table );
++ footerTrgEls = footer.find('tr'); // the original tfoot is in its own table and must be sized
++ footerSrcEls = footerCopy.find('tr');
++ }
++
++
++ /*
++ * 2. Take live measurements from the DOM - do not alter the DOM itself!
++ */
++
++ // Remove old sizing and apply the calculated column widths
++ // Get the unique column headers in the newly created (cloned) header. We want to apply the
++ // calculated sizes to this header
++ if ( ! scrollX )
++ {
++ divBodyStyle.width = '100%';
++ divHeader[0].style.width = '100%';
++ }
++
++ $.each( _fnGetUniqueThs( settings, headerCopy ), function ( i, el ) {
++ idx = _fnVisibleToColumnIndex( settings, i );
++ el.style.width = settings.aoColumns[idx].sWidth;
++ } );
++
++ if ( footer ) {
++ _fnApplyToChildren( function(n) {
++ n.style.width = "";
++ }, footerSrcEls );
++ }
++
++ // If scroll collapse is enabled, when we put the headers back into the body for sizing, we
++ // will end up forcing the scrollbar to appear, making our measurements wrong for when we
++ // then hide it (end of this function), so add the header height to the body scroller.
++ if ( scroll.bCollapse && scrollY !== "" ) {
++ divBodyStyle.height = (divBody[0].offsetHeight + header[0].offsetHeight)+"px";
++ }
++
++ // Size the table as a whole
++ sanityWidth = table.outerWidth();
++ if ( scrollX === "" ) {
++ // No x scrolling
++ tableStyle.width = "100%";
++
++ // IE7 will make the width of the table when 100% include the scrollbar
++ // - which is shouldn't. When there is a scrollbar we need to take this
++ // into account.
++ if ( ie67 && (table.find('tbody').height() > divBodyEl.offsetHeight ||
++ divBody.css('overflow-y') == "scroll")
++ ) {
++ tableStyle.width = _fnStringToCss( table.outerWidth() - barWidth);
++ }
++ }
++ else
++ {
++ // x scrolling
++ if ( scrollXInner !== "" ) {
++ // x scroll inner has been given - use it
++ tableStyle.width = _fnStringToCss(scrollXInner);
++ }
++ else if ( sanityWidth == divBody.width() && divBody.height() < table.height() ) {
++ // There is y-scrolling - try to take account of the y scroll bar
++ tableStyle.width = _fnStringToCss( sanityWidth-barWidth );
++ if ( table.outerWidth() > sanityWidth-barWidth ) {
++ // Not possible to take account of it
++ tableStyle.width = _fnStringToCss( sanityWidth );
++ }
++ }
++ else {
++ // When all else fails
++ tableStyle.width = _fnStringToCss( sanityWidth );
++ }
++ }
++
++ // Recalculate the sanity width - now that we've applied the required width,
++ // before it was a temporary variable. This is required because the column
++ // width calculation is done before this table DOM is created.
++ sanityWidth = table.outerWidth();
++
++ // Hidden header should have zero height, so remove padding and borders. Then
++ // set the width based on the real headers
++
++ // Apply all styles in one pass
++ _fnApplyToChildren( zeroOut, headerSrcEls );
++
++ // Read all widths in next pass
++ _fnApplyToChildren( function(nSizer) {
++ headerContent.push( nSizer.innerHTML );
++ headerWidths.push( _fnStringToCss( $(nSizer).css('width') ) );
++ }, headerSrcEls );
++
++ // Apply all widths in final pass
++ _fnApplyToChildren( function(nToSize, i) {
++ nToSize.style.width = headerWidths[i];
++ }, headerTrgEls );
++
++ $(headerSrcEls).height(0);
++
++ /* Same again with the footer if we have one */
++ if ( footer )
++ {
++ _fnApplyToChildren( zeroOut, footerSrcEls );
++
++ _fnApplyToChildren( function(nSizer) {
++ footerWidths.push( _fnStringToCss( $(nSizer).css('width') ) );
++ }, footerSrcEls );
++
++ _fnApplyToChildren( function(nToSize, i) {
++ nToSize.style.width = footerWidths[i];
++ }, footerTrgEls );
++
++ $(footerSrcEls).height(0);
++ }
++
++
++ /*
++ * 3. Apply the measurements
++ */
++
++ // "Hide" the header and footer that we used for the sizing. We need to keep
++ // the content of the cell so that the width applied to the header and body
++ // both match, but we want to hide it completely. We want to also fix their
++ // width to what they currently are
++ _fnApplyToChildren( function(nSizer, i) {
++ nSizer.innerHTML = '<div class="dataTables_sizing" style="height:0;overflow:hidden;">'+headerContent[i]+'</div>';
++ nSizer.style.width = headerWidths[i];
++ }, headerSrcEls );
++
++ if ( footer )
++ {
++ _fnApplyToChildren( function(nSizer, i) {
++ nSizer.innerHTML = "";
++ nSizer.style.width = footerWidths[i];
++ }, footerSrcEls );
++ }
++
++ // Sanity check that the table is of a sensible width. If not then we are going to get
++ // misalignment - try to prevent this by not allowing the table to shrink below its min width
++ if ( table.outerWidth() < sanityWidth )
++ {
++ // The min width depends upon if we have a vertical scrollbar visible or not */
++ correction = ((divBodyEl.scrollHeight > divBodyEl.offsetHeight ||
++ divBody.css('overflow-y') == "scroll")) ?
++ sanityWidth+barWidth :
++ sanityWidth;
++
++ // IE6/7 are a law unto themselves...
++ if ( ie67 && (divBodyEl.scrollHeight >
++ divBodyEl.offsetHeight || divBody.css('overflow-y') == "scroll")
++ ) {
++ tableStyle.width = _fnStringToCss( correction-barWidth );
++ }
++
++ // And give the user a warning that we've stopped the table getting too small
++ if ( scrollX === "" || scrollXInner !== "" ) {
++ _fnLog( settings, 1, 'Possible column misalignment', 6 );
++ }
++ }
++ else
++ {
++ correction = '100%';
++ }
++
++ // Apply to the container elements
++ divBodyStyle.width = _fnStringToCss( correction );
++ divHeaderStyle.width = _fnStringToCss( correction );
++
++ if ( footer ) {
++ settings.nScrollFoot.style.width = _fnStringToCss( correction );
++ }
++
++
++ /*
++ * 4. Clean up
++ */
++ if ( ! scrollY ) {
++ /* IE7< puts a vertical scrollbar in place (when it shouldn't be) due to subtracting
++ * the scrollbar height from the visible display, rather than adding it on. We need to
++ * set the height in order to sort this. Don't want to do it in any other browsers.
++ */
++ if ( ie67 ) {
++ divBodyStyle.height = _fnStringToCss( tableEl.offsetHeight+barWidth );
++ }
++ }
++
++ if ( scrollY && scroll.bCollapse ) {
++ divBodyStyle.height = _fnStringToCss( scrollY );
++
++ var iExtra = (scrollX && tableEl.offsetWidth > divBodyEl.offsetWidth) ?
++ barWidth :
++ 0;
++
++ if ( tableEl.offsetHeight < divBodyEl.offsetHeight ) {
++ divBodyStyle.height = _fnStringToCss( tableEl.offsetHeight+iExtra );
++ }
++ }
++
++ /* Finally set the width's of the header and footer tables */
++ var iOuterWidth = table.outerWidth();
++ divHeaderTable[0].style.width = _fnStringToCss( iOuterWidth );
++ divHeaderInnerStyle.width = _fnStringToCss( iOuterWidth );
++
++ // Figure out if there are scrollbar present - if so then we need a the header and footer to
++ // provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar)
++ var bScrolling = table.height() > divBodyEl.clientHeight || divBody.css('overflow-y') == "scroll";
++ var padding = 'padding' + (browser.bScrollbarLeft ? 'Left' : 'Right' );
++ divHeaderInnerStyle[ padding ] = bScrolling ? barWidth+"px" : "0px";
++
++ if ( footer ) {
++ divFooterTable[0].style.width = _fnStringToCss( iOuterWidth );
++ divFooterInner[0].style.width = _fnStringToCss( iOuterWidth );
++ divFooterInner[0].style[padding] = bScrolling ? barWidth+"px" : "0px";
++ }
++
++ /* Adjust the position of the header in case we loose the y-scrollbar */
++ divBody.scroll();
++
++ // If sorting or filtering has occurred, jump the scrolling back to the top
++ // only if we aren't holding the position
++ if ( (settings.bSorted || settings.bFiltered) && ! settings._drawHold ) {
++ divBodyEl.scrollTop = 0;
++ }
++ }
++
++
++
++ /**
++ * Apply a given function to the display child nodes of an element array (typically
++ * TD children of TR rows
++ * @param {function} fn Method to apply to the objects
++ * @param array {nodes} an1 List of elements to look through for display children
++ * @param array {nodes} an2 Another list (identical structure to the first) - optional
++ * @memberof DataTable#oApi
++ */
++ function _fnApplyToChildren( fn, an1, an2 )
++ {
++ var index=0, i=0, iLen=an1.length;
++ var nNode1, nNode2;
++
++ while ( i < iLen ) {
++ nNode1 = an1[i].firstChild;
++ nNode2 = an2 ? an2[i].firstChild : null;
++
++ while ( nNode1 ) {
++ if ( nNode1.nodeType === 1 ) {
++ if ( an2 ) {
++ fn( nNode1, nNode2, index );
++ }
++ else {
++ fn( nNode1, index );
++ }
++
++ index++;
++ }
++
++ nNode1 = nNode1.nextSibling;
++ nNode2 = an2 ? nNode2.nextSibling : null;
++ }
++
++ i++;
++ }
++ }
++
++
++
++ var __re_html_remove = /<.*?>/g;
++
++
++ /**
++ * Calculate the width of columns for the table
++ * @param {object} oSettings dataTables settings object
++ * @memberof DataTable#oApi
++ */
++ function _fnCalculateColumnWidths ( oSettings )
++ {
++ var
++ table = oSettings.nTable,
++ columns = oSettings.aoColumns,
++ scroll = oSettings.oScroll,
++ scrollY = scroll.sY,
++ scrollX = scroll.sX,
++ scrollXInner = scroll.sXInner,
++ columnCount = columns.length,
++ visibleColumns = _fnGetColumns( oSettings, 'bVisible' ),
++ headerCells = $('th', oSettings.nTHead),
++ tableWidthAttr = table.getAttribute('width'), // from DOM element
++ tableContainer = table.parentNode,
++ userInputs = false,
++ i, column, columnIdx, width, outerWidth;
++
++ var styleWidth = table.style.width;
++ if ( styleWidth && styleWidth.indexOf('%') !== -1 ) {
++ tableWidthAttr = styleWidth;
++ }
++
++ /* Convert any user input sizes into pixel sizes */
++ for ( i=0 ; i<visibleColumns.length ; i++ ) {
++ column = columns[ visibleColumns[i] ];
++
++ if ( column.sWidth !== null ) {
++ column.sWidth = _fnConvertToWidth( column.sWidthOrig, tableContainer );
++
++ userInputs = true;
++ }
++ }
++
++ /* If the number of columns in the DOM equals the number that we have to
++ * process in DataTables, then we can use the offsets that are created by
++ * the web- browser. No custom sizes can be set in order for this to happen,
++ * nor scrolling used
++ */
++ if ( ! userInputs && ! scrollX && ! scrollY &&
++ columnCount == _fnVisbleColumns( oSettings ) &&
++ columnCount == headerCells.length
++ ) {
++ for ( i=0 ; i<columnCount ; i++ ) {
++ columns[i].sWidth = _fnStringToCss( headerCells.eq(i).width() );
++ }
++ }
++ else
++ {
++ // Otherwise construct a single row, worst case, table with the widest
++ // node in the data, assign any user defined widths, then insert it into
++ // the DOM and allow the browser to do all the hard work of calculating
++ // table widths
++ var tmpTable = $(table).clone() // don't use cloneNode - IE8 will remove events on the main table
++ .css( 'visibility', 'hidden' )
++ .removeAttr( 'id' );
++
++ // Clean up the table body
++ tmpTable.find('tbody tr').remove();
++ var tr = $('<tr/>').appendTo( tmpTable.find('tbody') );
++
++ // Remove any assigned widths from the footer (from scrolling)
++ tmpTable.find('tfoot th, tfoot td').css('width', '');
++
++ // Apply custom sizing to the cloned header
++ headerCells = _fnGetUniqueThs( oSettings, tmpTable.find('thead')[0] );
++
++ for ( i=0 ; i<visibleColumns.length ; i++ ) {
++ column = columns[ visibleColumns[i] ];
++
++ headerCells[i].style.width = column.sWidthOrig !== null && column.sWidthOrig !== '' ?
++ _fnStringToCss( column.sWidthOrig ) :
++ '';
++ }
++
++ // Find the widest cell for each column and put it into the table
++ if ( oSettings.aoData.length ) {
++ for ( i=0 ; i<visibleColumns.length ; i++ ) {
++ columnIdx = visibleColumns[i];
++ column = columns[ columnIdx ];
++
++ $( _fnGetWidestNode( oSettings, columnIdx ) )
++ .clone( false )
++ .append( column.sContentPadding )
++ .appendTo( tr );
++ }
++ }
++
++ // Table has been built, attach to the document so we can work with it
++ tmpTable.appendTo( tableContainer );
++
++ // When scrolling (X or Y) we want to set the width of the table as
++ // appropriate. However, when not scrolling leave the table width as it
++ // is. This results in slightly different, but I think correct behaviour
++ if ( scrollX && scrollXInner ) {
++ tmpTable.width( scrollXInner );
++ }
++ else if ( scrollX ) {
++ tmpTable.css( 'width', 'auto' );
++
++ if ( tmpTable.width() < tableContainer.offsetWidth ) {
++ tmpTable.width( tableContainer.offsetWidth );
++ }
++ }
++ else if ( scrollY ) {
++ tmpTable.width( tableContainer.offsetWidth );
++ }
++ else if ( tableWidthAttr ) {
++ tmpTable.width( tableWidthAttr );
++ }
++
++ // Take into account the y scrollbar
++ _fnScrollingWidthAdjust( oSettings, tmpTable[0] );
++
++ // Browsers need a bit of a hand when a width is assigned to any columns
++ // when x-scrolling as they tend to collapse the table to the min-width,
++ // even if we sent the column widths. So we need to keep track of what
++ // the table width should be by summing the user given values, and the
++ // automatic values
++ if ( scrollX )
++ {
++ var total = 0;
++
++ for ( i=0 ; i<visibleColumns.length ; i++ ) {
++ column = columns[ visibleColumns[i] ];
++ outerWidth = $(headerCells[i]).outerWidth();
++
++ total += column.sWidthOrig === null ?
++ outerWidth :
++ parseInt( column.sWidth, 10 ) + outerWidth - $(headerCells[i]).width();
++ }
++
++ tmpTable.width( _fnStringToCss( total ) );
++ table.style.width = _fnStringToCss( total );
++ }
++
++ // Get the width of each column in the constructed table
++ for ( i=0 ; i<visibleColumns.length ; i++ ) {
++ column = columns[ visibleColumns[i] ];
++ width = $(headerCells[i]).width();
++
++ if ( width ) {
++ column.sWidth = _fnStringToCss( width );
++ }
++ }
++
++ table.style.width = _fnStringToCss( tmpTable.css('width') );
++
++ // Finished with the table - ditch it
++ tmpTable.remove();
++ }
++
++ // If there is a width attr, we want to attach an event listener which
++ // allows the table sizing to automatically adjust when the window is
++ // resized. Use the width attr rather than CSS, since we can't know if the
++ // CSS is a relative value or absolute - DOM read is always px.
++ if ( tableWidthAttr ) {
++ table.style.width = _fnStringToCss( tableWidthAttr );
++ }
++
++ if ( (tableWidthAttr || scrollX) && ! oSettings._reszEvt ) {
++ var bindResize = function () {
++ $(window).bind('resize.DT-'+oSettings.sInstance, _fnThrottle( function () {
++ _fnAdjustColumnSizing( oSettings );
++ } ) );
++ };
++
++ // IE6/7 will crash if we bind a resize event handler on page load.
++ // To be removed in 1.11 which drops IE6/7 support
++ if ( oSettings.oBrowser.bScrollOversize ) {
++ setTimeout( bindResize, 1000 );
++ }
++ else {
++ bindResize();
++ }
++
++ oSettings._reszEvt = true;
++ }
++ }
++
++
++ /**
++ * Throttle the calls to a function. Arguments and context are maintained for
++ * the throttled function
++ * @param {function} fn Function to be called
++ * @param {int} [freq=200] call frequency in mS
++ * @returns {function} wrapped function
++ * @memberof DataTable#oApi
++ */
++ function _fnThrottle( fn, freq ) {
++ var
++ frequency = freq !== undefined ? freq : 200,
++ last,
++ timer;
++
++ return function () {
++ var
++ that = this,
++ now = +new Date(),
++ args = arguments;
++
++ if ( last && now < last + frequency ) {
++ clearTimeout( timer );
++
++ timer = setTimeout( function () {
++ last = undefined;
++ fn.apply( that, args );
++ }, frequency );
++ }
++ else {
++ last = now;
++ fn.apply( that, args );
++ }
++ };
++ }
++
++
++ /**
++ * Convert a CSS unit width to pixels (e.g. 2em)
++ * @param {string} width width to be converted
++ * @param {node} parent parent to get the with for (required for relative widths) - optional
++ * @returns {int} width in pixels
++ * @memberof DataTable#oApi
++ */
++ function _fnConvertToWidth ( width, parent )
++ {
++ if ( ! width ) {
++ return 0;
++ }
++
++ var n = $('<div/>')
++ .css( 'width', _fnStringToCss( width ) )
++ .appendTo( parent || document.body );
++
++ var val = n[0].offsetWidth;
++ n.remove();
++
++ return val;
++ }
++
++
++ /**
++ * Adjust a table's width to take account of vertical scroll bar
++ * @param {object} oSettings dataTables settings object
++ * @param {node} n table node
++ * @memberof DataTable#oApi
++ */
++
++ function _fnScrollingWidthAdjust ( settings, n )
++ {
++ var scroll = settings.oScroll;
++
++ if ( scroll.sX || scroll.sY ) {
++ // When y-scrolling only, we want to remove the width of the scroll bar
++ // so the table + scroll bar will fit into the area available, otherwise
++ // we fix the table at its current size with no adjustment
++ var correction = ! scroll.sX ? scroll.iBarWidth : 0;
++ n.style.width = _fnStringToCss( $(n).outerWidth() - correction );
++ }
++ }
++
++
++ /**
++ * Get the widest node
++ * @param {object} settings dataTables settings object
++ * @param {int} colIdx column of interest
++ * @returns {node} widest table node
++ * @memberof DataTable#oApi
++ */
++ function _fnGetWidestNode( settings, colIdx )
++ {
++ var idx = _fnGetMaxLenString( settings, colIdx );
++ if ( idx < 0 ) {
++ return null;
++ }
++
++ var data = settings.aoData[ idx ];
++ return ! data.nTr ? // Might not have been created when deferred rendering
++ $('<td/>').html( _fnGetCellData( settings, idx, colIdx, 'display' ) )[0] :
++ data.anCells[ colIdx ];
++ }
++
++
++ /**
++ * Get the maximum strlen for each data column
++ * @param {object} settings dataTables settings object
++ * @param {int} colIdx column of interest
++ * @returns {string} max string length for each column
++ * @memberof DataTable#oApi
++ */
++ function _fnGetMaxLenString( settings, colIdx )
++ {
++ var s, max=-1, maxIdx = -1;
++
++ for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
++ s = _fnGetCellData( settings, i, colIdx, 'display' )+'';
++ s = s.replace( __re_html_remove, '' );
++
++ if ( s.length > max ) {
++ max = s.length;
++ maxIdx = i;
++ }
++ }
++
++ return maxIdx;
++ }
++
++
++ /**
++ * Append a CSS unit (only if required) to a string
++ * @param {string} value to css-ify
++ * @returns {string} value with css unit
++ * @memberof DataTable#oApi
++ */
++ function _fnStringToCss( s )
++ {
++ if ( s === null ) {
++ return '0px';
++ }
++
++ if ( typeof s == 'number' ) {
++ return s < 0 ?
++ '0px' :
++ s+'px';
++ }
++
++ // Check it has a unit character already
++ return s.match(/\d$/) ?
++ s+'px' :
++ s;
++ }
++
++
++ /**
++ * Get the width of a scroll bar in this browser being used
++ * @returns {int} width in pixels
++ * @memberof DataTable#oApi
++ */
++ function _fnScrollBarWidth ()
++ {
++ // On first run a static variable is set, since this is only needed once.
++ // Subsequent runs will just use the previously calculated value
++ var width = DataTable.__scrollbarWidth;
++
++ if ( width === undefined ) {
++ var sizer = $('<p/>').css( {
++ position: 'absolute',
++ top: 0,
++ left: 0,
++ width: '100%',
++ height: 150,
++ padding: 0,
++ overflow: 'scroll',
++ visibility: 'hidden'
++ } )
++ .appendTo('body');
++
++ width = sizer[0].offsetWidth - sizer[0].clientWidth;
++ DataTable.__scrollbarWidth = width;
++
++ sizer.remove();
++ }
++
++ return width;
++ }
++
++
++
++ function _fnSortFlatten ( settings )
++ {
++ var
++ i, iLen, k, kLen,
++ aSort = [],
++ aiOrig = [],
++ aoColumns = settings.aoColumns,
++ aDataSort, iCol, sType, srcCol,
++ fixed = settings.aaSortingFixed,
++ fixedObj = $.isPlainObject( fixed ),
++ nestedSort = [],
++ add = function ( a ) {
++ if ( a.length && ! $.isArray( a[0] ) ) {
++ // 1D array
++ nestedSort.push( a );
++ }
++ else {
++ // 2D array
++ nestedSort.push.apply( nestedSort, a );
++ }
++ };
++
++ // Build the sort array, with pre-fix and post-fix options if they have been
++ // specified
++ if ( $.isArray( fixed ) ) {
++ add( fixed );
++ }
++
++ if ( fixedObj && fixed.pre ) {
++ add( fixed.pre );
++ }
++
++ add( settings.aaSorting );
++
++ if (fixedObj && fixed.post ) {
++ add( fixed.post );
++ }
++
++ for ( i=0 ; i<nestedSort.length ; i++ )
++ {
++ srcCol = nestedSort[i][0];
++ aDataSort = aoColumns[ srcCol ].aDataSort;
++
++ for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ )
++ {
++ iCol = aDataSort[k];
++ sType = aoColumns[ iCol ].sType || 'string';
++
++ if ( nestedSort[i]._idx === undefined ) {
++ nestedSort[i]._idx = $.inArray( nestedSort[i][1], aoColumns[iCol].asSorting );
++ }
++
++ aSort.push( {
++ src: srcCol,
++ col: iCol,
++ dir: nestedSort[i][1],
++ index: nestedSort[i]._idx,
++ type: sType,
++ formatter: DataTable.ext.type.order[ sType+"-pre" ]
++ } );
++ }
++ }
++
++ return aSort;
++ }
++
++ /**
++ * Change the order of the table
++ * @param {object} oSettings dataTables settings object
++ * @memberof DataTable#oApi
++ * @todo This really needs split up!
++ */
++ function _fnSort ( oSettings )
++ {
++ var
++ i, ien, iLen, j, jLen, k, kLen,
++ sDataType, nTh,
++ aiOrig = [],
++ oExtSort = DataTable.ext.type.order,
++ aoData = oSettings.aoData,
++ aoColumns = oSettings.aoColumns,
++ aDataSort, data, iCol, sType, oSort,
++ formatters = 0,
++ sortCol,
++ displayMaster = oSettings.aiDisplayMaster,
++ aSort;
++
++ // Resolve any column types that are unknown due to addition or invalidation
++ // @todo Can this be moved into a 'data-ready' handler which is called when
++ // data is going to be used in the table?
++ _fnColumnTypes( oSettings );
++
++ aSort = _fnSortFlatten( oSettings );
++
++ for ( i=0, ien=aSort.length ; i<ien ; i++ ) {
++ sortCol = aSort[i];
++
++ // Track if we can use the fast sort algorithm
++ if ( sortCol.formatter ) {
++ formatters++;
++ }
++
++ // Load the data needed for the sort, for each cell
++ _fnSortData( oSettings, sortCol.col );
++ }
++
++ /* No sorting required if server-side or no sorting array */
++ if ( _fnDataSource( oSettings ) != 'ssp' && aSort.length !== 0 )
++ {
++ // Create a value - key array of the current row positions such that we can use their
++ // current position during the sort, if values match, in order to perform stable sorting
++ for ( i=0, iLen=displayMaster.length ; i<iLen ; i++ ) {
++ aiOrig[ displayMaster[i] ] = i;
++ }
++
++ /* Do the sort - here we want multi-column sorting based on a given data source (column)
++ * and sorting function (from oSort) in a certain direction. It's reasonably complex to
++ * follow on it's own, but this is what we want (example two column sorting):
++ * fnLocalSorting = function(a,b){
++ * var iTest;
++ * iTest = oSort['string-asc']('data11', 'data12');
++ * if (iTest !== 0)
++ * return iTest;
++ * iTest = oSort['numeric-desc']('data21', 'data22');
++ * if (iTest !== 0)
++ * return iTest;
++ * return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
++ * }
++ * Basically we have a test for each sorting column, if the data in that column is equal,
++ * test the next column. If all columns match, then we use a numeric sort on the row
++ * positions in the original data array to provide a stable sort.
++ *
++ * Note - I know it seems excessive to have two sorting methods, but the first is around
++ * 15% faster, so the second is only maintained for backwards compatibility with sorting
++ * methods which do not have a pre-sort formatting function.
++ */
++ if ( formatters === aSort.length ) {
++ // All sort types have formatting functions
++ displayMaster.sort( function ( a, b ) {
++ var
++ x, y, k, test, sort,
++ len=aSort.length,
++ dataA = aoData[a]._aSortData,
++ dataB = aoData[b]._aSortData;
++
++ for ( k=0 ; k<len ; k++ ) {
++ sort = aSort[k];
++
++ x = dataA[ sort.col ];
++ y = dataB[ sort.col ];
++
++ test = x<y ? -1 : x>y ? 1 : 0;
++ if ( test !== 0 ) {
++ return sort.dir === 'asc' ? test : -test;
++ }
++ }
++
++ x = aiOrig[a];
++ y = aiOrig[b];
++ return x<y ? -1 : x>y ? 1 : 0;
++ } );
++ }
++ else {
++ // Depreciated - remove in 1.11 (providing a plug-in option)
++ // Not all sort types have formatting methods, so we have to call their sorting
++ // methods.
++ displayMaster.sort( function ( a, b ) {
++ var
++ x, y, k, l, test, sort, fn,
++ len=aSort.length,
++ dataA = aoData[a]._aSortData,
++ dataB = aoData[b]._aSortData;
++
++ for ( k=0 ; k<len ; k++ ) {
++ sort = aSort[k];
++
++ x = dataA[ sort.col ];
++ y = dataB[ sort.col ];
++
++ fn = oExtSort[ sort.type+"-"+sort.dir ] || oExtSort[ "string-"+sort.dir ];
++ test = fn( x, y );
++ if ( test !== 0 ) {
++ return test;
++ }
++ }
++
++ x = aiOrig[a];
++ y = aiOrig[b];
++ return x<y ? -1 : x>y ? 1 : 0;
++ } );
++ }
++ }
++
++ /* Tell the draw function that we have sorted the data */
++ oSettings.bSorted = true;
++ }
++
++
++ function _fnSortAria ( settings )
++ {
++ var label;
++ var nextSort;
++ var columns = settings.aoColumns;
++ var aSort = _fnSortFlatten( settings );
++ var oAria = settings.oLanguage.oAria;
++
++ // ARIA attributes - need to loop all columns, to update all (removing old
++ // attributes as needed)
++ for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
++ {
++ var col = columns[i];
++ var asSorting = col.asSorting;
++ var sTitle = col.sTitle.replace( /<.*?>/g, "" );
++ var th = col.nTh;
++
++ // IE7 is throwing an error when setting these properties with jQuery's
++ // attr() and removeAttr() methods...
++ th.removeAttribute('aria-sort');
++
++ /* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */
++ if ( col.bSortable ) {
++ if ( aSort.length > 0 && aSort[0].col == i ) {
++ th.setAttribute('aria-sort', aSort[0].dir=="asc" ? "ascending" : "descending" );
++ nextSort = asSorting[ aSort[0].index+1 ] || asSorting[0];
++ }
++ else {
++ nextSort = asSorting[0];
++ }
++
++ label = sTitle + ( nextSort === "asc" ?
++ oAria.sSortAscending :
++ oAria.sSortDescending
++ );
++ }
++ else {
++ label = sTitle;
++ }
++
++ th.setAttribute('aria-label', label);
++ }
++ }
++
++
++ /**
++ * Function to run on user sort request
++ * @param {object} settings dataTables settings object
++ * @param {node} attachTo node to attach the handler to
++ * @param {int} colIdx column sorting index
++ * @param {boolean} [append=false] Append the requested sort to the existing
++ * sort if true (i.e. multi-column sort)
++ * @param {function} [callback] callback function
++ * @memberof DataTable#oApi
++ */
++ function _fnSortListener ( settings, colIdx, append, callback )
++ {
++ var col = settings.aoColumns[ colIdx ];
++ var sorting = settings.aaSorting;
++ var asSorting = col.asSorting;
++ var nextSortIdx;
++ var next = function ( a, overflow ) {
++ var idx = a._idx;
++ if ( idx === undefined ) {
++ idx = $.inArray( a[1], asSorting );
++ }
++
++ return idx+1 < asSorting.length ?
++ idx+1 :
++ overflow ?
++ null :
++ 0;
++ };
++
++ // Convert to 2D array if needed
++ if ( typeof sorting[0] === 'number' ) {
++ sorting = settings.aaSorting = [ sorting ];
++ }
++
++ // If appending the sort then we are multi-column sorting
++ if ( append && settings.oFeatures.bSortMulti ) {
++ // Are we already doing some kind of sort on this column?
++ var sortIdx = $.inArray( colIdx, _pluck(sorting, '0') );
++
++ if ( sortIdx !== -1 ) {
++ // Yes, modify the sort
++ nextSortIdx = next( sorting[sortIdx], true );
++
++ if ( nextSortIdx === null && sorting.length === 1 ) {
++ nextSortIdx = 0; // can't remove sorting completely
++ }
++
++ if ( nextSortIdx === null ) {
++ sorting.splice( sortIdx, 1 );
++ }
++ else {
++ sorting[sortIdx][1] = asSorting[ nextSortIdx ];
++ sorting[sortIdx]._idx = nextSortIdx;
++ }
++ }
++ else {
++ // No sort on this column yet
++ sorting.push( [ colIdx, asSorting[0], 0 ] );
++ sorting[sorting.length-1]._idx = 0;
++ }
++ }
++ else if ( sorting.length && sorting[0][0] == colIdx ) {
++ // Single column - already sorting on this column, modify the sort
++ nextSortIdx = next( sorting[0] );
++
++ sorting.length = 1;
++ sorting[0][1] = asSorting[ nextSortIdx ];
++ sorting[0]._idx = nextSortIdx;
++ }
++ else {
++ // Single column - sort only on this column
++ sorting.length = 0;
++ sorting.push( [ colIdx, asSorting[0] ] );
++ sorting[0]._idx = 0;
++ }
++
++ // Run the sort by calling a full redraw
++ _fnReDraw( settings );
++
++ // callback used for async user interaction
++ if ( typeof callback == 'function' ) {
++ callback( settings );
++ }
++ }
++
++
++ /**
++ * Attach a sort handler (click) to a node
++ * @param {object} settings dataTables settings object
++ * @param {node} attachTo node to attach the handler to
++ * @param {int} colIdx column sorting index
++ * @param {function} [callback] callback function
++ * @memberof DataTable#oApi
++ */
++ function _fnSortAttachListener ( settings, attachTo, colIdx, callback )
++ {
++ var col = settings.aoColumns[ colIdx ];
++
++ _fnBindAction( attachTo, {}, function (e) {
++ /* If the column is not sortable - don't to anything */
++ if ( col.bSortable === false ) {
++ return;
++ }
++
++ // If processing is enabled use a timeout to allow the processing
++ // display to be shown - otherwise to it synchronously
++ if ( settings.oFeatures.bProcessing ) {
++ _fnProcessingDisplay( settings, true );
++
++ setTimeout( function() {
++ _fnSortListener( settings, colIdx, e.shiftKey, callback );
++
++ // In server-side processing, the draw callback will remove the
++ // processing display
++ if ( _fnDataSource( settings ) !== 'ssp' ) {
++ _fnProcessingDisplay( settings, false );
++ }
++ }, 0 );
++ }
++ else {
++ _fnSortListener( settings, colIdx, e.shiftKey, callback );
++ }
++ } );
++ }
++
++
++ /**
++ * Set the sorting classes on table's body, Note: it is safe to call this function
++ * when bSort and bSortClasses are false
++ * @param {object} oSettings dataTables settings object
++ * @memberof DataTable#oApi
++ */
++ function _fnSortingClasses( settings )
++ {
++ var oldSort = settings.aLastSort;
++ var sortClass = settings.oClasses.sSortColumn;
++ var sort = _fnSortFlatten( settings );
++ var features = settings.oFeatures;
++ var i, ien, colIdx;
++
++ if ( features.bSort && features.bSortClasses ) {
++ // Remove old sorting classes
++ for ( i=0, ien=oldSort.length ; i<ien ; i++ ) {
++ colIdx = oldSort[i].src;
++
++ // Remove column sorting
++ $( _pluck( settings.aoData, 'anCells', colIdx ) )
++ .removeClass( sortClass + (i<2 ? i+1 : 3) );
++ }
++
++ // Add new column sorting
++ for ( i=0, ien=sort.length ; i<ien ; i++ ) {
++ colIdx = sort[i].src;
++
++ $( _pluck( settings.aoData, 'anCells', colIdx ) )
++ .addClass( sortClass + (i<2 ? i+1 : 3) );
++ }
++ }
++
++ settings.aLastSort = sort;
++ }
++
++
++ // Get the data to sort a column, be it from cache, fresh (populating the
++ // cache), or from a sort formatter
++ function _fnSortData( settings, idx )
++ {
++ // Custom sorting function - provided by the sort data type
++ var column = settings.aoColumns[ idx ];
++ var customSort = DataTable.ext.order[ column.sSortDataType ];
++ var customData;
++
++ if ( customSort ) {
++ customData = customSort.call( settings.oInstance, settings, idx,
++ _fnColumnIndexToVisible( settings, idx )
++ );
++ }
++
++ // Use / populate cache
++ var row, cellData;
++ var formatter = DataTable.ext.type.order[ column.sType+"-pre" ];
++
++ for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
++ row = settings.aoData[i];
++
++ if ( ! row._aSortData ) {
++ row._aSortData = [];
++ }
++
++ if ( ! row._aSortData[idx] || customSort ) {
++ cellData = customSort ?
++ customData[i] : // If there was a custom sort function, use data from there
++ _fnGetCellData( settings, i, idx, 'sort' );
++
++ row._aSortData[ idx ] = formatter ?
++ formatter( cellData ) :
++ cellData;
++ }
++ }
++ }
++
++
++
++ /**
++ * Save the state of a table
++ * @param {object} oSettings dataTables settings object
++ * @memberof DataTable#oApi
++ */
++ function _fnSaveState ( settings )
++ {
++ if ( !settings.oFeatures.bStateSave || settings.bDestroying )
++ {
++ return;
++ }
++
++ /* Store the interesting variables */
++ var state = {
++ time: +new Date(),
++ start: settings._iDisplayStart,
++ length: settings._iDisplayLength,
++ order: $.extend( true, [], settings.aaSorting ),
++ search: _fnSearchToCamel( settings.oPreviousSearch ),
++ columns: $.map( settings.aoColumns, function ( col, i ) {
++ return {
++ visible: col.bVisible,
++ search: _fnSearchToCamel( settings.aoPreSearchCols[i] )
++ };
++ } )
++ };
++
++ _fnCallbackFire( settings, "aoStateSaveParams", 'stateSaveParams', [settings, state] );
++
++ settings.oSavedState = state;
++ settings.fnStateSaveCallback.call( settings.oInstance, settings, state );
++ }
++
++
++ /**
++ * Attempt to load a saved table state
++ * @param {object} oSettings dataTables settings object
++ * @param {object} oInit DataTables init object so we can override settings
++ * @memberof DataTable#oApi
++ */
++ function _fnLoadState ( settings, oInit )
++ {
++ var i, ien;
++ var columns = settings.aoColumns;
++
++ if ( ! settings.oFeatures.bStateSave ) {
++ return;
++ }
++
++ var state = settings.fnStateLoadCallback.call( settings.oInstance, settings );
++ if ( ! state || ! state.time ) {
++ return;
++ }
++
++ /* Allow custom and plug-in manipulation functions to alter the saved data set and
++ * cancelling of loading by returning false
++ */
++ var abStateLoad = _fnCallbackFire( settings, 'aoStateLoadParams', 'stateLoadParams', [settings, state] );
++ if ( $.inArray( false, abStateLoad ) !== -1 ) {
++ return;
++ }
++
++ /* Reject old data */
++ var duration = settings.iStateDuration;
++ if ( duration > 0 && state.time < +new Date() - (duration*1000) ) {
++ return;
++ }
++
++ // Number of columns have changed - all bets are off, no restore of settings
++ if ( columns.length !== state.columns.length ) {
++ return;
++ }
++
++ // Store the saved state so it might be accessed at any time
++ settings.oLoadedState = $.extend( true, {}, state );
++
++ // Restore key features - todo - for 1.11 this needs to be done by
++ // subscribed events
++ if ( state.start !== undefined ) {
++ settings._iDisplayStart = state.start;
++ settings.iInitDisplayStart = state.start;
++ }
++ if ( state.length !== undefined ) {
++ settings._iDisplayLength = state.length;
++ }
++
++ // Order
++ if ( state.order !== undefined ) {
++ settings.aaSorting = [];
++ $.each( state.order, function ( i, col ) {
++ settings.aaSorting.push( col[0] >= columns.length ?
++ [ 0, col[1] ] :
++ col
++ );
++ } );
++ }
++
++ // Search
++ if ( state.search !== undefined ) {
++ $.extend( settings.oPreviousSearch, _fnSearchToHung( state.search ) );
++ }
++
++ // Columns
++ for ( i=0, ien=state.columns.length ; i<ien ; i++ ) {
++ var col = state.columns[i];
++
++ // Visibility
++ if ( col.visible !== undefined ) {
++ columns[i].bVisible = col.visible;
++ }
++
++ // Search
++ if ( col.search !== undefined ) {
++ $.extend( settings.aoPreSearchCols[i], _fnSearchToHung( col.search ) );
++ }
++ }
++
++ _fnCallbackFire( settings, 'aoStateLoaded', 'stateLoaded', [settings, state] );
++ }
++
++
++ /**
++ * Return the settings object for a particular table
++ * @param {node} table table we are using as a dataTable
++ * @returns {object} Settings object - or null if not found
++ * @memberof DataTable#oApi
++ */
++ function _fnSettingsFromNode ( table )
++ {
++ var settings = DataTable.settings;
++ var idx = $.inArray( table, _pluck( settings, 'nTable' ) );
++
++ return idx !== -1 ?
++ settings[ idx ] :
++ null;
++ }
++
++
++ /**
++ * Log an error message
++ * @param {object} settings dataTables settings object
++ * @param {int} level log error messages, or display them to the user
++ * @param {string} msg error message
++ * @param {int} tn Technical note id to get more information about the error.
++ * @memberof DataTable#oApi
++ */
++ function _fnLog( settings, level, msg, tn )
++ {
++ msg = 'DataTables warning: '+
++ (settings!==null ? 'table id='+settings.sTableId+' - ' : '')+msg;
++
++ if ( tn ) {
++ msg += '. For more information about this error, please see '+
++ 'http://datatables.net/tn/'+tn;
++ }
++
++ if ( ! level ) {
++ // Backwards compatibility pre 1.10
++ var ext = DataTable.ext;
++ var type = ext.sErrMode || ext.errMode;
++
++ _fnCallbackFire( settings, null, 'error', [ settings, tn, msg ] );
++
++ if ( type == 'alert' ) {
++ alert( msg );
++ }
++ else if ( type == 'throw' ) {
++ throw new Error(msg);
++ }
++ else if ( typeof type == 'function' ) {
++ type( settings, tn, msg );
++ }
++ }
++ else if ( window.console && console.log ) {
++ console.log( msg );
++ }
++ }
++
++
++ /**
++ * See if a property is defined on one object, if so assign it to the other object
++ * @param {object} ret target object
++ * @param {object} src source object
++ * @param {string} name property
++ * @param {string} [mappedName] name to map too - optional, name used if not given
++ * @memberof DataTable#oApi
++ */
++ function _fnMap( ret, src, name, mappedName )
++ {
++ if ( $.isArray( name ) ) {
++ $.each( name, function (i, val) {
++ if ( $.isArray( val ) ) {
++ _fnMap( ret, src, val[0], val[1] );
++ }
++ else {
++ _fnMap( ret, src, val );
++ }
++ } );
++
++ return;
++ }
++
++ if ( mappedName === undefined ) {
++ mappedName = name;
++ }
++
++ if ( src[name] !== undefined ) {
++ ret[mappedName] = src[name];
++ }
++ }
++
++
++ /**
++ * Extend objects - very similar to jQuery.extend, but deep copy objects, and
++ * shallow copy arrays. The reason we need to do this, is that we don't want to
++ * deep copy array init values (such as aaSorting) since the dev wouldn't be
++ * able to override them, but we do want to deep copy arrays.
++ * @param {object} out Object to extend
++ * @param {object} extender Object from which the properties will be applied to
++ * out
++ * @param {boolean} breakRefs If true, then arrays will be sliced to take an
++ * independent copy with the exception of the `data` or `aaData` parameters
++ * if they are present. This is so you can pass in a collection to
++ * DataTables and have that used as your data source without breaking the
++ * references
++ * @returns {object} out Reference, just for convenience - out === the return.
++ * @memberof DataTable#oApi
++ * @todo This doesn't take account of arrays inside the deep copied objects.
++ */
++ function _fnExtend( out, extender, breakRefs )
++ {
++ var val;
++
++ for ( var prop in extender ) {
++ if ( extender.hasOwnProperty(prop) ) {
++ val = extender[prop];
++
++ if ( $.isPlainObject( val ) ) {
++ if ( ! $.isPlainObject( out[prop] ) ) {
++ out[prop] = {};
++ }
++ $.extend( true, out[prop], val );
++ }
++ else if ( breakRefs && prop !== 'data' && prop !== 'aaData' && $.isArray(val) ) {
++ out[prop] = val.slice();
++ }
++ else {
++ out[prop] = val;
++ }
++ }
++ }
++
++ return out;
++ }
++
++
++ /**
++ * Bind an event handers to allow a click or return key to activate the callback.
++ * This is good for accessibility since a return on the keyboard will have the
++ * same effect as a click, if the element has focus.
++ * @param {element} n Element to bind the action to
++ * @param {object} oData Data object to pass to the triggered function
++ * @param {function} fn Callback function for when the event is triggered
++ * @memberof DataTable#oApi
++ */
++ function _fnBindAction( n, oData, fn )
++ {
++ $(n)
++ .bind( 'click.DT', oData, function (e) {
++ n.blur(); // Remove focus outline for mouse users
++ fn(e);
++ } )
++ .bind( 'keypress.DT', oData, function (e){
++ if ( e.which === 13 ) {
++ e.preventDefault();
++ fn(e);
++ }
++ } )
++ .bind( 'selectstart.DT', function () {
++ /* Take the brutal approach to cancelling text selection */
++ return false;
++ } );
++ }
++
++
++ /**
++ * Register a callback function. Easily allows a callback function to be added to
++ * an array store of callback functions that can then all be called together.
++ * @param {object} oSettings dataTables settings object
++ * @param {string} sStore Name of the array storage for the callbacks in oSettings
++ * @param {function} fn Function to be called back
++ * @param {string} sName Identifying name for the callback (i.e. a label)
++ * @memberof DataTable#oApi
++ */
++ function _fnCallbackReg( oSettings, sStore, fn, sName )
++ {
++ if ( fn )
++ {
++ oSettings[sStore].push( {
++ "fn": fn,
++ "sName": sName
++ } );
++ }
++ }
++
++
++ /**
++ * Fire callback functions and trigger events. Note that the loop over the
++ * callback array store is done backwards! Further note that you do not want to
++ * fire off triggers in time sensitive applications (for example cell creation)
++ * as its slow.
++ * @param {object} settings dataTables settings object
++ * @param {string} callbackArr Name of the array storage for the callbacks in
++ * oSettings
++ * @param {string} eventName Name of the jQuery custom event to trigger. If
++ * null no trigger is fired
++ * @param {array} args Array of arguments to pass to the callback function /
++ * trigger
++ * @memberof DataTable#oApi
++ */
++ function _fnCallbackFire( settings, callbackArr, eventName, args )
++ {
++ var ret = [];
++
++ if ( callbackArr ) {
++ ret = $.map( settings[callbackArr].slice().reverse(), function (val, i) {
++ return val.fn.apply( settings.oInstance, args );
++ } );
++ }
++
++ if ( eventName !== null ) {
++ var e = $.Event( eventName+'.dt' );
++
++ $(settings.nTable).trigger( e, args );
++
++ ret.push( e.result );
++ }
++
++ return ret;
++ }
++
++
++ function _fnLengthOverflow ( settings )
++ {
++ var
++ start = settings._iDisplayStart,
++ end = settings.fnDisplayEnd(),
++ len = settings._iDisplayLength;
++
++ /* If we have space to show extra rows (backing up from the end point - then do so */
++ if ( start >= end )
++ {
++ start = end - len;
++ }
++
++ // Keep the start record on the current page
++ start -= (start % len);
++
++ if ( len === -1 || start < 0 )
++ {
++ start = 0;
++ }
++
++ settings._iDisplayStart = start;
++ }
++
++
++ function _fnRenderer( settings, type )
++ {
++ var renderer = settings.renderer;
++ var host = DataTable.ext.renderer[type];
++
++ if ( $.isPlainObject( renderer ) && renderer[type] ) {
++ // Specific renderer for this type. If available use it, otherwise use
++ // the default.
++ return host[renderer[type]] || host._;
++ }
++ else if ( typeof renderer === 'string' ) {
++ // Common renderer - if there is one available for this type use it,
++ // otherwise use the default
++ return host[renderer] || host._;
++ }
++
++ // Use the default
++ return host._;
++ }
++
++
++ /**
++ * Detect the data source being used for the table. Used to simplify the code
++ * a little (ajax) and to make it compress a little smaller.
++ *
++ * @param {object} settings dataTables settings object
++ * @returns {string} Data source
++ * @memberof DataTable#oApi
++ */
++ function _fnDataSource ( settings )
++ {
++ if ( settings.oFeatures.bServerSide ) {
++ return 'ssp';
++ }
++ else if ( settings.ajax || settings.sAjaxSource ) {
++ return 'ajax';
++ }
++ return 'dom';
++ }
++
++
++ DataTable = function( options )
++ {
++ /**
++ * Perform a jQuery selector action on the table's TR elements (from the tbody) and
++ * return the resulting jQuery object.
++ * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
++ * @param {object} [oOpts] Optional parameters for modifying the rows to be included
++ * @param {string} [oOpts.filter=none] Select TR elements that meet the current filter
++ * criterion ("applied") or all TR elements (i.e. no filter).
++ * @param {string} [oOpts.order=current] Order of the TR elements in the processed array.
++ * Can be either 'current', whereby the current sorting of the table is used, or
++ * 'original' whereby the original order the data was read into the table is used.
++ * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
++ * ("current") or not ("all"). If 'current' is given, then order is assumed to be
++ * 'current' and filter is 'applied', regardless of what they might be given as.
++ * @returns {object} jQuery object, filtered by the given selector.
++ * @dtopt API
++ * @deprecated Since v1.10
++ *
++ * @example
++ * $(document).ready(function() {
++ * var oTable = $('#example').dataTable();
++ *
++ * // Highlight every second row
++ * oTable.$('tr:odd').css('backgroundColor', 'blue');
++ * } );
++ *
++ * @example
++ * $(document).ready(function() {
++ * var oTable = $('#example').dataTable();
++ *
++ * // Filter to rows with 'Webkit' in them, add a background colour and then
++ * // remove the filter, thus highlighting the 'Webkit' rows only.
++ * oTable.fnFilter('Webkit');
++ * oTable.$('tr', {"search": "applied"}).css('backgroundColor', 'blue');
++ * oTable.fnFilter('');
++ * } );
++ */
++ this.$ = function ( sSelector, oOpts )
++ {
++ return this.api(true).$( sSelector, oOpts );
++ };
++
++
++ /**
++ * Almost identical to $ in operation, but in this case returns the data for the matched
++ * rows - as such, the jQuery selector used should match TR row nodes or TD/TH cell nodes
++ * rather than any descendants, so the data can be obtained for the row/cell. If matching
++ * rows are found, the data returned is the original data array/object that was used to
++ * create the row (or a generated array if from a DOM source).
++ *
++ * This method is often useful in-combination with $ where both functions are given the
++ * same parameters and the array indexes will match identically.
++ * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
++ * @param {object} [oOpts] Optional parameters for modifying the rows to be included
++ * @param {string} [oOpts.filter=none] Select elements that meet the current filter
++ * criterion ("applied") or all elements (i.e. no filter).
++ * @param {string} [oOpts.order=current] Order of the data in the processed array.
++ * Can be either 'current', whereby the current sorting of the table is used, or
++ * 'original' whereby the original order the data was read into the table is used.
++ * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
++ * ("current") or not ("all"). If 'current' is given, then order is assumed to be
++ * 'current' and filter is 'applied', regardless of what they might be given as.
++ * @returns {array} Data for the matched elements. If any elements, as a result of the
++ * selector, were not TR, TD or TH elements in the DataTable, they will have a null
++ * entry in the array.
++ * @dtopt API
++ * @deprecated Since v1.10
++ *
++ * @example
++ * $(document).ready(function() {
++ * var oTable = $('#example').dataTable();
++ *
++ * // Get the data from the first row in the table
++ * var data = oTable._('tr:first');
++ *
++ * // Do something useful with the data
++ * alert( "First cell is: "+data[0] );
++ * } );
++ *
++ * @example
++ * $(document).ready(function() {
++ * var oTable = $('#example').dataTable();
++ *
++ * // Filter to 'Webkit' and get all data for
++ * oTable.fnFilter('Webkit');
++ * var data = oTable._('tr', {"search": "applied"});
++ *
++ * // Do something with the data
++ * alert( data.length+" rows matched the search" );
++ * } );
++ */
++ this._ = function ( sSelector, oOpts )
++ {
++ return this.api(true).rows( sSelector, oOpts ).data();
++ };
++
++
++ /**
++ * Create a DataTables Api instance, with the currently selected tables for
++ * the Api's context.
++ * @param {boolean} [traditional=false] Set the API instance's context to be
++ * only the table referred to by the `DataTable.ext.iApiIndex` option, as was
++ * used in the API presented by DataTables 1.9- (i.e. the traditional mode),
++ * or if all tables captured in the jQuery object should be used.
++ * @return {DataTables.Api}
++ */
++ this.api = function ( traditional )
++ {
++ return traditional ?
++ new _Api(
++ _fnSettingsFromNode( this[ _ext.iApiIndex ] )
++ ) :
++ new _Api( this );
++ };
++
++
++ /**
++ * Add a single new row or multiple rows of data to the table. Please note
++ * that this is suitable for client-side processing only - if you are using
++ * server-side processing (i.e. "bServerSide": true), then to add data, you
++ * must add it to the data source, i.e. the server-side, through an Ajax call.
++ * @param {array|object} data The data to be added to the table. This can be:
++ * <ul>
++ * <li>1D array of data - add a single row with the data provided</li>
++ * <li>2D array of arrays - add multiple rows in a single call</li>
++ * <li>object - data object when using <i>mData</i></li>
++ * <li>array of objects - multiple data objects when using <i>mData</i></li>
++ * </ul>
++ * @param {bool} [redraw=true] redraw the table or not
++ * @returns {array} An array of integers, representing the list of indexes in
++ * <i>aoData</i> ({@link DataTable.models.oSettings}) that have been added to
++ * the table.
++ * @dtopt API
++ * @deprecated Since v1.10
++ *
++ * @example
++ * // Global var for counter
++ * var giCount = 2;
++ *
++ * $(document).ready(function() {
++ * $('#example').dataTable();
++ * } );
++ *
++ * function fnClickAddRow() {
++ * $('#example').dataTable().fnAddData( [
++ * giCount+".1",
++ * giCount+".2",
++ * giCount+".3",
++ * giCount+".4" ]
++ * );
++ *
++ * giCount++;
++ * }
++ */
++ this.fnAddData = function( data, redraw )
++ {
++ var api = this.api( true );
++
++ /* Check if we want to add multiple rows or not */
++ var rows = $.isArray(data) && ( $.isArray(data[0]) || $.isPlainObject(data[0]) ) ?
++ api.rows.add( data ) :
++ api.row.add( data );
++
++ if ( redraw === undefined || redraw ) {
++ api.draw();
++ }
++
++ return rows.flatten().toArray();
++ };
++
++
++ /**
++ * This function will make DataTables recalculate the column sizes, based on the data
++ * contained in the table and the sizes applied to the columns (in the DOM, CSS or
++ * through the sWidth parameter). This can be useful when the width of the table's
++ * parent element changes (for example a window resize).
++ * @param {boolean} [bRedraw=true] Redraw the table or not, you will typically want to
++ * @dtopt API
++ * @deprecated Since v1.10
++ *
++ * @example
++ * $(document).ready(function() {
++ * var oTable = $('#example').dataTable( {
++ * "sScrollY": "200px",
++ * "bPaginate": false
++ * } );
++ *
++ * $(window).bind('resize', function () {
++ * oTable.fnAdjustColumnSizing();
++ * } );
++ * } );
++ */
++ this.fnAdjustColumnSizing = function ( bRedraw )
++ {
++ var api = this.api( true ).columns.adjust();
++ var settings = api.settings()[0];
++ var scroll = settings.oScroll;
++
++ if ( bRedraw === undefined || bRedraw ) {
++ api.draw( false );
++ }
++ else if ( scroll.sX !== "" || scroll.sY !== "" ) {
++ /* If not redrawing, but scrolling, we want to apply the new column sizes anyway */
++ _fnScrollDraw( settings );
++ }
++ };
++
++
++ /**
++ * Quickly and simply clear a table
++ * @param {bool} [bRedraw=true] redraw the table or not
++ * @dtopt API
++ * @deprecated Since v1.10
++ *
++ * @example
++ * $(document).ready(function() {
++ * var oTable = $('#example').dataTable();
++ *
++ * // Immediately 'nuke' the current rows (perhaps waiting for an Ajax callback...)
++ * oTable.fnClearTable();
++ * } );
++ */
++ this.fnClearTable = function( bRedraw )
++ {
++ var api = this.api( true ).clear();
++
++ if ( bRedraw === undefined || bRedraw ) {
++ api.draw();
++ }
++ };
++
++
++ /**
++ * The exact opposite of 'opening' a row, this function will close any rows which
++ * are currently 'open'.
++ * @param {node} nTr the table row to 'close'
++ * @returns {int} 0 on success, or 1 if failed (can't find the row)
++ * @dtopt API
++ * @deprecated Since v1.10
++ *
++ * @example
++ * $(document).ready(function() {
++ * var oTable;
++ *
++ * // 'open' an information row when a row is clicked on
++ * $('#example tbody tr').click( function () {
++ * if ( oTable.fnIsOpen(this) ) {
++ * oTable.fnClose( this );
++ * } else {
++ * oTable.fnOpen( this, "Temporary row opened", "info_row" );
++ * }
++ * } );
++ *
++ * oTable = $('#example').dataTable();
++ * } );
++ */
++ this.fnClose = function( nTr )
++ {
++ this.api( true ).row( nTr ).child.hide();
++ };
++
++
++ /**
++ * Remove a row for the table
++ * @param {mixed} target The index of the row from aoData to be deleted, or
++ * the TR element you want to delete
++ * @param {function|null} [callBack] Callback function
++ * @param {bool} [redraw=true] Redraw the table or not
++ * @returns {array} The row that was deleted
++ * @dtopt API
++ * @deprecated Since v1.10
++ *
++ * @example
++ * $(document).ready(function() {
++ * var oTable = $('#example').dataTable();
++ *
++ * // Immediately remove the first row
++ * oTable.fnDeleteRow( 0 );
++ * } );
++ */
++ this.fnDeleteRow = function( target, callback, redraw )
++ {
++ var api = this.api( true );
++ var rows = api.rows( target );
++ var settings = rows.settings()[0];
++ var data = settings.aoData[ rows[0][0] ];
++
++ rows.remove();
++
++ if ( callback ) {
++ callback.call( this, settings, data );
++ }
++
++ if ( redraw === undefined || redraw ) {
++ api.draw();
++ }
++
++ return data;
++ };
++
++
++ /**
++ * Restore the table to it's original state in the DOM by removing all of DataTables
++ * enhancements, alterations to the DOM structure of the table and event listeners.
++ * @param {boolean} [remove=false] Completely remove the table from the DOM
++ * @dtopt API
++ * @deprecated Since v1.10
++ *
++ * @example
++ * $(document).ready(function() {
++ * // This example is fairly pointless in reality, but shows how fnDestroy can be used
++ * var oTable = $('#example').dataTable();
++ * oTable.fnDestroy();
++ * } );
++ */
++ this.fnDestroy = function ( remove )
++ {
++ this.api( true ).destroy( remove );
++ };
++
++
++ /**
++ * Redraw the table
++ * @param {bool} [complete=true] Re-filter and resort (if enabled) the table before the draw.
++ * @dtopt API
++ * @deprecated Since v1.10
++ *
++ * @example
++ * $(document).ready(function() {
++ * var oTable = $('#example').dataTable();
++ *
++ * // Re-draw the table - you wouldn't want to do it here, but it's an example :-)
++ * oTable.fnDraw();
++ * } );
++ */
++ this.fnDraw = function( complete )
++ {
++ // Note that this isn't an exact match to the old call to _fnDraw - it takes
++ // into account the new data, but can hold position.
++ this.api( true ).draw( complete );
++ };
++
++
++ /**
++ * Filter the input based on data
++ * @param {string} sInput String to filter the table on
++ * @param {int|null} [iColumn] Column to limit filtering to
++ * @param {bool} [bRegex=false] Treat as regular expression or not
++ * @param {bool} [bSmart=true] Perform smart filtering or not
++ * @param {bool} [bShowGlobal=true] Show the input global filter in it's input box(es)
++ * @param {bool} [bCaseInsensitive=true] Do case-insensitive matching (true) or not (false)
++ * @dtopt API
++ * @deprecated Since v1.10
++ *
++ * @example
++ * $(document).ready(function() {
++ * var oTable = $('#example').dataTable();
++ *
++ * // Sometime later - filter...
++ * oTable.fnFilter( 'test string' );
++ * } );
++ */
++ this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive )
++ {
++ var api = this.api( true );
++
++ if ( iColumn === null || iColumn === undefined ) {
++ api.search( sInput, bRegex, bSmart, bCaseInsensitive );
++ }
++ else {
++ api.column( iColumn ).search( sInput, bRegex, bSmart, bCaseInsensitive );
++ }
++
++ api.draw();
++ };
++
++
++ /**
++ * Get the data for the whole table, an individual row or an individual cell based on the
++ * provided parameters.
++ * @param {int|node} [src] A TR row node, TD/TH cell node or an integer. If given as
++ * a TR node then the data source for the whole row will be returned. If given as a
++ * TD/TH cell node then iCol will be automatically calculated and the data for the
++ * cell returned. If given as an integer, then this is treated as the aoData internal
++ * data index for the row (see fnGetPosition) and the data for that row used.
++ * @param {int} [col] Optional column index that you want the data of.
++ * @returns {array|object|string} If mRow is undefined, then the data for all rows is
++ * returned. If mRow is defined, just data for that row, and is iCol is
++ * defined, only data for the designated cell is returned.
++ * @dtopt API
++ * @deprecated Since v1.10
++ *
++ * @example
++ * // Row data
++ * $(document).ready(function() {
++ * oTable = $('#example').dataTable();
++ *
++ * oTable.$('tr').click( function () {
++ * var data = oTable.fnGetData( this );
++ * // ... do something with the array / object of data for the row
++ * } );
++ * } );
++ *
++ * @example
++ * // Individual cell data
++ * $(document).ready(function() {
++ * oTable = $('#example').dataTable();
++ *
++ * oTable.$('td').click( function () {
++ * var sData = oTable.fnGetData( this );
++ * alert( 'The cell clicked on had the value of '+sData );
++ * } );
++ * } );
++ */
++ this.fnGetData = function( src, col )
++ {
++ var api = this.api( true );
++
++ if ( src !== undefined ) {
++ var type = src.nodeName ? src.nodeName.toLowerCase() : '';
++
++ return col !== undefined || type == 'td' || type == 'th' ?
++ api.cell( src, col ).data() :
++ api.row( src ).data() || null;
++ }
++
++ return api.data().toArray();
++ };
++
++
++ /**
++ * Get an array of the TR nodes that are used in the table's body. Note that you will
++ * typically want to use the '$' API method in preference to this as it is more
++ * flexible.
++ * @param {int} [iRow] Optional row index for the TR element you want
++ * @returns {array|node} If iRow is undefined, returns an array of all TR elements
++ * in the table's body, or iRow is defined, just the TR element requested.
++ * @dtopt API
++ * @deprecated Since v1.10
++ *
++ * @example
++ * $(document).ready(function() {
++ * var oTable = $('#example').dataTable();
++ *
++ * // Get the nodes from the table
++ * var nNodes = oTable.fnGetNodes( );
++ * } );
++ */
++ this.fnGetNodes = function( iRow )
++ {
++ var api = this.api( true );
++
++ return iRow !== undefined ?
++ api.row( iRow ).node() :
++ api.rows().nodes().flatten().toArray();
++ };
++
++
++ /**
++ * Get the array indexes of a particular cell from it's DOM element
++ * and column index including hidden columns
++ * @param {node} node this can either be a TR, TD or TH in the table's body
++ * @returns {int} If nNode is given as a TR, then a single index is returned, or
++ * if given as a cell, an array of [row index, column index (visible),
++ * column index (all)] is given.
++ * @dtopt API
++ * @deprecated Since v1.10
++ *
++ * @example
++ * $(document).ready(function() {
++ * $('#example tbody td').click( function () {
++ * // Get the position of the current data from the node
++ * var aPos = oTable.fnGetPosition( this );
++ *
++ * // Get the data array for this row
++ * var aData = oTable.fnGetData( aPos[0] );
++ *
++ * // Update the data array and return the value
++ * aData[ aPos[1] ] = 'clicked';
++ * this.innerHTML = 'clicked';
++ * } );
++ *
++ * // Init DataTables
++ * oTable = $('#example').dataTable();
++ * } );
++ */
++ this.fnGetPosition = function( node )
++ {
++ var api = this.api( true );
++ var nodeName = node.nodeName.toUpperCase();
++
++ if ( nodeName == 'TR' ) {
++ return api.row( node ).index();
++ }
++ else if ( nodeName == 'TD' || nodeName == 'TH' ) {
++ var cell = api.cell( node ).index();
++
++ return [
++ cell.row,
++ cell.columnVisible,
++ cell.column
++ ];
++ }
++ return null;
++ };
++
++
++ /**
++ * Check to see if a row is 'open' or not.
++ * @param {node} nTr the table row to check
++ * @returns {boolean} true if the row is currently open, false otherwise
++ * @dtopt API
++ * @deprecated Since v1.10
++ *
++ * @example
++ * $(document).ready(function() {
++ * var oTable;
++ *
++ * // 'open' an information row when a row is clicked on
++ * $('#example tbody tr').click( function () {
++ * if ( oTable.fnIsOpen(this) ) {
++ * oTable.fnClose( this );
++ * } else {
++ * oTable.fnOpen( this, "Temporary row opened", "info_row" );
++ * }
++ * } );
++ *
++ * oTable = $('#example').dataTable();
++ * } );
++ */
++ this.fnIsOpen = function( nTr )
++ {
++ return this.api( true ).row( nTr ).child.isShown();
++ };
++
++
++ /**
++ * This function will place a new row directly after a row which is currently
++ * on display on the page, with the HTML contents that is passed into the
++ * function. This can be used, for example, to ask for confirmation that a
++ * particular record should be deleted.
++ * @param {node} nTr The table row to 'open'
++ * @param {string|node|jQuery} mHtml The HTML to put into the row
++ * @param {string} sClass Class to give the new TD cell
++ * @returns {node} The row opened. Note that if the table row passed in as the
++ * first parameter, is not found in the table, this method will silently
++ * return.
++ * @dtopt API
++ * @deprecated Since v1.10
++ *
++ * @example
++ * $(document).ready(function() {
++ * var oTable;
++ *
++ * // 'open' an information row when a row is clicked on
++ * $('#example tbody tr').click( function () {
++ * if ( oTable.fnIsOpen(this) ) {
++ * oTable.fnClose( this );
++ * } else {
++ * oTable.fnOpen( this, "Temporary row opened", "info_row" );
++ * }
++ * } );
++ *
++ * oTable = $('#example').dataTable();
++ * } );
++ */
++ this.fnOpen = function( nTr, mHtml, sClass )
++ {
++ return this.api( true )
++ .row( nTr )
++ .child( mHtml, sClass )
++ .show()
++ .child()[0];
++ };
++
++
++ /**
++ * Change the pagination - provides the internal logic for pagination in a simple API
++ * function. With this function you can have a DataTables table go to the next,
++ * previous, first or last pages.
++ * @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last"
++ * or page number to jump to (integer), note that page 0 is the first page.
++ * @param {bool} [bRedraw=true] Redraw the table or not
++ * @dtopt API
++ * @deprecated Since v1.10
++ *
++ * @example
++ * $(document).ready(function() {
++ * var oTable = $('#example').dataTable();
++ * oTable.fnPageChange( 'next' );
++ * } );
++ */
++ this.fnPageChange = function ( mAction, bRedraw )
++ {
++ var api = this.api( true ).page( mAction );
++
++ if ( bRedraw === undefined || bRedraw ) {
++ api.draw(false);
++ }
++ };
++
++
++ /**
++ * Show a particular column
++ * @param {int} iCol The column whose display should be changed
++ * @param {bool} bShow Show (true) or hide (false) the column
++ * @param {bool} [bRedraw=true] Redraw the table or not
++ * @dtopt API
++ * @deprecated Since v1.10
++ *
++ * @example
++ * $(document).ready(function() {
++ * var oTable = $('#example').dataTable();
++ *
++ * // Hide the second column after initialisation
++ * oTable.fnSetColumnVis( 1, false );
++ * } );
++ */
++ this.fnSetColumnVis = function ( iCol, bShow, bRedraw )
++ {
++ var api = this.api( true ).column( iCol ).visible( bShow );
++
++ if ( bRedraw === undefined || bRedraw ) {
++ api.columns.adjust().draw();
++ }
++ };
++
++
++ /**
++ * Get the settings for a particular table for external manipulation
++ * @returns {object} DataTables settings object. See
++ * {@link DataTable.models.oSettings}
++ * @dtopt API
++ * @deprecated Since v1.10
++ *
++ * @example
++ * $(document).ready(function() {
++ * var oTable = $('#example').dataTable();
++ * var oSettings = oTable.fnSettings();
++ *
++ * // Show an example parameter from the settings
++ * alert( oSettings._iDisplayStart );
++ * } );
++ */
++ this.fnSettings = function()
++ {
++ return _fnSettingsFromNode( this[_ext.iApiIndex] );
++ };
++
++
++ /**
++ * Sort the table by a particular column
++ * @param {int} iCol the data index to sort on. Note that this will not match the
++ * 'display index' if you have hidden data entries
++ * @dtopt API
++ * @deprecated Since v1.10
++ *
++ * @example
++ * $(document).ready(function() {
++ * var oTable = $('#example').dataTable();
++ *
++ * // Sort immediately with columns 0 and 1
++ * oTable.fnSort( [ [0,'asc'], [1,'asc'] ] );
++ * } );
++ */
++ this.fnSort = function( aaSort )
++ {
++ this.api( true ).order( aaSort ).draw();
++ };
++
++
++ /**
++ * Attach a sort listener to an element for a given column
++ * @param {node} nNode the element to attach the sort listener to
++ * @param {int} iColumn the column that a click on this node will sort on
++ * @param {function} [fnCallback] callback function when sort is run
++ * @dtopt API
++ * @deprecated Since v1.10
++ *
++ * @example
++ * $(document).ready(function() {
++ * var oTable = $('#example').dataTable();
++ *
++ * // Sort on column 1, when 'sorter' is clicked on
++ * oTable.fnSortListener( document.getElementById('sorter'), 1 );
++ * } );
++ */
++ this.fnSortListener = function( nNode, iColumn, fnCallback )
++ {
++ this.api( true ).order.listener( nNode, iColumn, fnCallback );
++ };
++
++
++ /**
++ * Update a table cell or row - this method will accept either a single value to
++ * update the cell with, an array of values with one element for each column or
++ * an object in the same format as the original data source. The function is
++ * self-referencing in order to make the multi column updates easier.
++ * @param {object|array|string} mData Data to update the cell/row with
++ * @param {node|int} mRow TR element you want to update or the aoData index
++ * @param {int} [iColumn] The column to update, give as null or undefined to
++ * update a whole row.
++ * @param {bool} [bRedraw=true] Redraw the table or not
++ * @param {bool} [bAction=true] Perform pre-draw actions or not
++ * @returns {int} 0 on success, 1 on error
++ * @dtopt API
++ * @deprecated Since v1.10
++ *
++ * @example
++ * $(document).ready(function() {
++ * var oTable = $('#example').dataTable();
++ * oTable.fnUpdate( 'Example update', 0, 0 ); // Single cell
++ * oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], $('tbody tr')[0] ); // Row
++ * } );
++ */
++ this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction )
++ {
++ var api = this.api( true );
++
++ if ( iColumn === undefined || iColumn === null ) {
++ api.row( mRow ).data( mData );
++ }
++ else {
++ api.cell( mRow, iColumn ).data( mData );
++ }
++
++ if ( bAction === undefined || bAction ) {
++ api.columns.adjust();
++ }
++
++ if ( bRedraw === undefined || bRedraw ) {
++ api.draw();
++ }
++ return 0;
++ };
++
++
++ /**
++ * Provide a common method for plug-ins to check the version of DataTables being used, in order
++ * to ensure compatibility.
++ * @param {string} sVersion Version string to check for, in the format "X.Y.Z". Note that the
++ * formats "X" and "X.Y" are also acceptable.
++ * @returns {boolean} true if this version of DataTables is greater or equal to the required
++ * version, or false if this version of DataTales is not suitable
++ * @method
++ * @dtopt API
++ * @deprecated Since v1.10
++ *
++ * @example
++ * $(document).ready(function() {
++ * var oTable = $('#example').dataTable();
++ * alert( oTable.fnVersionCheck( '1.9.0' ) );
++ * } );
++ */
++ this.fnVersionCheck = _ext.fnVersionCheck;
++
++
++ var _that = this;
++ var emptyInit = options === undefined;
++ var len = this.length;
++
++ if ( emptyInit ) {
++ options = {};
++ }
++
++ this.oApi = this.internal = _ext.internal;
++
++ // Extend with old style plug-in API methods
++ for ( var fn in DataTable.ext.internal ) {
++ if ( fn ) {
++ this[fn] = _fnExternApiFunc(fn);
++ }
++ }
++
++ this.each(function() {
++ // For each initialisation we want to give it a clean initialisation
++ // object that can be bashed around
++ var o = {};
++ var oInit = len > 1 ? // optimisation for single table case
++ _fnExtend( o, options, true ) :
++ options;
++
++ /*global oInit,_that,emptyInit*/
++ var i=0, iLen, j, jLen, k, kLen;
++ var sId = this.getAttribute( 'id' );
++ var bInitHandedOff = false;
++ var defaults = DataTable.defaults;
++ var $this = $(this);
++
++
++ /* Sanity check */
++ if ( this.nodeName.toLowerCase() != 'table' )
++ {
++ _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 );
++ return;
++ }
++
++ /* Backwards compatibility for the defaults */
++ _fnCompatOpts( defaults );
++ _fnCompatCols( defaults.column );
++
++ /* Convert the camel-case defaults to Hungarian */
++ _fnCamelToHungarian( defaults, defaults, true );
++ _fnCamelToHungarian( defaults.column, defaults.column, true );
++
++ /* Setting up the initialisation object */
++ _fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ) );
++
++
++
++ /* Check to see if we are re-initialising a table */
++ var allSettings = DataTable.settings;
++ for ( i=0, iLen=allSettings.length ; i<iLen ; i++ )
++ {
++ var s = allSettings[i];
++
++ /* Base check on table node */
++ if ( s.nTable == this || s.nTHead.parentNode == this || (s.nTFoot && s.nTFoot.parentNode == this) )
++ {
++ var bRetrieve = oInit.bRetrieve !== undefined ? oInit.bRetrieve : defaults.bRetrieve;
++ var bDestroy = oInit.bDestroy !== undefined ? oInit.bDestroy : defaults.bDestroy;
++
++ if ( emptyInit || bRetrieve )
++ {
++ return s.oInstance;
++ }
++ else if ( bDestroy )
++ {
++ s.oInstance.fnDestroy();
++ break;
++ }
++ else
++ {
++ _fnLog( s, 0, 'Cannot reinitialise DataTable', 3 );
++ return;
++ }
++ }
++
++ /* If the element we are initialising has the same ID as a table which was previously
++ * initialised, but the table nodes don't match (from before) then we destroy the old
++ * instance by simply deleting it. This is under the assumption that the table has been
++ * destroyed by other methods. Anyone using non-id selectors will need to do this manually
++ */
++ if ( s.sTableId == this.id )
++ {
++ allSettings.splice( i, 1 );
++ break;
++ }
++ }
++
++ /* Ensure the table has an ID - required for accessibility */
++ if ( sId === null || sId === "" )
++ {
++ sId = "DataTables_Table_"+(DataTable.ext._unique++);
++ this.id = sId;
++ }
++
++ /* Create the settings object for this table and set some of the default parameters */
++ var oSettings = $.extend( true, {}, DataTable.models.oSettings, {
++ "sDestroyWidth": $this[0].style.width,
++ "sInstance": sId,
++ "sTableId": sId
++ } );
++ oSettings.nTable = this;
++ oSettings.oApi = _that.internal;
++ oSettings.oInit = oInit;
++
++ allSettings.push( oSettings );
++
++ // Need to add the instance after the instance after the settings object has been added
++ // to the settings array, so we can self reference the table instance if more than one
++ oSettings.oInstance = (_that.length===1) ? _that : $this.dataTable();
++
++ // Backwards compatibility, before we apply all the defaults
++ _fnCompatOpts( oInit );
++
++ if ( oInit.oLanguage )
++ {
++ _fnLanguageCompat( oInit.oLanguage );
++ }
++
++ // If the length menu is given, but the init display length is not, use the length menu
++ if ( oInit.aLengthMenu && ! oInit.iDisplayLength )
++ {
++ oInit.iDisplayLength = $.isArray( oInit.aLengthMenu[0] ) ?
++ oInit.aLengthMenu[0][0] : oInit.aLengthMenu[0];
++ }
++
++ // Apply the defaults and init options to make a single init object will all
++ // options defined from defaults and instance options.
++ oInit = _fnExtend( $.extend( true, {}, defaults ), oInit );
++
++
++ // Map the initialisation options onto the settings object
++ _fnMap( oSettings.oFeatures, oInit, [
++ "bPaginate",
++ "bLengthChange",
++ "bFilter",
++ "bSort",
++ "bSortMulti",
++ "bInfo",
++ "bProcessing",
++ "bAutoWidth",
++ "bSortClasses",
++ "bServerSide",
++ "bDeferRender"
++ ] );
++ _fnMap( oSettings, oInit, [
++ "asStripeClasses",
++ "ajax",
++ "fnServerData",
++ "fnFormatNumber",
++ "sServerMethod",
++ "aaSorting",
++ "aaSortingFixed",
++ "aLengthMenu",
++ "sPaginationType",
++ "sAjaxSource",
++ "sAjaxDataProp",
++ "iStateDuration",
++ "sDom",
++ "bSortCellsTop",
++ "iTabIndex",
++ "fnStateLoadCallback",
++ "fnStateSaveCallback",
++ "renderer",
++ "searchDelay",
++ [ "iCookieDuration", "iStateDuration" ], // backwards compat
++ [ "oSearch", "oPreviousSearch" ],
++ [ "aoSearchCols", "aoPreSearchCols" ],
++ [ "iDisplayLength", "_iDisplayLength" ],
++ [ "bJQueryUI", "bJUI" ]
++ ] );
++ _fnMap( oSettings.oScroll, oInit, [
++ [ "sScrollX", "sX" ],
++ [ "sScrollXInner", "sXInner" ],
++ [ "sScrollY", "sY" ],
++ [ "bScrollCollapse", "bCollapse" ]
++ ] );
++ _fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" );
++
++ /* Callback functions which are array driven */
++ _fnCallbackReg( oSettings, 'aoDrawCallback', oInit.fnDrawCallback, 'user' );
++ _fnCallbackReg( oSettings, 'aoServerParams', oInit.fnServerParams, 'user' );
++ _fnCallbackReg( oSettings, 'aoStateSaveParams', oInit.fnStateSaveParams, 'user' );
++ _fnCallbackReg( oSettings, 'aoStateLoadParams', oInit.fnStateLoadParams, 'user' );
++ _fnCallbackReg( oSettings, 'aoStateLoaded', oInit.fnStateLoaded, 'user' );
++ _fnCallbackReg( oSettings, 'aoRowCallback', oInit.fnRowCallback, 'user' );
++ _fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow, 'user' );
++ _fnCallbackReg( oSettings, 'aoHeaderCallback', oInit.fnHeaderCallback, 'user' );
++ _fnCallbackReg( oSettings, 'aoFooterCallback', oInit.fnFooterCallback, 'user' );
++ _fnCallbackReg( oSettings, 'aoInitComplete', oInit.fnInitComplete, 'user' );
++ _fnCallbackReg( oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback, 'user' );
++
++ var oClasses = oSettings.oClasses;
++
++ // @todo Remove in 1.11
++ if ( oInit.bJQueryUI )
++ {
++ /* Use the JUI classes object for display. You could clone the oStdClasses object if
++ * you want to have multiple tables with multiple independent classes
++ */
++ $.extend( oClasses, DataTable.ext.oJUIClasses, oInit.oClasses );
++
++ if ( oInit.sDom === defaults.sDom && defaults.sDom === "lfrtip" )
++ {
++ /* Set the DOM to use a layout suitable for jQuery UI's theming */
++ oSettings.sDom = '<"H"lfr>t<"F"ip>';
++ }
++
++ if ( ! oSettings.renderer ) {
++ oSettings.renderer = 'jqueryui';
++ }
++ else if ( $.isPlainObject( oSettings.renderer ) && ! oSettings.renderer.header ) {
++ oSettings.renderer.header = 'jqueryui';
++ }
++ }
++ else
++ {
++ $.extend( oClasses, DataTable.ext.classes, oInit.oClasses );
++ }
++ $this.addClass( oClasses.sTable );
++
++ /* Calculate the scroll bar width and cache it for use later on */
++ if ( oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "" )
++ {
++ oSettings.oScroll.iBarWidth = _fnScrollBarWidth();
++ }
++ if ( oSettings.oScroll.sX === true ) { // Easy initialisation of x-scrolling
++ oSettings.oScroll.sX = '100%';
++ }
++
++ if ( oSettings.iInitDisplayStart === undefined )
++ {
++ /* Display start point, taking into account the save saving */
++ oSettings.iInitDisplayStart = oInit.iDisplayStart;
++ oSettings._iDisplayStart = oInit.iDisplayStart;
++ }
++
++ if ( oInit.iDeferLoading !== null )
++ {
++ oSettings.bDeferLoading = true;
++ var tmp = $.isArray( oInit.iDeferLoading );
++ oSettings._iRecordsDisplay = tmp ? oInit.iDeferLoading[0] : oInit.iDeferLoading;
++ oSettings._iRecordsTotal = tmp ? oInit.iDeferLoading[1] : oInit.iDeferLoading;
++ }
++
++ /* Language definitions */
++ var oLanguage = oSettings.oLanguage;
++ $.extend( true, oLanguage, oInit.oLanguage );
++
++ if ( oLanguage.sUrl !== "" )
++ {
++ /* Get the language definitions from a file - because this Ajax call makes the language
++ * get async to the remainder of this function we use bInitHandedOff to indicate that
++ * _fnInitialise will be fired by the returned Ajax handler, rather than the constructor
++ */
++ $.ajax( {
++ dataType: 'json',
++ url: oLanguage.sUrl,
++ success: function ( json ) {
++ _fnLanguageCompat( json );
++ _fnCamelToHungarian( defaults.oLanguage, json );
++ $.extend( true, oLanguage, json );
++ _fnInitialise( oSettings );
++ },
++ error: function () {
++ // Error occurred loading language file, continue on as best we can
++ _fnInitialise( oSettings );
++ }
++ } );
++ bInitHandedOff = true;
++ }
++
++ /*
++ * Stripes
++ */
++ if ( oInit.asStripeClasses === null )
++ {
++ oSettings.asStripeClasses =[
++ oClasses.sStripeOdd,
++ oClasses.sStripeEven
++ ];
++ }
++
++ /* Remove row stripe classes if they are already on the table row */
++ var stripeClasses = oSettings.asStripeClasses;
++ var rowOne = $this.children('tbody').find('tr').eq(0);
++ if ( $.inArray( true, $.map( stripeClasses, function(el, i) {
++ return rowOne.hasClass(el);
++ } ) ) !== -1 ) {
++ $('tbody tr', this).removeClass( stripeClasses.join(' ') );
++ oSettings.asDestroyStripes = stripeClasses.slice();
++ }
++
++ /*
++ * Columns
++ * See if we should load columns automatically or use defined ones
++ */
++ var anThs = [];
++ var aoColumnsInit;
++ var nThead = this.getElementsByTagName('thead');
++ if ( nThead.length !== 0 )
++ {
++ _fnDetectHeader( oSettings.aoHeader, nThead[0] );
++ anThs = _fnGetUniqueThs( oSettings );
++ }
++
++ /* If not given a column array, generate one with nulls */
++ if ( oInit.aoColumns === null )
++ {
++ aoColumnsInit = [];
++ for ( i=0, iLen=anThs.length ; i<iLen ; i++ )
++ {
++ aoColumnsInit.push( null );
++ }
++ }
++ else
++ {
++ aoColumnsInit = oInit.aoColumns;
++ }
++
++ /* Add the columns */
++ for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ )
++ {
++ _fnAddColumn( oSettings, anThs ? anThs[i] : null );
++ }
++
++ /* Apply the column definitions */
++ _fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, aoColumnsInit, function (iCol, oDef) {
++ _fnColumnOptions( oSettings, iCol, oDef );
++ } );
++
++ /* HTML5 attribute detection - build an mData object automatically if the
++ * attributes are found
++ */
++ if ( rowOne.length ) {
++ var a = function ( cell, name ) {
++ return cell.getAttribute( 'data-'+name ) !== null ? name : null;
++ };
++
++ $.each( _fnGetRowElements( oSettings, rowOne[0] ).cells, function (i, cell) {
++ var col = oSettings.aoColumns[i];
++
++ if ( col.mData === i ) {
++ var sort = a( cell, 'sort' ) || a( cell, 'order' );
++ var filter = a( cell, 'filter' ) || a( cell, 'search' );
++
++ if ( sort !== null || filter !== null ) {
++ col.mData = {
++ _: i+'.display',
++ sort: sort !== null ? i+'.@data-'+sort : undefined,
++ type: sort !== null ? i+'.@data-'+sort : undefined,
++ filter: filter !== null ? i+'.@data-'+filter : undefined
++ };
++
++ _fnColumnOptions( oSettings, i );
++ }
++ }
++ } );
++ }
++
++ var features = oSettings.oFeatures;
++
++ /* Must be done after everything which can be overridden by the state saving! */
++ if ( oInit.bStateSave )
++ {
++ features.bStateSave = true;
++ _fnLoadState( oSettings, oInit );
++ _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState, 'state_save' );
++ }
++
++
++ /*
++ * Sorting
++ * @todo For modularisation (1.11) this needs to do into a sort start up handler
++ */
++
++ // If aaSorting is not defined, then we use the first indicator in asSorting
++ // in case that has been altered, so the default sort reflects that option
++ if ( oInit.aaSorting === undefined )
++ {
++ var sorting = oSettings.aaSorting;
++ for ( i=0, iLen=sorting.length ; i<iLen ; i++ )
++ {
++ sorting[i][1] = oSettings.aoColumns[ i ].asSorting[0];
++ }
++ }
++
++ /* Do a first pass on the sorting classes (allows any size changes to be taken into
++ * account, and also will apply sorting disabled classes if disabled
++ */
++ _fnSortingClasses( oSettings );
++
++ if ( features.bSort )
++ {
++ _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
++ if ( oSettings.bSorted ) {
++ var aSort = _fnSortFlatten( oSettings );
++ var sortedColumns = {};
++
++ $.each( aSort, function (i, val) {
++ sortedColumns[ val.src ] = val.dir;
++ } );
++
++ _fnCallbackFire( oSettings, null, 'order', [oSettings, aSort, sortedColumns] );
++ _fnSortAria( oSettings );
++ }
++ } );
++ }
++
++ _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
++ if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) {
++ _fnSortingClasses( oSettings );
++ }
++ }, 'sc' );
++
++
++ /*
++ * Final init
++ * Cache the header, body and footer as required, creating them if needed
++ */
++
++ /* Browser support detection */
++ _fnBrowserDetect( oSettings );
++
++ // Work around for Webkit bug 83867 - store the caption-side before removing from doc
++ var captions = $this.children('caption').each( function () {
++ this._captionSide = $this.css('caption-side');
++ } );
++
++ var thead = $this.children('thead');
++ if ( thead.length === 0 )
++ {
++ thead = $('<thead/>').appendTo(this);
++ }
++ oSettings.nTHead = thead[0];
++
++ var tbody = $this.children('tbody');
++ if ( tbody.length === 0 )
++ {
++ tbody = $('<tbody/>').appendTo(this);
++ }
++ oSettings.nTBody = tbody[0];
++
++ var tfoot = $this.children('tfoot');
++ if ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") )
++ {
++ // If we are a scrolling table, and no footer has been given, then we need to create
++ // a tfoot element for the caption element to be appended to
++ tfoot = $('<tfoot/>').appendTo(this);
++ }
++
++ if ( tfoot.length === 0 || tfoot.children().length === 0 ) {
++ $this.addClass( oClasses.sNoFooter );
++ }
++ else if ( tfoot.length > 0 ) {
++ oSettings.nTFoot = tfoot[0];
++ _fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot );
++ }
++
++ /* Check if there is data passing into the constructor */
++ if ( oInit.aaData )
++ {
++ for ( i=0 ; i<oInit.aaData.length ; i++ )
++ {
++ _fnAddData( oSettings, oInit.aaData[ i ] );
++ }
++ }
++ else if ( oSettings.bDeferLoading || _fnDataSource( oSettings ) == 'dom' )
++ {
++ /* Grab the data from the page - only do this when deferred loading or no Ajax
++ * source since there is no point in reading the DOM data if we are then going
++ * to replace it with Ajax data
++ */
++ _fnAddTr( oSettings, $(oSettings.nTBody).children('tr') );
++ }
++
++ /* Copy the data index array */
++ oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
++
++ /* Initialisation complete - table can be drawn */
++ oSettings.bInitialised = true;
++
++ /* Check if we need to initialise the table (it might not have been handed off to the
++ * language processor)
++ */
++ if ( bInitHandedOff === false )
++ {
++ _fnInitialise( oSettings );
++ }
++ } );
++ _that = null;
++ return this;
++ };
++
++
++
++ /**
++ * Computed structure of the DataTables API, defined by the options passed to
++ * `DataTable.Api.register()` when building the API.
++ *
++ * The structure is built in order to speed creation and extension of the Api
++ * objects since the extensions are effectively pre-parsed.
++ *
++ * The array is an array of objects with the following structure, where this
++ * base array represents the Api prototype base:
++ *
++ * [
++ * {
++ * name: 'data' -- string - Property name
++ * val: function () {}, -- function - Api method (or undefined if just an object
++ * methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result
++ * propExt: [ ... ] -- array - Array of Api object definitions to extend the property
++ * },
++ * {
++ * name: 'row'
++ * val: {},
++ * methodExt: [ ... ],
++ * propExt: [
++ * {
++ * name: 'data'
++ * val: function () {},
++ * methodExt: [ ... ],
++ * propExt: [ ... ]
++ * },
++ * ...
++ * ]
++ * }
++ * ]
++ *
++ * @type {Array}
++ * @ignore
++ */
++ var __apiStruct = [];
++
++
++ /**
++ * `Array.prototype` reference.
++ *
++ * @type object
++ * @ignore
++ */
++ var __arrayProto = Array.prototype;
++
++
++ /**
++ * Abstraction for `context` parameter of the `Api` constructor to allow it to
++ * take several different forms for ease of use.
++ *
++ * Each of the input parameter types will be converted to a DataTables settings
++ * object where possible.
++ *
++ * @param {string|node|jQuery|object} mixed DataTable identifier. Can be one
++ * of:
++ *
++ * * `string` - jQuery selector. Any DataTables' matching the given selector
++ * with be found and used.
++ * * `node` - `TABLE` node which has already been formed into a DataTable.
++ * * `jQuery` - A jQuery object of `TABLE` nodes.
++ * * `object` - DataTables settings object
++ * * `DataTables.Api` - API instance
++ * @return {array|null} Matching DataTables settings objects. `null` or
++ * `undefined` is returned if no matching DataTable is found.
++ * @ignore
++ */
++ var _toSettings = function ( mixed )
++ {
++ var idx, jq;
++ var settings = DataTable.settings;
++ var tables = $.map( settings, function (el, i) {
++ return el.nTable;
++ } );
++
++ if ( ! mixed ) {
++ return [];
++ }
++ else if ( mixed.nTable && mixed.oApi ) {
++ // DataTables settings object
++ return [ mixed ];
++ }
++ else if ( mixed.nodeName && mixed.nodeName.toLowerCase() === 'table' ) {
++ // Table node
++ idx = $.inArray( mixed, tables );
++ return idx !== -1 ? [ settings[idx] ] : null;
++ }
++ else if ( mixed && typeof mixed.settings === 'function' ) {
++ return mixed.settings().toArray();
++ }
++ else if ( typeof mixed === 'string' ) {
++ // jQuery selector
++ jq = $(mixed);
++ }
++ else if ( mixed instanceof $ ) {
++ // jQuery object (also DataTables instance)
++ jq = mixed;
++ }
++
++ if ( jq ) {
++ return jq.map( function(i) {
++ idx = $.inArray( this, tables );
++ return idx !== -1 ? settings[idx] : null;
++ } ).toArray();
++ }
++ };
++
++
++ /**
++ * DataTables API class - used to control and interface with one or more
++ * DataTables enhanced tables.
++ *
++ * The API class is heavily based on jQuery, presenting a chainable interface
++ * that you can use to interact with tables. Each instance of the API class has
++ * a "context" - i.e. the tables that it will operate on. This could be a single
++ * table, all tables on a page or a sub-set thereof.
++ *
++ * Additionally the API is designed to allow you to easily work with the data in
++ * the tables, retrieving and manipulating it as required. This is done by
++ * presenting the API class as an array like interface. The contents of the
++ * array depend upon the actions requested by each method (for example
++ * `rows().nodes()` will return an array of nodes, while `rows().data()` will
++ * return an array of objects or arrays depending upon your table's
++ * configuration). The API object has a number of array like methods (`push`,
++ * `pop`, `reverse` etc) as well as additional helper methods (`each`, `pluck`,
++ * `unique` etc) to assist your working with the data held in a table.
++ *
++ * Most methods (those which return an Api instance) are chainable, which means
++ * the return from a method call also has all of the methods available that the
++ * top level object had. For example, these two calls are equivalent:
++ *
++ * // Not chained
++ * api.row.add( {...} );
++ * api.draw();
++ *
++ * // Chained
++ * api.row.add( {...} ).draw();
++ *
++ * @class DataTable.Api
++ * @param {array|object|string|jQuery} context DataTable identifier. This is
++ * used to define which DataTables enhanced tables this API will operate on.
++ * Can be one of:
++ *
++ * * `string` - jQuery selector. Any DataTables' matching the given selector
++ * with be found and used.
++ * * `node` - `TABLE` node which has already been formed into a DataTable.
++ * * `jQuery` - A jQuery object of `TABLE` nodes.
++ * * `object` - DataTables settings object
++ * @param {array} [data] Data to initialise the Api instance with.
++ *
++ * @example
++ * // Direct initialisation during DataTables construction
++ * var api = $('#example').DataTable();
++ *
++ * @example
++ * // Initialisation using a DataTables jQuery object
++ * var api = $('#example').dataTable().api();
++ *
++ * @example
++ * // Initialisation as a constructor
++ * var api = new $.fn.DataTable.Api( 'table.dataTable' );
++ */
++ _Api = function ( context, data )
++ {
++ if ( ! (this instanceof _Api) ) {
++ return new _Api( context, data );
++ }
++
++ var settings = [];
++ var ctxSettings = function ( o ) {
++ var a = _toSettings( o );
++ if ( a ) {
++ settings.push.apply( settings, a );
++ }
++ };
++
++ if ( $.isArray( context ) ) {
++ for ( var i=0, ien=context.length ; i<ien ; i++ ) {
++ ctxSettings( context[i] );
++ }
++ }
++ else {
++ ctxSettings( context );
++ }
++
++ // Remove duplicates
++ this.context = _unique( settings );
++
++ // Initial data
++ if ( data ) {
++ this.push.apply( this, data.toArray ? data.toArray() : data );
++ }
++
++ // selector
++ this.selector = {
++ rows: null,
++ cols: null,
++ opts: null
++ };
++
++ _Api.extend( this, this, __apiStruct );
++ };
++
++ DataTable.Api = _Api;
++
++ _Api.prototype = /** @lends DataTables.Api */{
++ any: function ()
++ {
++ return this.flatten().length !== 0;
++ },
++
++
++ concat: __arrayProto.concat,
++
++
++ context: [], // array of table settings objects
++
++
++ each: function ( fn )
++ {
++ for ( var i=0, ien=this.length ; i<ien; i++ ) {
++ fn.call( this, this[i], i, this );
++ }
++
++ return this;
++ },
++
++
++ eq: function ( idx )
++ {
++ var ctx = this.context;
++
++ return ctx.length > idx ?
++ new _Api( ctx[idx], this[idx] ) :
++ null;
++ },
++
++
++ filter: function ( fn )
++ {
++ var a = [];
++
++ if ( __arrayProto.filter ) {
++ a = __arrayProto.filter.call( this, fn, this );
++ }
++ else {
++ // Compatibility for browsers without EMCA-252-5 (JS 1.6)
++ for ( var i=0, ien=this.length ; i<ien ; i++ ) {
++ if ( fn.call( this, this[i], i, this ) ) {
++ a.push( this[i] );
++ }
++ }
++ }
++
++ return new _Api( this.context, a );
++ },
++
++
++ flatten: function ()
++ {
++ var a = [];
++ return new _Api( this.context, a.concat.apply( a, this.toArray() ) );
++ },
++
++
++ join: __arrayProto.join,
++
++
++ indexOf: __arrayProto.indexOf || function (obj, start)
++ {
++ for ( var i=(start || 0), ien=this.length ; i<ien ; i++ ) {
++ if ( this[i] === obj ) {
++ return i;
++ }
++ }
++ return -1;
++ },
++
++ iterator: function ( flatten, type, fn, alwaysNew ) {
++ var
++ a = [], ret,
++ i, ien, j, jen,
++ context = this.context,
++ rows, items, item,
++ selector = this.selector;
++
++ // Argument shifting
++ if ( typeof flatten === 'string' ) {
++ alwaysNew = fn;
++ fn = type;
++ type = flatten;
++ flatten = false;
++ }
++
++ for ( i=0, ien=context.length ; i<ien ; i++ ) {
++ var apiInst = new _Api( context[i] );
++
++ if ( type === 'table' ) {
++ ret = fn.call( apiInst, context[i], i );
++
++ if ( ret !== undefined ) {
++ a.push( ret );
++ }
++ }
++ else if ( type === 'columns' || type === 'rows' ) {
++ // this has same length as context - one entry for each table
++ ret = fn.call( apiInst, context[i], this[i], i );
++
++ if ( ret !== undefined ) {
++ a.push( ret );
++ }
++ }
++ else if ( type === 'column' || type === 'column-rows' || type === 'row' || type === 'cell' ) {
++ // columns and rows share the same structure.
++ // 'this' is an array of column indexes for each context
++ items = this[i];
++
++ if ( type === 'column-rows' ) {
++ rows = _selector_row_indexes( context[i], selector.opts );
++ }
++
++ for ( j=0, jen=items.length ; j<jen ; j++ ) {
++ item = items[j];
++
++ if ( type === 'cell' ) {
++ ret = fn.call( apiInst, context[i], item.row, item.column, i, j );
++ }
++ else {
++ ret = fn.call( apiInst, context[i], item, i, j, rows );
++ }
++
++ if ( ret !== undefined ) {
++ a.push( ret );
++ }
++ }
++ }
++ }
++
++ if ( a.length || alwaysNew ) {
++ var api = new _Api( context, flatten ? a.concat.apply( [], a ) : a );
++ var apiSelector = api.selector;
++ apiSelector.rows = selector.rows;
++ apiSelector.cols = selector.cols;
++ apiSelector.opts = selector.opts;
++ return api;
++ }
++ return this;
++ },
++
++
++ lastIndexOf: __arrayProto.lastIndexOf || function (obj, start)
++ {
++ // Bit cheeky...
++ return this.indexOf.apply( this.toArray.reverse(), arguments );
++ },
++
++
++ length: 0,
++
++
++ map: function ( fn )
++ {
++ var a = [];
++
++ if ( __arrayProto.map ) {
++ a = __arrayProto.map.call( this, fn, this );
++ }
++ else {
++ // Compatibility for browsers without EMCA-252-5 (JS 1.6)
++ for ( var i=0, ien=this.length ; i<ien ; i++ ) {
++ a.push( fn.call( this, this[i], i ) );
++ }
++ }
++
++ return new _Api( this.context, a );
++ },
++
++
++ pluck: function ( prop )
++ {
++ return this.map( function ( el ) {
++ return el[ prop ];
++ } );
++ },
++
++ pop: __arrayProto.pop,
++
++
++ push: __arrayProto.push,
++
++
++ // Does not return an API instance
++ reduce: __arrayProto.reduce || function ( fn, init )
++ {
++ return _fnReduce( this, fn, init, 0, this.length, 1 );
++ },
++
++
++ reduceRight: __arrayProto.reduceRight || function ( fn, init )
++ {
++ return _fnReduce( this, fn, init, this.length-1, -1, -1 );
++ },
++
++
++ reverse: __arrayProto.reverse,
++
++
++ // Object with rows, columns and opts
++ selector: null,
++
++
++ shift: __arrayProto.shift,
++
++
++ sort: __arrayProto.sort, // ? name - order?
++
++
++ splice: __arrayProto.splice,
++
++
++ toArray: function ()
++ {
++ return __arrayProto.slice.call( this );
++ },
++
++
++ to$: function ()
++ {
++ return $( this );
++ },
++
++
++ toJQuery: function ()
++ {
++ return $( this );
++ },
++
++
++ unique: function ()
++ {
++ return new _Api( this.context, _unique(this) );
++ },
++
++
++ unshift: __arrayProto.unshift
++ };
++
++
++ _Api.extend = function ( scope, obj, ext )
++ {
++ // Only extend API instances and static properties of the API
++ if ( ! ext.length || ! obj || ( ! (obj instanceof _Api) && ! obj.__dt_wrapper ) ) {
++ return;
++ }
++
++ var
++ i, ien,
++ j, jen,
++ struct, inner,
++ methodScoping = function ( scope, fn, struc ) {
++ return function () {
++ var ret = fn.apply( scope, arguments );
++
++ // Method extension
++ _Api.extend( ret, ret, struc.methodExt );
++ return ret;
++ };
++ };
++
++ for ( i=0, ien=ext.length ; i<ien ; i++ ) {
++ struct = ext[i];
++
++ // Value
++ obj[ struct.name ] = typeof struct.val === 'function' ?
++ methodScoping( scope, struct.val, struct ) :
++ $.isPlainObject( struct.val ) ?
++ {} :
++ struct.val;
++
++ obj[ struct.name ].__dt_wrapper = true;
++
++ // Property extension
++ _Api.extend( scope, obj[ struct.name ], struct.propExt );
++ }
++ };
++
++
++ // @todo - Is there need for an augment function?
++ // _Api.augment = function ( inst, name )
++ // {
++ // // Find src object in the structure from the name
++ // var parts = name.split('.');
++
++ // _Api.extend( inst, obj );
++ // };
++
++
++ // [
++ // {
++ // name: 'data' -- string - Property name
++ // val: function () {}, -- function - Api method (or undefined if just an object
++ // methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result
++ // propExt: [ ... ] -- array - Array of Api object definitions to extend the property
++ // },
++ // {
++ // name: 'row'
++ // val: {},
++ // methodExt: [ ... ],
++ // propExt: [
++ // {
++ // name: 'data'
++ // val: function () {},
++ // methodExt: [ ... ],
++ // propExt: [ ... ]
++ // },
++ // ...
++ // ]
++ // }
++ // ]
++
++ _Api.register = _api_register = function ( name, val )
++ {
++ if ( $.isArray( name ) ) {
++ for ( var j=0, jen=name.length ; j<jen ; j++ ) {
++ _Api.register( name[j], val );
++ }
++ return;
++ }
++
++ var
++ i, ien,
++ heir = name.split('.'),
++ struct = __apiStruct,
++ key, method;
++
++ var find = function ( src, name ) {
++ for ( var i=0, ien=src.length ; i<ien ; i++ ) {
++ if ( src[i].name === name ) {
++ return src[i];
++ }
++ }
++ return null;
++ };
++
++ for ( i=0, ien=heir.length ; i<ien ; i++ ) {
++ method = heir[i].indexOf('()') !== -1;
++ key = method ?
++ heir[i].replace('()', '') :
++ heir[i];
++
++ var src = find( struct, key );
++ if ( ! src ) {
++ src = {
++ name: key,
++ val: {},
++ methodExt: [],
++ propExt: []
++ };
++ struct.push( src );
++ }
++
++ if ( i === ien-1 ) {
++ src.val = val;
++ }
++ else {
++ struct = method ?
++ src.methodExt :
++ src.propExt;
++ }
++ }
++ };
++
++
++ _Api.registerPlural = _api_registerPlural = function ( pluralName, singularName, val ) {
++ _Api.register( pluralName, val );
++
++ _Api.register( singularName, function () {
++ var ret = val.apply( this, arguments );
++
++ if ( ret === this ) {
++ // Returned item is the API instance that was passed in, return it
++ return this;
++ }
++ else if ( ret instanceof _Api ) {
++ // New API instance returned, want the value from the first item
++ // in the returned array for the singular result.
++ return ret.length ?
++ $.isArray( ret[0] ) ?
++ new _Api( ret.context, ret[0] ) : // Array results are 'enhanced'
++ ret[0] :
++ undefined;
++ }
++
++ // Non-API return - just fire it back
++ return ret;
++ } );
++ };
++
++
++ /**
++ * Selector for HTML tables. Apply the given selector to the give array of
++ * DataTables settings objects.
++ *
++ * @param {string|integer} [selector] jQuery selector string or integer
++ * @param {array} Array of DataTables settings objects to be filtered
++ * @return {array}
++ * @ignore
++ */
++ var __table_selector = function ( selector, a )
++ {
++ // Integer is used to pick out a table by index
++ if ( typeof selector === 'number' ) {
++ return [ a[ selector ] ];
++ }
++
++ // Perform a jQuery selector on the table nodes
++ var nodes = $.map( a, function (el, i) {
++ return el.nTable;
++ } );
++
++ return $(nodes)
++ .filter( selector )
++ .map( function (i) {
++ // Need to translate back from the table node to the settings
++ var idx = $.inArray( this, nodes );
++ return a[ idx ];
++ } )
++ .toArray();
++ };
++
++
++
++ /**
++ * Context selector for the API's context (i.e. the tables the API instance
++ * refers to.
++ *
++ * @name DataTable.Api#tables
++ * @param {string|integer} [selector] Selector to pick which tables the iterator
++ * should operate on. If not given, all tables in the current context are
++ * used. This can be given as a jQuery selector (for example `':gt(0)'`) to
++ * select multiple tables or as an integer to select a single table.
++ * @returns {DataTable.Api} Returns a new API instance if a selector is given.
++ */
++ _api_register( 'tables()', function ( selector ) {
++ // A new instance is created if there was a selector specified
++ return selector ?
++ new _Api( __table_selector( selector, this.context ) ) :
++ this;
++ } );
++
++
++ _api_register( 'table()', function ( selector ) {
++ var tables = this.tables( selector );
++ var ctx = tables.context;
++
++ // Truncate to the first matched table
++ return ctx.length ?
++ new _Api( ctx[0] ) :
++ tables;
++ } );
++
++
++ _api_registerPlural( 'tables().nodes()', 'table().node()' , function () {
++ return this.iterator( 'table', function ( ctx ) {
++ return ctx.nTable;
++ }, 1 );
++ } );
++
++
++ _api_registerPlural( 'tables().body()', 'table().body()' , function () {
++ return this.iterator( 'table', function ( ctx ) {
++ return ctx.nTBody;
++ }, 1 );
++ } );
++
++
++ _api_registerPlural( 'tables().header()', 'table().header()' , function () {
++ return this.iterator( 'table', function ( ctx ) {
++ return ctx.nTHead;
++ }, 1 );
++ } );
++
++
++ _api_registerPlural( 'tables().footer()', 'table().footer()' , function () {
++ return this.iterator( 'table', function ( ctx ) {
++ return ctx.nTFoot;
++ }, 1 );
++ } );
++
++
++ _api_registerPlural( 'tables().containers()', 'table().container()' , function () {
++ return this.iterator( 'table', function ( ctx ) {
++ return ctx.nTableWrapper;
++ }, 1 );
++ } );
++
++
++
++ /**
++ * Redraw the tables in the current context.
++ *
++ * @param {boolean} [reset=true] Reset (default) or hold the current paging
++ * position. A full re-sort and re-filter is performed when this method is
++ * called, which is why the pagination reset is the default action.
++ * @returns {DataTables.Api} this
++ */
++ _api_register( 'draw()', function ( resetPaging ) {
++ return this.iterator( 'table', function ( settings ) {
++ _fnReDraw( settings, resetPaging===false );
++ } );
++ } );
++
++
++
++ /**
++ * Get the current page index.
++ *
++ * @return {integer} Current page index (zero based)
++ *//**
++ * Set the current page.
++ *
++ * Note that if you attempt to show a page which does not exist, DataTables will
++ * not throw an error, but rather reset the paging.
++ *
++ * @param {integer|string} action The paging action to take. This can be one of:
++ * * `integer` - The page index to jump to
++ * * `string` - An action to take:
++ * * `first` - Jump to first page.
++ * * `next` - Jump to the next page
++ * * `previous` - Jump to previous page
++ * * `last` - Jump to the last page.
++ * @returns {DataTables.Api} this
++ */
++ _api_register( 'page()', function ( action ) {
++ if ( action === undefined ) {
++ return this.page.info().page; // not an expensive call
++ }
++
++ // else, have an action to take on all tables
++ return this.iterator( 'table', function ( settings ) {
++ _fnPageChange( settings, action );
++ } );
++ } );
++
++
++ /**
++ * Paging information for the first table in the current context.
++ *
++ * If you require paging information for another table, use the `table()` method
++ * with a suitable selector.
++ *
++ * @return {object} Object with the following properties set:
++ * * `page` - Current page index (zero based - i.e. the first page is `0`)
++ * * `pages` - Total number of pages
++ * * `start` - Display index for the first record shown on the current page
++ * * `end` - Display index for the last record shown on the current page
++ * * `length` - Display length (number of records). Note that generally `start
++ * + length = end`, but this is not always true, for example if there are
++ * only 2 records to show on the final page, with a length of 10.
++ * * `recordsTotal` - Full data set length
++ * * `recordsDisplay` - Data set length once the current filtering criterion
++ * are applied.
++ */
++ _api_register( 'page.info()', function ( action ) {
++ if ( this.context.length === 0 ) {
++ return undefined;
++ }
++
++ var
++ settings = this.context[0],
++ start = settings._iDisplayStart,
++ len = settings._iDisplayLength,
++ visRecords = settings.fnRecordsDisplay(),
++ all = len === -1;
++
++ return {
++ "page": all ? 0 : Math.floor( start / len ),
++ "pages": all ? 1 : Math.ceil( visRecords / len ),
++ "start": start,
++ "end": settings.fnDisplayEnd(),
++ "length": len,
++ "recordsTotal": settings.fnRecordsTotal(),
++ "recordsDisplay": visRecords
++ };
++ } );
++
++
++ /**
++ * Get the current page length.
++ *
++ * @return {integer} Current page length. Note `-1` indicates that all records
++ * are to be shown.
++ *//**
++ * Set the current page length.
++ *
++ * @param {integer} Page length to set. Use `-1` to show all records.
++ * @returns {DataTables.Api} this
++ */
++ _api_register( 'page.len()', function ( len ) {
++ // Note that we can't call this function 'length()' because `length`
++ // is a Javascript property of functions which defines how many arguments
++ // the function expects.
++ if ( len === undefined ) {
++ return this.context.length !== 0 ?
++ this.context[0]._iDisplayLength :
++ undefined;
++ }
++
++ // else, set the page length
++ return this.iterator( 'table', function ( settings ) {
++ _fnLengthChange( settings, len );
++ } );
++ } );
++
++
++
++ var __reload = function ( settings, holdPosition, callback ) {
++ // Use the draw event to trigger a callback
++ if ( callback ) {
++ var api = new _Api( settings );
++
++ api.one( 'draw', function () {
++ callback( api.ajax.json() );
++ } );
++ }
++
++ if ( _fnDataSource( settings ) == 'ssp' ) {
++ _fnReDraw( settings, holdPosition );
++ }
++ else {
++ // Trigger xhr
++ _fnProcessingDisplay( settings, true );
++
++ _fnBuildAjax( settings, [], function( json ) {
++ _fnClearTable( settings );
++
++ var data = _fnAjaxDataSrc( settings, json );
++ for ( var i=0, ien=data.length ; i<ien ; i++ ) {
++ _fnAddData( settings, data[i] );
++ }
++
++ _fnReDraw( settings, holdPosition );
++ _fnProcessingDisplay( settings, false );
++ } );
++ }
++ };
++
++
++ /**
++ * Get the JSON response from the last Ajax request that DataTables made to the
++ * server. Note that this returns the JSON from the first table in the current
++ * context.
++ *
++ * @return {object} JSON received from the server.
++ */
++ _api_register( 'ajax.json()', function () {
++ var ctx = this.context;
++
++ if ( ctx.length > 0 ) {
++ return ctx[0].json;
++ }
++
++ // else return undefined;
++ } );
++
++
++ /**
++ * Get the data submitted in the last Ajax request
++ */
++ _api_register( 'ajax.params()', function () {
++ var ctx = this.context;
++
++ if ( ctx.length > 0 ) {
++ return ctx[0].oAjaxData;
++ }
++
++ // else return undefined;
++ } );
++
++
++ /**
++ * Reload tables from the Ajax data source. Note that this function will
++ * automatically re-draw the table when the remote data has been loaded.
++ *
++ * @param {boolean} [reset=true] Reset (default) or hold the current paging
++ * position. A full re-sort and re-filter is performed when this method is
++ * called, which is why the pagination reset is the default action.
++ * @returns {DataTables.Api} this
++ */
++ _api_register( 'ajax.reload()', function ( callback, resetPaging ) {
++ return this.iterator( 'table', function (settings) {
++ __reload( settings, resetPaging===false, callback );
++ } );
++ } );
++
++
++ /**
++ * Get the current Ajax URL. Note that this returns the URL from the first
++ * table in the current context.
++ *
++ * @return {string} Current Ajax source URL
++ *//**
++ * Set the Ajax URL. Note that this will set the URL for all tables in the
++ * current context.
++ *
++ * @param {string} url URL to set.
++ * @returns {DataTables.Api} this
++ */
++ _api_register( 'ajax.url()', function ( url ) {
++ var ctx = this.context;
++
++ if ( url === undefined ) {
++ // get
++ if ( ctx.length === 0 ) {
++ return undefined;
++ }
++ ctx = ctx[0];
++
++ return ctx.ajax ?
++ $.isPlainObject( ctx.ajax ) ?
++ ctx.ajax.url :
++ ctx.ajax :
++ ctx.sAjaxSource;
++ }
++
++ // set
++ return this.iterator( 'table', function ( settings ) {
++ if ( $.isPlainObject( settings.ajax ) ) {
++ settings.ajax.url = url;
++ }
++ else {
++ settings.ajax = url;
++ }
++ // No need to consider sAjaxSource here since DataTables gives priority
++ // to `ajax` over `sAjaxSource`. So setting `ajax` here, renders any
++ // value of `sAjaxSource` redundant.
++ } );
++ } );
++
++
++ /**
++ * Load data from the newly set Ajax URL. Note that this method is only
++ * available when `ajax.url()` is used to set a URL. Additionally, this method
++ * has the same effect as calling `ajax.reload()` but is provided for
++ * convenience when setting a new URL. Like `ajax.reload()` it will
++ * automatically redraw the table once the remote data has been loaded.
++ *
++ * @returns {DataTables.Api} this
++ */
++ _api_register( 'ajax.url().load()', function ( callback, resetPaging ) {
++ // Same as a reload, but makes sense to present it for easy access after a
++ // url change
++ return this.iterator( 'table', function ( ctx ) {
++ __reload( ctx, resetPaging===false, callback );
++ } );
++ } );
++
++
++
++
++ var _selector_run = function ( type, selector, selectFn, settings, opts )
++ {
++ var
++ out = [], res,
++ a, i, ien, j, jen,
++ selectorType = typeof selector;
++
++ // Can't just check for isArray here, as an API or jQuery instance might be
++ // given with their array like look
++ if ( ! selector || selectorType === 'string' || selectorType === 'function' || selector.length === undefined ) {
++ selector = [ selector ];
++ }
++
++ for ( i=0, ien=selector.length ; i<ien ; i++ ) {
++ a = selector[i] && selector[i].split ?
++ selector[i].split(',') :
++ [ selector[i] ];
++
++ for ( j=0, jen=a.length ; j<jen ; j++ ) {
++ res = selectFn( typeof a[j] === 'string' ? $.trim(a[j]) : a[j] );
++
++ if ( res && res.length ) {
++ out.push.apply( out, res );
++ }
++ }
++ }
++
++ // selector extensions
++ var ext = _ext.selector[ type ];
++ if ( ext.length ) {
++ for ( i=0, ien=ext.length ; i<ien ; i++ ) {
++ out = ext[i]( settings, opts, out );
++ }
++ }
++
++ return out;
++ };
++
++
++ var _selector_opts = function ( opts )
++ {
++ if ( ! opts ) {
++ opts = {};
++ }
++
++ // Backwards compatibility for 1.9- which used the terminology filter rather
++ // than search
++ if ( opts.filter && opts.search === undefined ) {
++ opts.search = opts.filter;
++ }
++
++ return $.extend( {
++ search: 'none',
++ order: 'current',
++ page: 'all'
++ }, opts );
++ };
++
++
++ var _selector_first = function ( inst )
++ {
++ // Reduce the API instance to the first item found
++ for ( var i=0, ien=inst.length ; i<ien ; i++ ) {
++ if ( inst[i].length > 0 ) {
++ // Assign the first element to the first item in the instance
++ // and truncate the instance and context
++ inst[0] = inst[i];
++ inst[0].length = 1;
++ inst.length = 1;
++ inst.context = [ inst.context[i] ];
++
++ return inst;
++ }
++ }
++
++ // Not found - return an empty instance
++ inst.length = 0;
++ return inst;
++ };
++
++
++ var _selector_row_indexes = function ( settings, opts )
++ {
++ var
++ i, ien, tmp, a=[],
++ displayFiltered = settings.aiDisplay,
++ displayMaster = settings.aiDisplayMaster;
++
++ var
++ search = opts.search, // none, applied, removed
++ order = opts.order, // applied, current, index (original - compatibility with 1.9)
++ page = opts.page; // all, current
++
++ if ( _fnDataSource( settings ) == 'ssp' ) {
++ // In server-side processing mode, most options are irrelevant since
++ // rows not shown don't exist and the index order is the applied order
++ // Removed is a special case - for consistency just return an empty
++ // array
++ return search === 'removed' ?
++ [] :
++ _range( 0, displayMaster.length );
++ }
++ else if ( page == 'current' ) {
++ // Current page implies that order=current and fitler=applied, since it is
++ // fairly senseless otherwise, regardless of what order and search actually
++ // are
++ for ( i=settings._iDisplayStart, ien=settings.fnDisplayEnd() ; i<ien ; i++ ) {
++ a.push( displayFiltered[i] );
++ }
++ }
++ else if ( order == 'current' || order == 'applied' ) {
++ a = search == 'none' ?
++ displayMaster.slice() : // no search
++ search == 'applied' ?
++ displayFiltered.slice() : // applied search
++ $.map( displayMaster, function (el, i) { // removed search
++ return $.inArray( el, displayFiltered ) === -1 ? el : null;
++ } );
++ }
++ else if ( order == 'index' || order == 'original' ) {
++ for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
++ if ( search == 'none' ) {
++ a.push( i );
++ }
++ else { // applied | removed
++ tmp = $.inArray( i, displayFiltered );
++
++ if ((tmp === -1 && search == 'removed') ||
++ (tmp >= 0 && search == 'applied') )
++ {
++ a.push( i );
++ }
++ }
++ }
++ }
++
++ return a;
++ };
++
++
++ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
++ * Rows
++ *
++ * {} - no selector - use all available rows
++ * {integer} - row aoData index
++ * {node} - TR node
++ * {string} - jQuery selector to apply to the TR elements
++ * {array} - jQuery array of nodes, or simply an array of TR nodes
++ *
++ */
++
++
++ var __row_selector = function ( settings, selector, opts )
++ {
++ var run = function ( sel ) {
++ var selInt = _intVal( sel );
++ var i, ien;
++
++ // Short cut - selector is a number and no options provided (default is
++ // all records, so no need to check if the index is in there, since it
++ // must be - dev error if the index doesn't exist).
++ if ( selInt !== null && ! opts ) {
++ return [ selInt ];
++ }
++
++ var rows = _selector_row_indexes( settings, opts );
++
++ if ( selInt !== null && $.inArray( selInt, rows ) !== -1 ) {
++ // Selector - integer
++ return [ selInt ];
++ }
++ else if ( ! sel ) {
++ // Selector - none
++ return rows;
++ }
++
++ // Selector - function
++ if ( typeof sel === 'function' ) {
++ return $.map( rows, function (idx) {
++ var row = settings.aoData[ idx ];
++ return sel( idx, row._aData, row.nTr ) ? idx : null;
++ } );
++ }
++
++ // Get nodes in the order from the `rows` array with null values removed
++ var nodes = _removeEmpty(
++ _pluck_order( settings.aoData, rows, 'nTr' )
++ );
++
++ // Selector - node
++ if ( sel.nodeName ) {
++ if ( $.inArray( sel, nodes ) !== -1 ) {
++ return [ sel._DT_RowIndex ]; // sel is a TR node that is in the table
++ // and DataTables adds a prop for fast lookup
++ }
++ }
++
++ // Selector - jQuery selector string, array of nodes or jQuery object/
++ // As jQuery's .filter() allows jQuery objects to be passed in filter,
++ // it also allows arrays, so this will cope with all three options
++ return $(nodes)
++ .filter( sel )
++ .map( function () {
++ return this._DT_RowIndex;
++ } )
++ .toArray();
++ };
++
++ return _selector_run( 'row', selector, run, settings, opts );
++ };
++
++
++ _api_register( 'rows()', function ( selector, opts ) {
++ // argument shifting
++ if ( selector === undefined ) {
++ selector = '';
++ }
++ else if ( $.isPlainObject( selector ) ) {
++ opts = selector;
++ selector = '';
++ }
++
++ opts = _selector_opts( opts );
++
++ var inst = this.iterator( 'table', function ( settings ) {
++ return __row_selector( settings, selector, opts );
++ }, 1 );
++
++ // Want argument shifting here and in __row_selector?
++ inst.selector.rows = selector;
++ inst.selector.opts = opts;
++
++ return inst;
++ } );
++
++ _api_register( 'rows().nodes()', function () {
++ return this.iterator( 'row', function ( settings, row ) {
++ return settings.aoData[ row ].nTr || undefined;
++ }, 1 );
++ } );
++
++ _api_register( 'rows().data()', function () {
++ return this.iterator( true, 'rows', function ( settings, rows ) {
++ return _pluck_order( settings.aoData, rows, '_aData' );
++ }, 1 );
++ } );
++
++ _api_registerPlural( 'rows().cache()', 'row().cache()', function ( type ) {
++ return this.iterator( 'row', function ( settings, row ) {
++ var r = settings.aoData[ row ];
++ return type === 'search' ? r._aFilterData : r._aSortData;
++ }, 1 );
++ } );
++
++ _api_registerPlural( 'rows().invalidate()', 'row().invalidate()', function ( src ) {
++ return this.iterator( 'row', function ( settings, row ) {
++ _fnInvalidate( settings, row, src );
++ } );
++ } );
++
++ _api_registerPlural( 'rows().indexes()', 'row().index()', function () {
++ return this.iterator( 'row', function ( settings, row ) {
++ return row;
++ }, 1 );
++ } );
++
++ _api_registerPlural( 'rows().remove()', 'row().remove()', function () {
++ var that = this;
++
++ return this.iterator( 'row', function ( settings, row, thatIdx ) {
++ var data = settings.aoData;
++
++ data.splice( row, 1 );
++
++ // Update the _DT_RowIndex parameter on all rows in the table
++ for ( var i=0, ien=data.length ; i<ien ; i++ ) {
++ if ( data[i].nTr !== null ) {
++ data[i].nTr._DT_RowIndex = i;
++ }
++ }
++
++ // Remove the target row from the search array
++ var displayIndex = $.inArray( row, settings.aiDisplay );
++
++ // Delete from the display arrays
++ _fnDeleteIndex( settings.aiDisplayMaster, row );
++ _fnDeleteIndex( settings.aiDisplay, row );
++ _fnDeleteIndex( that[ thatIdx ], row, false ); // maintain local indexes
++
++ // Check for an 'overflow' they case for displaying the table
++ _fnLengthOverflow( settings );
++ } );
++ } );
++
++
++ _api_register( 'rows.add()', function ( rows ) {
++ var newRows = this.iterator( 'table', function ( settings ) {
++ var row, i, ien;
++ var out = [];
++
++ for ( i=0, ien=rows.length ; i<ien ; i++ ) {
++ row = rows[i];
++
++ if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
++ out.push( _fnAddTr( settings, row )[0] );
++ }
++ else {
++ out.push( _fnAddData( settings, row ) );
++ }
++ }
++
++ return out;
++ }, 1 );
++
++ // Return an Api.rows() extended instance, so rows().nodes() etc can be used
++ var modRows = this.rows( -1 );
++ modRows.pop();
++ modRows.push.apply( modRows, newRows.toArray() );
++
++ return modRows;
++ } );
++
++
++
++
++
++ /**
++ *
++ */
++ _api_register( 'row()', function ( selector, opts ) {
++ return _selector_first( this.rows( selector, opts ) );
++ } );
++
++
++ _api_register( 'row().data()', function ( data ) {
++ var ctx = this.context;
++
++ if ( data === undefined ) {
++ // Get
++ return ctx.length && this.length ?
++ ctx[0].aoData[ this[0] ]._aData :
++ undefined;
++ }
++
++ // Set
++ ctx[0].aoData[ this[0] ]._aData = data;
++
++ // Automatically invalidate
++ _fnInvalidate( ctx[0], this[0], 'data' );
++
++ return this;
++ } );
++
++
++ _api_register( 'row().node()', function () {
++ var ctx = this.context;
++
++ return ctx.length && this.length ?
++ ctx[0].aoData[ this[0] ].nTr || null :
++ null;
++ } );
++
++
++ _api_register( 'row.add()', function ( row ) {
++ // Allow a jQuery object to be passed in - only a single row is added from
++ // it though - the first element in the set
++ if ( row instanceof $ && row.length ) {
++ row = row[0];
++ }
++
++ var rows = this.iterator( 'table', function ( settings ) {
++ if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
++ return _fnAddTr( settings, row )[0];
++ }
++ return _fnAddData( settings, row );
++ } );
++
++ // Return an Api.rows() extended instance, with the newly added row selected
++ return this.row( rows[0] );
++ } );
++
++
++
++ var __details_add = function ( ctx, row, data, klass )
++ {
++ // Convert to array of TR elements
++ var rows = [];
++ var addRow = function ( r, k ) {
++ // Recursion to allow for arrays of jQuery objects
++ if ( $.isArray( r ) || r instanceof $ ) {
++ for ( var i=0, ien=r.length ; i<ien ; i++ ) {
++ addRow( r[i], k );
++ }
++ return;
++ }
++
++ // If we get a TR element, then just add it directly - up to the dev
++ // to add the correct number of columns etc
++ if ( r.nodeName && r.nodeName.toLowerCase() === 'tr' ) {
++ rows.push( r );
++ }
++ else {
++ // Otherwise create a row with a wrapper
++ var created = $('<tr><td/></tr>').addClass( k );
++ $('td', created)
++ .addClass( k )
++ .html( r )
++ [0].colSpan = _fnVisbleColumns( ctx );
++
++ rows.push( created[0] );
++ }
++ };
++
++ addRow( data, klass );
++
++ if ( row._details ) {
++ row._details.remove();
++ }
++
++ row._details = $(rows);
++
++ // If the children were already shown, that state should be retained
++ if ( row._detailsShow ) {
++ row._details.insertAfter( row.nTr );
++ }
++ };
++
++
++ var __details_remove = function ( api, idx )
++ {
++ var ctx = api.context;
++
++ if ( ctx.length ) {
++ var row = ctx[0].aoData[ idx !== undefined ? idx : api[0] ];
++
++ if ( row._details ) {
++ row._details.remove();
++
++ row._detailsShow = undefined;
++ row._details = undefined;
++ }
++ }
++ };
++
++
++ var __details_display = function ( api, show ) {
++ var ctx = api.context;
++
++ if ( ctx.length && api.length ) {
++ var row = ctx[0].aoData[ api[0] ];
++
++ if ( row._details ) {
++ row._detailsShow = show;
++
++ if ( show ) {
++ row._details.insertAfter( row.nTr );
++ }
++ else {
++ row._details.detach();
++ }
++
++ __details_events( ctx[0] );
++ }
++ }
++ };
++
++
++ var __details_events = function ( settings )
++ {
++ var api = new _Api( settings );
++ var namespace = '.dt.DT_details';
++ var drawEvent = 'draw'+namespace;
++ var colvisEvent = 'column-visibility'+namespace;
++ var destroyEvent = 'destroy'+namespace;
++ var data = settings.aoData;
++
++ api.off( drawEvent +' '+ colvisEvent +' '+ destroyEvent );
++
++ if ( _pluck( data, '_details' ).length > 0 ) {
++ // On each draw, insert the required elements into the document
++ api.on( drawEvent, function ( e, ctx ) {
++ if ( settings !== ctx ) {
++ return;
++ }
++
++ api.rows( {page:'current'} ).eq(0).each( function (idx) {
++ // Internal data grab
++ var row = data[ idx ];
++
++ if ( row._detailsShow ) {
++ row._details.insertAfter( row.nTr );
++ }
++ } );
++ } );
++
++ // Column visibility change - update the colspan
++ api.on( colvisEvent, function ( e, ctx, idx, vis ) {
++ if ( settings !== ctx ) {
++ return;
++ }
++
++ // Update the colspan for the details rows (note, only if it already has
++ // a colspan)
++ var row, visible = _fnVisbleColumns( ctx );
++
++ for ( var i=0, ien=data.length ; i<ien ; i++ ) {
++ row = data[i];
++
++ if ( row._details ) {
++ row._details.children('td[colspan]').attr('colspan', visible );
++ }
++ }
++ } );
++
++ // Table destroyed - nuke any child rows
++ api.on( destroyEvent, function ( e, ctx ) {
++ if ( settings !== ctx ) {
++ return;
++ }
++
++ for ( var i=0, ien=data.length ; i<ien ; i++ ) {
++ if ( data[i]._details ) {
++ __details_remove( api, i );
++ }
++ }
++ } );
++ }
++ };
++
++ // Strings for the method names to help minification
++ var _emp = '';
++ var _child_obj = _emp+'row().child';
++ var _child_mth = _child_obj+'()';
++
++ // data can be:
++ // tr
++ // string
++ // jQuery or array of any of the above
++ _api_register( _child_mth, function ( data, klass ) {
++ var ctx = this.context;
++
++ if ( data === undefined ) {
++ // get
++ return ctx.length && this.length ?
++ ctx[0].aoData[ this[0] ]._details :
++ undefined;
++ }
++ else if ( data === true ) {
++ // show
++ this.child.show();
++ }
++ else if ( data === false ) {
++ // remove
++ __details_remove( this );
++ }
++ else if ( ctx.length && this.length ) {
++ // set
++ __details_add( ctx[0], ctx[0].aoData[ this[0] ], data, klass );
++ }
++
++ return this;
++ } );
++
++
++ _api_register( [
++ _child_obj+'.show()',
++ _child_mth+'.show()' // only when `child()` was called with parameters (without
++ ], function ( show ) { // it returns an object and this method is not executed)
++ __details_display( this, true );
++ return this;
++ } );
++
++
++ _api_register( [
++ _child_obj+'.hide()',
++ _child_mth+'.hide()' // only when `child()` was called with parameters (without
++ ], function () { // it returns an object and this method is not executed)
++ __details_display( this, false );
++ return this;
++ } );
++
++
++ _api_register( [
++ _child_obj+'.remove()',
++ _child_mth+'.remove()' // only when `child()` was called with parameters (without
++ ], function () { // it returns an object and this method is not executed)
++ __details_remove( this );
++ return this;
++ } );
++
++
++ _api_register( _child_obj+'.isShown()', function () {
++ var ctx = this.context;
++
++ if ( ctx.length && this.length ) {
++ // _detailsShown as false or undefined will fall through to return false
++ return ctx[0].aoData[ this[0] ]._detailsShow || false;
++ }
++ return false;
++ } );
++
++
++
++ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
++ * Columns
++ *
++ * {integer} - column index (>=0 count from left, <0 count from right)
++ * "{integer}:visIdx" - visible column index (i.e. translate to column index) (>=0 count from left, <0 count from right)
++ * "{integer}:visible" - alias for {integer}:visIdx (>=0 count from left, <0 count from right)
++ * "{string}:name" - column name
++ * "{string}" - jQuery selector on column header nodes
++ *
++ */
++
++ // can be an array of these items, comma separated list, or an array of comma
++ // separated lists
++
++ var __re_column_selector = /^(.+):(name|visIdx|visible)$/;
++
++
++ // r1 and r2 are redundant - but it means that the parameters match for the
++ // iterator callback in columns().data()
++ var __columnData = function ( settings, column, r1, r2, rows ) {
++ var a = [];
++ for ( var row=0, ien=rows.length ; row<ien ; row++ ) {
++ a.push( _fnGetCellData( settings, rows[row], column ) );
++ }
++ return a;
++ };
++
++
++ var __column_selector = function ( settings, selector, opts )
++ {
++ var
++ columns = settings.aoColumns,
++ names = _pluck( columns, 'sName' ),
++ nodes = _pluck( columns, 'nTh' );
++
++ var run = function ( s ) {
++ var selInt = _intVal( s );
++
++ // Selector - all
++ if ( s === '' ) {
++ return _range( columns.length );
++ }
++
++ // Selector - index
++ if ( selInt !== null ) {
++ return [ selInt >= 0 ?
++ selInt : // Count from left
++ columns.length + selInt // Count from right (+ because its a negative value)
++ ];
++ }
++
++ // Selector = function
++ if ( typeof s === 'function' ) {
++ var rows = _selector_row_indexes( settings, opts );
++
++ return $.map( columns, function (col, idx) {
++ return s(
++ idx,
++ __columnData( settings, idx, 0, 0, rows ),
++ nodes[ idx ]
++ ) ? idx : null;
++ } );
++ }
++
++ // jQuery or string selector
++ var match = typeof s === 'string' ?
++ s.match( __re_column_selector ) :
++ '';
++
++ if ( match ) {
++ switch( match[2] ) {
++ case 'visIdx':
++ case 'visible':
++ var idx = parseInt( match[1], 10 );
++ // Visible index given, convert to column index
++ if ( idx < 0 ) {
++ // Counting from the right
++ var visColumns = $.map( columns, function (col,i) {
++ return col.bVisible ? i : null;
++ } );
++ return [ visColumns[ visColumns.length + idx ] ];
++ }
++ // Counting from the left
++ return [ _fnVisibleToColumnIndex( settings, idx ) ];
++
++ case 'name':
++ // match by name. `names` is column index complete and in order
++ return $.map( names, function (name, i) {
++ return name === match[1] ? i : null;
++ } );
++ }
++ }
++ else {
++ // jQuery selector on the TH elements for the columns
++ return $( nodes )
++ .filter( s )
++ .map( function () {
++ return $.inArray( this, nodes ); // `nodes` is column index complete and in order
++ } )
++ .toArray();
++ }
++ };
++
++ return _selector_run( 'column', selector, run, settings, opts );
++ };
++
++
++ var __setColumnVis = function ( settings, column, vis, recalc ) {
++ var
++ cols = settings.aoColumns,
++ col = cols[ column ],
++ data = settings.aoData,
++ row, cells, i, ien, tr;
++
++ // Get
++ if ( vis === undefined ) {
++ return col.bVisible;
++ }
++
++ // Set
++ // No change
++ if ( col.bVisible === vis ) {
++ return;
++ }
++
++ if ( vis ) {
++ // Insert column
++ // Need to decide if we should use appendChild or insertBefore
++ var insertBefore = $.inArray( true, _pluck(cols, 'bVisible'), column+1 );
++
++ for ( i=0, ien=data.length ; i<ien ; i++ ) {
++ tr = data[i].nTr;
++ cells = data[i].anCells;
++
++ if ( tr ) {
++ // insertBefore can act like appendChild if 2nd arg is null
++ tr.insertBefore( cells[ column ], cells[ insertBefore ] || null );
++ }
++ }
++ }
++ else {
++ // Remove column
++ $( _pluck( settings.aoData, 'anCells', column ) ).detach();
++ }
++
++ // Common actions
++ col.bVisible = vis;
++ _fnDrawHead( settings, settings.aoHeader );
++ _fnDrawHead( settings, settings.aoFooter );
++
++ if ( recalc === undefined || recalc ) {
++ // Automatically adjust column sizing
++ _fnAdjustColumnSizing( settings );
++
++ // Realign columns for scrolling
++ if ( settings.oScroll.sX || settings.oScroll.sY ) {
++ _fnScrollDraw( settings );
++ }
++ }
++
++ _fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis] );
++
++ _fnSaveState( settings );
++ };
++
++
++ _api_register( 'columns()', function ( selector, opts ) {
++ // argument shifting
++ if ( selector === undefined ) {
++ selector = '';
++ }
++ else if ( $.isPlainObject( selector ) ) {
++ opts = selector;
++ selector = '';
++ }
++
++ opts = _selector_opts( opts );
++
++ var inst = this.iterator( 'table', function ( settings ) {
++ return __column_selector( settings, selector, opts );
++ }, 1 );
++
++ // Want argument shifting here and in _row_selector?
++ inst.selector.cols = selector;
++ inst.selector.opts = opts;
++
++ return inst;
++ } );
++
++ _api_registerPlural( 'columns().header()', 'column().header()', function ( selector, opts ) {
++ return this.iterator( 'column', function ( settings, column ) {
++ return settings.aoColumns[column].nTh;
++ }, 1 );
++ } );
++
++ _api_registerPlural( 'columns().footer()', 'column().footer()', function ( selector, opts ) {
++ return this.iterator( 'column', function ( settings, column ) {
++ return settings.aoColumns[column].nTf;
++ }, 1 );
++ } );
++
++ _api_registerPlural( 'columns().data()', 'column().data()', function () {
++ return this.iterator( 'column-rows', __columnData, 1 );
++ } );
++
++ _api_registerPlural( 'columns().dataSrc()', 'column().dataSrc()', function () {
++ return this.iterator( 'column', function ( settings, column ) {
++ return settings.aoColumns[column].mData;
++ }, 1 );
++ } );
++
++ _api_registerPlural( 'columns().cache()', 'column().cache()', function ( type ) {
++ return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
++ return _pluck_order( settings.aoData, rows,
++ type === 'search' ? '_aFilterData' : '_aSortData', column
++ );
++ }, 1 );
++ } );
++
++ _api_registerPlural( 'columns().nodes()', 'column().nodes()', function () {
++ return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
++ return _pluck_order( settings.aoData, rows, 'anCells', column ) ;
++ }, 1 );
++ } );
++
++ _api_registerPlural( 'columns().visible()', 'column().visible()', function ( vis, calc ) {
++ return this.iterator( 'column', function ( settings, column ) {
++ if ( vis === undefined ) {
++ return settings.aoColumns[ column ].bVisible;
++ } // else
++ __setColumnVis( settings, column, vis, calc );
++ } );
++ } );
++
++ _api_registerPlural( 'columns().indexes()', 'column().index()', function ( type ) {
++ return this.iterator( 'column', function ( settings, column ) {
++ return type === 'visible' ?
++ _fnColumnIndexToVisible( settings, column ) :
++ column;
++ }, 1 );
++ } );
++
++ _api_register( 'columns.adjust()', function () {
++ return this.iterator( 'table', function ( settings ) {
++ _fnAdjustColumnSizing( settings );
++ }, 1 );
++ } );
++
++ _api_register( 'column.index()', function ( type, idx ) {
++ if ( this.context.length !== 0 ) {
++ var ctx = this.context[0];
++
++ if ( type === 'fromVisible' || type === 'toData' ) {
++ return _fnVisibleToColumnIndex( ctx, idx );
++ }
++ else if ( type === 'fromData' || type === 'toVisible' ) {
++ return _fnColumnIndexToVisible( ctx, idx );
++ }
++ }
++ } );
++
++ _api_register( 'column()', function ( selector, opts ) {
++ return _selector_first( this.columns( selector, opts ) );
++ } );
++
++
++
++
++ var __cell_selector = function ( settings, selector, opts )
++ {
++ var data = settings.aoData;
++ var rows = _selector_row_indexes( settings, opts );
++ var cells = _removeEmpty( _pluck_order( data, rows, 'anCells' ) );
++ var allCells = $( [].concat.apply([], cells) );
++ var row;
++ var columns = settings.aoColumns.length;
++ var a, i, ien, j, o, host;
++
++ var run = function ( s ) {
++ var fnSelector = typeof s === 'function';
++
++ if ( s === null || s === undefined || fnSelector ) {
++ // All cells and function selectors
++ a = [];
++
++ for ( i=0, ien=rows.length ; i<ien ; i++ ) {
++ row = rows[i];
++
++ for ( j=0 ; j<columns ; j++ ) {
++ o = {
++ row: row,
++ column: j
++ };
++
++ if ( fnSelector ) {
++ // Selector - function
++ host = settings.aoData[ row ];
++
++ if ( s( o, _fnGetCellData(settings, row, j), host.anCells ? host.anCells[j] : null ) ) {
++ a.push( o );
++ }
++ }
++ else {
++ // Selector - all
++ a.push( o );
++ }
++ }
++ }
++
++ return a;
++ }
++
++ // Selector - index
++ if ( $.isPlainObject( s ) ) {
++ return [s];
++ }
++
++ // Selector - jQuery filtered cells
++ return allCells
++ .filter( s )
++ .map( function (i, el) {
++ row = el.parentNode._DT_RowIndex;
++
++ return {
++ row: row,
++ column: $.inArray( el, data[ row ].anCells )
++ };
++ } )
++ .toArray();
++ };
++
++ return _selector_run( 'cell', selector, run, settings, opts );
++ };
++
++
++
++
++ _api_register( 'cells()', function ( rowSelector, columnSelector, opts ) {
++ // Argument shifting
++ if ( $.isPlainObject( rowSelector ) ) {
++ // Indexes
++ if ( rowSelector.row === undefined ) {
++ // Selector options in first parameter
++ opts = rowSelector;
++ rowSelector = null;
++ }
++ else {
++ // Cell index objects in first parameter
++ opts = columnSelector;
++ columnSelector = null;
++ }
++ }
++ if ( $.isPlainObject( columnSelector ) ) {
++ opts = columnSelector;
++ columnSelector = null;
++ }
++
++ // Cell selector
++ if ( columnSelector === null || columnSelector === undefined ) {
++ return this.iterator( 'table', function ( settings ) {
++ return __cell_selector( settings, rowSelector, _selector_opts( opts ) );
++ } );
++ }
++
++ // Row + column selector
++ var columns = this.columns( columnSelector, opts );
++ var rows = this.rows( rowSelector, opts );
++ var a, i, ien, j, jen;
++
++ var cells = this.iterator( 'table', function ( settings, idx ) {
++ a = [];
++
++ for ( i=0, ien=rows[idx].length ; i<ien ; i++ ) {
++ for ( j=0, jen=columns[idx].length ; j<jen ; j++ ) {
++ a.push( {
++ row: rows[idx][i],
++ column: columns[idx][j]
++ } );
++ }
++ }
++
++ return a;
++ }, 1 );
++
++ $.extend( cells.selector, {
++ cols: columnSelector,
++ rows: rowSelector,
++ opts: opts
++ } );
++
++ return cells;
++ } );
++
++
++ _api_registerPlural( 'cells().nodes()', 'cell().node()', function () {
++ return this.iterator( 'cell', function ( settings, row, column ) {
++ var cells = settings.aoData[ row ].anCells;
++ return cells ?
++ cells[ column ] :
++ undefined;
++ }, 1 );
++ } );
++
++
++ _api_register( 'cells().data()', function () {
++ return this.iterator( 'cell', function ( settings, row, column ) {
++ return _fnGetCellData( settings, row, column );
++ }, 1 );
++ } );
++
++
++ _api_registerPlural( 'cells().cache()', 'cell().cache()', function ( type ) {
++ type = type === 'search' ? '_aFilterData' : '_aSortData';
++
++ return this.iterator( 'cell', function ( settings, row, column ) {
++ return settings.aoData[ row ][ type ][ column ];
++ }, 1 );
++ } );
++
++
++ _api_registerPlural( 'cells().render()', 'cell().render()', function ( type ) {
++ return this.iterator( 'cell', function ( settings, row, column ) {
++ return _fnGetCellData( settings, row, column, type );
++ }, 1 );
++ } );
++
++
++ _api_registerPlural( 'cells().indexes()', 'cell().index()', function () {
++ return this.iterator( 'cell', function ( settings, row, column ) {
++ return {
++ row: row,
++ column: column,
++ columnVisible: _fnColumnIndexToVisible( settings, column )
++ };
++ }, 1 );
++ } );
++
++
++ _api_registerPlural( 'cells().invalidate()', 'cell().invalidate()', function ( src ) {
++ return this.iterator( 'cell', function ( settings, row, column ) {
++ _fnInvalidate( settings, row, src, column );
++ } );
++ } );
++
++
++
++ _api_register( 'cell()', function ( rowSelector, columnSelector, opts ) {
++ return _selector_first( this.cells( rowSelector, columnSelector, opts ) );
++ } );
++
++
++ _api_register( 'cell().data()', function ( data ) {
++ var ctx = this.context;
++ var cell = this[0];
++
++ if ( data === undefined ) {
++ // Get
++ return ctx.length && cell.length ?
++ _fnGetCellData( ctx[0], cell[0].row, cell[0].column ) :
++ undefined;
++ }
++
++ // Set
++ _fnSetCellData( ctx[0], cell[0].row, cell[0].column, data );
++ _fnInvalidate( ctx[0], cell[0].row, 'data', cell[0].column );
++
++ return this;
++ } );
++
++
++
++ /**
++ * Get current ordering (sorting) that has been applied to the table.
++ *
++ * @returns {array} 2D array containing the sorting information for the first
++ * table in the current context. Each element in the parent array represents
++ * a column being sorted upon (i.e. multi-sorting with two columns would have
++ * 2 inner arrays). The inner arrays may have 2 or 3 elements. The first is
++ * the column index that the sorting condition applies to, the second is the
++ * direction of the sort (`desc` or `asc`) and, optionally, the third is the
++ * index of the sorting order from the `column.sorting` initialisation array.
++ *//**
++ * Set the ordering for the table.
++ *
++ * @param {integer} order Column index to sort upon.
++ * @param {string} direction Direction of the sort to be applied (`asc` or `desc`)
++ * @returns {DataTables.Api} this
++ *//**
++ * Set the ordering for the table.
++ *
++ * @param {array} order 1D array of sorting information to be applied.
++ * @param {array} [...] Optional additional sorting conditions
++ * @returns {DataTables.Api} this
++ *//**
++ * Set the ordering for the table.
++ *
++ * @param {array} order 2D array of sorting information to be applied.
++ * @returns {DataTables.Api} this
++ */
++ _api_register( 'order()', function ( order, dir ) {
++ var ctx = this.context;
++
++ if ( order === undefined ) {
++ // get
++ return ctx.length !== 0 ?
++ ctx[0].aaSorting :
++ undefined;
++ }
++
++ // set
++ if ( typeof order === 'number' ) {
++ // Simple column / direction passed in
++ order = [ [ order, dir ] ];
++ }
++ else if ( ! $.isArray( order[0] ) ) {
++ // Arguments passed in (list of 1D arrays)
++ order = Array.prototype.slice.call( arguments );
++ }
++ // otherwise a 2D array was passed in
++
++ return this.iterator( 'table', function ( settings ) {
++ settings.aaSorting = order.slice();
++ } );
++ } );
++
++
++ /**
++ * Attach a sort listener to an element for a given column
++ *
++ * @param {node|jQuery|string} node Identifier for the element(s) to attach the
++ * listener to. This can take the form of a single DOM node, a jQuery
++ * collection of nodes or a jQuery selector which will identify the node(s).
++ * @param {integer} column the column that a click on this node will sort on
++ * @param {function} [callback] callback function when sort is run
++ * @returns {DataTables.Api} this
++ */
++ _api_register( 'order.listener()', function ( node, column, callback ) {
++ return this.iterator( 'table', function ( settings ) {
++ _fnSortAttachListener( settings, node, column, callback );
++ } );
++ } );
++
++
++ // Order by the selected column(s)
++ _api_register( [
++ 'columns().order()',
++ 'column().order()'
++ ], function ( dir ) {
++ var that = this;
++
++ return this.iterator( 'table', function ( settings, i ) {
++ var sort = [];
++
++ $.each( that[i], function (j, col) {
++ sort.push( [ col, dir ] );
++ } );
++
++ settings.aaSorting = sort;
++ } );
++ } );
++
++
++
++ _api_register( 'search()', function ( input, regex, smart, caseInsen ) {
++ var ctx = this.context;
++
++ if ( input === undefined ) {
++ // get
++ return ctx.length !== 0 ?
++ ctx[0].oPreviousSearch.sSearch :
++ undefined;
++ }
++
++ // set
++ return this.iterator( 'table', function ( settings ) {
++ if ( ! settings.oFeatures.bFilter ) {
++ return;
++ }
++
++ _fnFilterComplete( settings, $.extend( {}, settings.oPreviousSearch, {
++ "sSearch": input+"",
++ "bRegex": regex === null ? false : regex,
++ "bSmart": smart === null ? true : smart,
++ "bCaseInsensitive": caseInsen === null ? true : caseInsen
++ } ), 1 );
++ } );
++ } );
++
++
++ _api_registerPlural(
++ 'columns().search()',
++ 'column().search()',
++ function ( input, regex, smart, caseInsen ) {
++ return this.iterator( 'column', function ( settings, column ) {
++ var preSearch = settings.aoPreSearchCols;
++
++ if ( input === undefined ) {
++ // get
++ return preSearch[ column ].sSearch;
++ }
++
++ // set
++ if ( ! settings.oFeatures.bFilter ) {
++ return;
++ }
++
++ $.extend( preSearch[ column ], {
++ "sSearch": input+"",
++ "bRegex": regex === null ? false : regex,
++ "bSmart": smart === null ? true : smart,
++ "bCaseInsensitive": caseInsen === null ? true : caseInsen
++ } );
++
++ _fnFilterComplete( settings, settings.oPreviousSearch, 1 );
++ } );
++ }
++ );
++
++ /*
++ * State API methods
++ */
++
++ _api_register( 'state()', function () {
++ return this.context.length ?
++ this.context[0].oSavedState :
++ null;
++ } );
++
++
++ _api_register( 'state.clear()', function () {
++ return this.iterator( 'table', function ( settings ) {
++ // Save an empty object
++ settings.fnStateSaveCallback.call( settings.oInstance, settings, {} );
++ } );
++ } );
++
++
++ _api_register( 'state.loaded()', function () {
++ return this.context.length ?
++ this.context[0].oLoadedState :
++ null;
++ } );
++
++
++ _api_register( 'state.save()', function () {
++ return this.iterator( 'table', function ( settings ) {
++ _fnSaveState( settings );
++ } );
++ } );
++
++
++
++ /**
++ * Provide a common method for plug-ins to check the version of DataTables being
++ * used, in order to ensure compatibility.
++ *
++ * @param {string} version Version string to check for, in the format "X.Y.Z".
++ * Note that the formats "X" and "X.Y" are also acceptable.
++ * @returns {boolean} true if this version of DataTables is greater or equal to
++ * the required version, or false if this version of DataTales is not
++ * suitable
++ * @static
++ * @dtopt API-Static
++ *
++ * @example
++ * alert( $.fn.dataTable.versionCheck( '1.9.0' ) );
++ */
++ DataTable.versionCheck = DataTable.fnVersionCheck = function( version )
++ {
++ var aThis = DataTable.version.split('.');
++ var aThat = version.split('.');
++ var iThis, iThat;
++
++ for ( var i=0, iLen=aThat.length ; i<iLen ; i++ ) {
++ iThis = parseInt( aThis[i], 10 ) || 0;
++ iThat = parseInt( aThat[i], 10 ) || 0;
++
++ // Parts are the same, keep comparing
++ if (iThis === iThat) {
++ continue;
++ }
++
++ // Parts are different, return immediately
++ return iThis > iThat;
++ }
++
++ return true;
++ };
++
++
++ /**
++ * Check if a `<table>` node is a DataTable table already or not.
++ *
++ * @param {node|jquery|string} table Table node, jQuery object or jQuery
++ * selector for the table to test. Note that if more than more than one
++ * table is passed on, only the first will be checked
++ * @returns {boolean} true the table given is a DataTable, or false otherwise
++ * @static
++ * @dtopt API-Static
++ *
++ * @example
++ * if ( ! $.fn.DataTable.isDataTable( '#example' ) ) {
++ * $('#example').dataTable();
++ * }
++ */
++ DataTable.isDataTable = DataTable.fnIsDataTable = function ( table )
++ {
++ var t = $(table).get(0);
++ var is = false;
++
++ $.each( DataTable.settings, function (i, o) {
++ var head = o.nScrollHead ? $('table', o.nScrollHead)[0] : null;
++ var foot = o.nScrollFoot ? $('table', o.nScrollFoot)[0] : null;
++
++ if ( o.nTable === t || head === t || foot === t ) {
++ is = true;
++ }
++ } );
++
++ return is;
++ };
++
++
++ /**
++ * Get all DataTable tables that have been initialised - optionally you can
++ * select to get only currently visible tables.
++ *
++ * @param {boolean} [visible=false] Flag to indicate if you want all (default)
++ * or visible tables only.
++ * @returns {array} Array of `table` nodes (not DataTable instances) which are
++ * DataTables
++ * @static
++ * @dtopt API-Static
++ *
++ * @example
++ * $.each( $.fn.dataTable.tables(true), function () {
++ * $(table).DataTable().columns.adjust();
++ * } );
++ */
++ DataTable.tables = DataTable.fnTables = function ( visible )
++ {
++ return $.map( DataTable.settings, function (o) {
++ if ( !visible || (visible && $(o.nTable).is(':visible')) ) {
++ return o.nTable;
++ }
++ } );
++ };
++
++
++ /**
++ * DataTables utility methods
++ *
++ * This namespace provides helper methods that DataTables uses internally to
++ * create a DataTable, but which are not exclusively used only for DataTables.
++ * These methods can be used by extension authors to save the duplication of
++ * code.
++ *
++ * @namespace
++ */
++ DataTable.util = {
++ /**
++ * Throttle the calls to a function. Arguments and context are maintained
++ * for the throttled function.
++ *
++ * @param {function} fn Function to be called
++ * @param {integer} freq Call frequency in mS
++ * @return {function} Wrapped function
++ */
++ throttle: _fnThrottle,
++
++
++ /**
++ * Escape a string such that it can be used in a regular expression
++ *
++ * @param {string} sVal string to escape
++ * @returns {string} escaped string
++ */
++ escapeRegex: _fnEscapeRegex
++ };
++
++
++ /**
++ * Convert from camel case parameters to Hungarian notation. This is made public
++ * for the extensions to provide the same ability as DataTables core to accept
++ * either the 1.9 style Hungarian notation, or the 1.10+ style camelCase
++ * parameters.
++ *
++ * @param {object} src The model object which holds all parameters that can be
++ * mapped.
++ * @param {object} user The object to convert from camel case to Hungarian.
++ * @param {boolean} force When set to `true`, properties which already have a
++ * Hungarian value in the `user` object will be overwritten. Otherwise they
++ * won't be.
++ */
++ DataTable.camelToHungarian = _fnCamelToHungarian;
++
++
++
++ /**
++ *
++ */
++ _api_register( '$()', function ( selector, opts ) {
++ var
++ rows = this.rows( opts ).nodes(), // Get all rows
++ jqRows = $(rows);
++
++ return $( [].concat(
++ jqRows.filter( selector ).toArray(),
++ jqRows.find( selector ).toArray()
++ ) );
++ } );
++
++
++ // jQuery functions to operate on the tables
++ $.each( [ 'on', 'one', 'off' ], function (i, key) {
++ _api_register( key+'()', function ( /* event, handler */ ) {
++ var args = Array.prototype.slice.call(arguments);
++
++ // Add the `dt` namespace automatically if it isn't already present
++ if ( ! args[0].match(/\.dt\b/) ) {
++ args[0] += '.dt';
++ }
++
++ var inst = $( this.tables().nodes() );
++ inst[key].apply( inst, args );
++ return this;
++ } );
++ } );
++
++
++ _api_register( 'clear()', function () {
++ return this.iterator( 'table', function ( settings ) {
++ _fnClearTable( settings );
++ } );
++ } );
++
++
++ _api_register( 'settings()', function () {
++ return new _Api( this.context, this.context );
++ } );
++
++
++ _api_register( 'init()', function () {
++ var ctx = this.context;
++ return ctx.length ? ctx[0].oInit : null;
++ } );
++
++
++ _api_register( 'data()', function () {
++ return this.iterator( 'table', function ( settings ) {
++ return _pluck( settings.aoData, '_aData' );
++ } ).flatten();
++ } );
++
++
++ _api_register( 'destroy()', function ( remove ) {
++ remove = remove || false;
++
++ return this.iterator( 'table', function ( settings ) {
++ var orig = settings.nTableWrapper.parentNode;
++ var classes = settings.oClasses;
++ var table = settings.nTable;
++ var tbody = settings.nTBody;
++ var thead = settings.nTHead;
++ var tfoot = settings.nTFoot;
++ var jqTable = $(table);
++ var jqTbody = $(tbody);
++ var jqWrapper = $(settings.nTableWrapper);
++ var rows = $.map( settings.aoData, function (r) { return r.nTr; } );
++ var i, ien;
++
++ // Flag to note that the table is currently being destroyed - no action
++ // should be taken
++ settings.bDestroying = true;
++
++ // Fire off the destroy callbacks for plug-ins etc
++ _fnCallbackFire( settings, "aoDestroyCallback", "destroy", [settings] );
++
++ // If not being removed from the document, make all columns visible
++ if ( ! remove ) {
++ new _Api( settings ).columns().visible( true );
++ }
++
++ // Blitz all `DT` namespaced events (these are internal events, the
++ // lowercase, `dt` events are user subscribed and they are responsible
++ // for removing them
++ jqWrapper.unbind('.DT').find(':not(tbody *)').unbind('.DT');
++ $(window).unbind('.DT-'+settings.sInstance);
++
++ // When scrolling we had to break the table up - restore it
++ if ( table != thead.parentNode ) {
++ jqTable.children('thead').detach();
++ jqTable.append( thead );
++ }
++
++ if ( tfoot && table != tfoot.parentNode ) {
++ jqTable.children('tfoot').detach();
++ jqTable.append( tfoot );
++ }
++
++ // Remove the DataTables generated nodes, events and classes
++ jqTable.detach();
++ jqWrapper.detach();
++
++ settings.aaSorting = [];
++ settings.aaSortingFixed = [];
++ _fnSortingClasses( settings );
++
++ $( rows ).removeClass( settings.asStripeClasses.join(' ') );
++
++ $('th, td', thead).removeClass( classes.sSortable+' '+
++ classes.sSortableAsc+' '+classes.sSortableDesc+' '+classes.sSortableNone
++ );
++
++ if ( settings.bJUI ) {
++ $('th span.'+classes.sSortIcon+ ', td span.'+classes.sSortIcon, thead).detach();
++ $('th, td', thead).each( function () {
++ var wrapper = $('div.'+classes.sSortJUIWrapper, this);
++ $(this).append( wrapper.contents() );
++ wrapper.detach();
++ } );
++ }
++
++ if ( ! remove && orig ) {
++ // insertBefore acts like appendChild if !arg[1]
++ orig.insertBefore( table, settings.nTableReinsertBefore );
++ }
++
++ // Add the TR elements back into the table in their original order
++ jqTbody.children().detach();
++ jqTbody.append( rows );
++
++ // Restore the width of the original table - was read from the style property,
++ // so we can restore directly to that
++ jqTable
++ .css( 'width', settings.sDestroyWidth )
++ .removeClass( classes.sTable );
++
++ // If the were originally stripe classes - then we add them back here.
++ // Note this is not fool proof (for example if not all rows had stripe
++ // classes - but it's a good effort without getting carried away
++ ien = settings.asDestroyStripes.length;
++
++ if ( ien ) {
++ jqTbody.children().each( function (i) {
++ $(this).addClass( settings.asDestroyStripes[i % ien] );
++ } );
++ }
++
++ /* Remove the settings object from the settings array */
++ var idx = $.inArray( settings, DataTable.settings );
++ if ( idx !== -1 ) {
++ DataTable.settings.splice( idx, 1 );
++ }
++ } );
++ } );
++
++
++ // Add the `every()` method for rows, columns and cells in a compact form
++ $.each( [ 'column', 'row', 'cell' ], function ( i, type ) {
++ _api_register( type+'s().every()', function ( fn ) {
++ return this.iterator( type, function ( settings, idx, idx2 ) {
++ // idx2 is undefined for rows and columns.
++ fn.call( new _Api( settings )[ type ]( idx, idx2 ) );
++ } );
++ } );
++ } );
++
++
++ // i18n method for extensions to be able to use the language object from the
++ // DataTable
++ _api_register( 'i18n()', function ( token, def, plural ) {
++ var ctx = this.context[0];
++ var resolved = _fnGetObjectDataFn( token )( ctx.oLanguage );
++
++ if ( resolved === undefined ) {
++ resolved = def;
++ }
++
++ if ( plural !== undefined && $.isPlainObject( resolved ) ) {
++ resolved = resolved[ plural ] !== undefined ?
++ resolved[ plural ] :
++ resolved._;
++ }
++
++ return resolved.replace( '%d', plural ); // nb: plural might be undefined,
++ } );
++
++ /**
++ * Version string for plug-ins to check compatibility. Allowed format is
++ * `a.b.c-d` where: a:int, b:int, c:int, d:string(dev|beta|alpha). `d` is used
++ * only for non-release builds. See http://semver.org/ for more information.
++ * @member
++ * @type string
++ * @default Version number
++ */
++ DataTable.version = "1.10.7";
++
++ /**
++ * Private data store, containing all of the settings objects that are
++ * created for the tables on a given page.
++ *
++ * Note that the `DataTable.settings` object is aliased to
++ * `jQuery.fn.dataTableExt` through which it may be accessed and
++ * manipulated, or `jQuery.fn.dataTable.settings`.
++ * @member
++ * @type array
++ * @default []
++ * @private
++ */
++ DataTable.settings = [];
++
++ /**
++ * Object models container, for the various models that DataTables has
++ * available to it. These models define the objects that are used to hold
++ * the active state and configuration of the table.
++ * @namespace
++ */
++ DataTable.models = {};
++
++
++
++ /**
++ * Template object for the way in which DataTables holds information about
++ * search information for the global filter and individual column filters.
++ * @namespace
++ */
++ DataTable.models.oSearch = {
++ /**
++ * Flag to indicate if the filtering should be case insensitive or not
++ * @type boolean
++ * @default true
++ */
++ "bCaseInsensitive": true,
++
++ /**
++ * Applied search term
++ * @type string
++ * @default <i>Empty string</i>
++ */
++ "sSearch": "",
++
++ /**
++ * Flag to indicate if the search term should be interpreted as a
++ * regular expression (true) or not (false) and therefore and special
++ * regex characters escaped.
++ * @type boolean
++ * @default false
++ */
++ "bRegex": false,
++
++ /**
++ * Flag to indicate if DataTables is to use its smart filtering or not.
++ * @type boolean
++ * @default true
++ */
++ "bSmart": true
++ };
++
++
++
++
++ /**
++ * Template object for the way in which DataTables holds information about
++ * each individual row. This is the object format used for the settings
++ * aoData array.
++ * @namespace
++ */
++ DataTable.models.oRow = {
++ /**
++ * TR element for the row
++ * @type node
++ * @default null
++ */
++ "nTr": null,
++
++ /**
++ * Array of TD elements for each row. This is null until the row has been
++ * created.
++ * @type array nodes
++ * @default []
++ */
++ "anCells": null,
++
++ /**
++ * Data object from the original data source for the row. This is either
++ * an array if using the traditional form of DataTables, or an object if
++ * using mData options. The exact type will depend on the passed in
++ * data from the data source, or will be an array if using DOM a data
++ * source.
++ * @type array|object
++ * @default []
++ */
++ "_aData": [],
++
++ /**
++ * Sorting data cache - this array is ostensibly the same length as the
++ * number of columns (although each index is generated only as it is
++ * needed), and holds the data that is used for sorting each column in the
++ * row. We do this cache generation at the start of the sort in order that
++ * the formatting of the sort data need be done only once for each cell
++ * per sort. This array should not be read from or written to by anything
++ * other than the master sorting methods.
++ * @type array
++ * @default null
++ * @private
++ */
++ "_aSortData": null,
++
++ /**
++ * Per cell filtering data cache. As per the sort data cache, used to
++ * increase the performance of the filtering in DataTables
++ * @type array
++ * @default null
++ * @private
++ */
++ "_aFilterData": null,
++
++ /**
++ * Filtering data cache. This is the same as the cell filtering cache, but
++ * in this case a string rather than an array. This is easily computed with
++ * a join on `_aFilterData`, but is provided as a cache so the join isn't
++ * needed on every search (memory traded for performance)
++ * @type array
++ * @default null
++ * @private
++ */
++ "_sFilterRow": null,
++
++ /**
++ * Cache of the class name that DataTables has applied to the row, so we
++ * can quickly look at this variable rather than needing to do a DOM check
++ * on className for the nTr property.
++ * @type string
++ * @default <i>Empty string</i>
++ * @private
++ */
++ "_sRowStripe": "",
++
++ /**
++ * Denote if the original data source was from the DOM, or the data source
++ * object. This is used for invalidating data, so DataTables can
++ * automatically read data from the original source, unless uninstructed
++ * otherwise.
++ * @type string
++ * @default null
++ * @private
++ */
++ "src": null
++ };
++
++
++ /**
++ * Template object for the column information object in DataTables. This object
++ * is held in the settings aoColumns array and contains all the information that
++ * DataTables needs about each individual column.
++ *
++ * Note that this object is related to {@link DataTable.defaults.column}
++ * but this one is the internal data store for DataTables's cache of columns.
++ * It should NOT be manipulated outside of DataTables. Any configuration should
++ * be done through the initialisation options.
++ * @namespace
++ */
++ DataTable.models.oColumn = {
++ /**
++ * Column index. This could be worked out on-the-fly with $.inArray, but it
++ * is faster to just hold it as a variable
++ * @type integer
++ * @default null
++ */
++ "idx": null,
++
++ /**
++ * A list of the columns that sorting should occur on when this column
++ * is sorted. That this property is an array allows multi-column sorting
++ * to be defined for a column (for example first name / last name columns
++ * would benefit from this). The values are integers pointing to the
++ * columns to be sorted on (typically it will be a single integer pointing
++ * at itself, but that doesn't need to be the case).
++ * @type array
++ */
++ "aDataSort": null,
++
++ /**
++ * Define the sorting directions that are applied to the column, in sequence
++ * as the column is repeatedly sorted upon - i.e. the first value is used
++ * as the sorting direction when the column if first sorted (clicked on).
++ * Sort it again (click again) and it will move on to the next index.
++ * Repeat until loop.
++ * @type array
++ */
++ "asSorting": null,
++
++ /**
++ * Flag to indicate if the column is searchable, and thus should be included
++ * in the filtering or not.
++ * @type boolean
++ */
++ "bSearchable": null,
++
++ /**
++ * Flag to indicate if the column is sortable or not.
++ * @type boolean
++ */
++ "bSortable": null,
++
++ /**
++ * Flag to indicate if the column is currently visible in the table or not
++ * @type boolean
++ */
++ "bVisible": null,
++
++ /**
++ * Store for manual type assignment using the `column.type` option. This
++ * is held in store so we can manipulate the column's `sType` property.
++ * @type string
++ * @default null
++ * @private
++ */
++ "_sManualType": null,
++
++ /**
++ * Flag to indicate if HTML5 data attributes should be used as the data
++ * source for filtering or sorting. True is either are.
++ * @type boolean
++ * @default false
++ * @private
++ */
++ "_bAttrSrc": false,
++
++ /**
++ * Developer definable function that is called whenever a cell is created (Ajax source,
++ * etc) or processed for input (DOM source). This can be used as a compliment to mRender
++ * allowing you to modify the DOM element (add background colour for example) when the
++ * element is available.
++ * @type function
++ * @param {element} nTd The TD node that has been created
++ * @param {*} sData The Data for the cell
++ * @param {array|object} oData The data for the whole row
++ * @param {int} iRow The row index for the aoData data store
++ * @default null
++ */
++ "fnCreatedCell": null,
++
++ /**
++ * Function to get data from a cell in a column. You should <b>never</b>
++ * access data directly through _aData internally in DataTables - always use
++ * the method attached to this property. It allows mData to function as
++ * required. This function is automatically assigned by the column
++ * initialisation method
++ * @type function
++ * @param {array|object} oData The data array/object for the array
++ * (i.e. aoData[]._aData)
++ * @param {string} sSpecific The specific data type you want to get -
++ * 'display', 'type' 'filter' 'sort'
++ * @returns {*} The data for the cell from the given row's data
++ * @default null
++ */
++ "fnGetData": null,
++
++ /**
++ * Function to set data for a cell in the column. You should <b>never</b>
++ * set the data directly to _aData internally in DataTables - always use
++ * this method. It allows mData to function as required. This function
++ * is automatically assigned by the column initialisation method
++ * @type function
++ * @param {array|object} oData The data array/object for the array
++ * (i.e. aoData[]._aData)
++ * @param {*} sValue Value to set
++ * @default null
++ */
++ "fnSetData": null,
++
++ /**
++ * Property to read the value for the cells in the column from the data
++ * source array / object. If null, then the default content is used, if a
++ * function is given then the return from the function is used.
++ * @type function|int|string|null
++ * @default null
++ */
++ "mData": null,
++
++ /**
++ * Partner property to mData which is used (only when defined) to get
++ * the data - i.e. it is basically the same as mData, but without the
++ * 'set' option, and also the data fed to it is the result from mData.
++ * This is the rendering method to match the data method of mData.
++ * @type function|int|string|null
++ * @default null
++ */
++ "mRender": null,
++
++ /**
++ * Unique header TH/TD element for this column - this is what the sorting
++ * listener is attached to (if sorting is enabled.)
++ * @type node
++ * @default null
++ */
++ "nTh": null,
++
++ /**
++ * Unique footer TH/TD element for this column (if there is one). Not used
++ * in DataTables as such, but can be used for plug-ins to reference the
++ * footer for each column.
++ * @type node
++ * @default null
++ */
++ "nTf": null,
++
++ /**
++ * The class to apply to all TD elements in the table's TBODY for the column
++ * @type string
++ * @default null
++ */
++ "sClass": null,
++
++ /**
++ * When DataTables calculates the column widths to assign to each column,
++ * it finds the longest string in each column and then constructs a
++ * temporary table and reads the widths from that. The problem with this
++ * is that "mmm" is much wider then "iiii", but the latter is a longer
++ * string - thus the calculation can go wrong (doing it properly and putting
++ * it into an DOM object and measuring that is horribly(!) slow). Thus as
++ * a "work around" we provide this option. It will append its value to the
++ * text that is found to be the longest string for the column - i.e. padding.
++ * @type string
++ */
++ "sContentPadding": null,
++
++ /**
++ * Allows a default value to be given for a column's data, and will be used
++ * whenever a null data source is encountered (this can be because mData
++ * is set to null, or because the data source itself is null).
++ * @type string
++ * @default null
++ */
++ "sDefaultContent": null,
++
++ /**
++ * Name for the column, allowing reference to the column by name as well as
++ * by index (needs a lookup to work by name).
++ * @type string
++ */
++ "sName": null,
++
++ /**
++ * Custom sorting data type - defines which of the available plug-ins in
++ * afnSortData the custom sorting will use - if any is defined.
++ * @type string
++ * @default std
++ */
++ "sSortDataType": 'std',
++
++ /**
++ * Class to be applied to the header element when sorting on this column
++ * @type string
++ * @default null
++ */
++ "sSortingClass": null,
++
++ /**
++ * Class to be applied to the header element when sorting on this column -
++ * when jQuery UI theming is used.
++ * @type string
++ * @default null
++ */
++ "sSortingClassJUI": null,
++
++ /**
++ * Title of the column - what is seen in the TH element (nTh).
++ * @type string
++ */
++ "sTitle": null,
++
++ /**
++ * Column sorting and filtering type
++ * @type string
++ * @default null
++ */
++ "sType": null,
++
++ /**
++ * Width of the column
++ * @type string
++ * @default null
++ */
++ "sWidth": null,
++
++ /**
++ * Width of the column when it was first "encountered"
++ * @type string
++ * @default null
++ */
++ "sWidthOrig": null
++ };
++
++
++ /*
++ * Developer note: The properties of the object below are given in Hungarian
++ * notation, that was used as the interface for DataTables prior to v1.10, however
++ * from v1.10 onwards the primary interface is camel case. In order to avoid
++ * breaking backwards compatibility utterly with this change, the Hungarian
++ * version is still, internally the primary interface, but is is not documented
++ * - hence the @name tags in each doc comment. This allows a Javascript function
++ * to create a map from Hungarian notation to camel case (going the other direction
++ * would require each property to be listed, which would at around 3K to the size
++ * of DataTables, while this method is about a 0.5K hit.
++ *
++ * Ultimately this does pave the way for Hungarian notation to be dropped
++ * completely, but that is a massive amount of work and will break current
++ * installs (therefore is on-hold until v2).
++ */
++
++ /**
++ * Initialisation options that can be given to DataTables at initialisation
++ * time.
++ * @namespace
++ */
++ DataTable.defaults = {
++ /**
++ * An array of data to use for the table, passed in at initialisation which
++ * will be used in preference to any data which is already in the DOM. This is
++ * particularly useful for constructing tables purely in Javascript, for
++ * example with a custom Ajax call.
++ * @type array
++ * @default null
++ *
++ * @dtopt Option
++ * @name DataTable.defaults.data
++ *
++ * @example
++ * // Using a 2D array data source
++ * $(document).ready( function () {
++ * $('#example').dataTable( {
++ * "data": [
++ * ['Trident', 'Internet Explorer 4.0', 'Win 95+', 4, 'X'],
++ * ['Trident', 'Internet Explorer 5.0', 'Win 95+', 5, 'C'],
++ * ],
++ * "columns": [
++ * { "title": "Engine" },
++ * { "title": "Browser" },
++ * { "title": "Platform" },
++ * { "title": "Version" },
++ * { "title": "Grade" }
++ * ]
++ * } );
++ * } );
++ *
++ * @example
++ * // Using an array of objects as a data source (`data`)
++ * $(document).ready( function () {
++ * $('#example').dataTable( {
++ * "data": [
++ * {
++ * "engine": "Trident",
++ * "browser": "Internet Explorer 4.0",
++ * "platform": "Win 95+",
++ * "version": 4,
++ * "grade": "X"
++ * },
++ * {
++ * "engine": "Trident",
++ * "browser": "Internet Explorer 5.0",
++ * "platform": "Win 95+",
++ * "version": 5,
++ * "grade": "C"
++ * }
++ * ],
++ * "columns": [
++ * { "title": "Engine", "data": "engine" },
++ * { "title": "Browser", "data": "browser" },
++ * { "title": "Platform", "data": "platform" },
++ * { "title": "Version", "data": "version" },
++ * { "title": "Grade", "data": "grade" }
++ * ]
++ * } );
++ * } );
++ */
++ "aaData": null,
++
++
++ /**
++ * If ordering is enabled, then DataTables will perform a first pass sort on
++ * initialisation. You can define which column(s) the sort is performed
++ * upon, and the sorting direction, with this variable. The `sorting` array
++ * should contain an array for each column to be sorted initially containing
++ * the column's index and a direction string ('asc' or 'desc').
++ * @type array
++ * @default [[0,'asc']]
++ *
++ * @dtopt Option
++ * @name DataTable.defaults.order
++ *
++ * @example
++ * // Sort by 3rd column first, and then 4th column
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "order": [[2,'asc'], [3,'desc']]
++ * } );
++ * } );
++ *
++ * // No initial sorting
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "order": []
++ * } );
++ * } );
++ */
++ "aaSorting": [[0,'asc']],
++
++
++ /**
++ * This parameter is basically identical to the `sorting` parameter, but
++ * cannot be overridden by user interaction with the table. What this means
++ * is that you could have a column (visible or hidden) which the sorting
++ * will always be forced on first - any sorting after that (from the user)
++ * will then be performed as required. This can be useful for grouping rows
++ * together.
++ * @type array
++ * @default null
++ *
++ * @dtopt Option
++ * @name DataTable.defaults.orderFixed
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "orderFixed": [[0,'asc']]
++ * } );
++ * } )
++ */
++ "aaSortingFixed": [],
++
++
++ /**
++ * DataTables can be instructed to load data to display in the table from a
++ * Ajax source. This option defines how that Ajax call is made and where to.
++ *
++ * The `ajax` property has three different modes of operation, depending on
++ * how it is defined. These are:
++ *
++ * * `string` - Set the URL from where the data should be loaded from.
++ * * `object` - Define properties for `jQuery.ajax`.
++ * * `function` - Custom data get function
++ *
++ * `string`
++ * --------
++ *
++ * As a string, the `ajax` property simply defines the URL from which
++ * DataTables will load data.
++ *
++ * `object`
++ * --------
++ *
++ * As an object, the parameters in the object are passed to
++ * [jQuery.ajax](http://api.jquery.com/jQuery.ajax/) allowing fine control
++ * of the Ajax request. DataTables has a number of default parameters which
++ * you can override using this option. Please refer to the jQuery
++ * documentation for a full description of the options available, although
++ * the following parameters provide additional options in DataTables or
++ * require special consideration:
++ *
++ * * `data` - As with jQuery, `data` can be provided as an object, but it
++ * can also be used as a function to manipulate the data DataTables sends
++ * to the server. The function takes a single parameter, an object of
++ * parameters with the values that DataTables has readied for sending. An
++ * object may be returned which will be merged into the DataTables
++ * defaults, or you can add the items to the object that was passed in and
++ * not return anything from the function. This supersedes `fnServerParams`
++ * from DataTables 1.9-.
++ *
++ * * `dataSrc` - By default DataTables will look for the property `data` (or
++ * `aaData` for compatibility with DataTables 1.9-) when obtaining data
++ * from an Ajax source or for server-side processing - this parameter
++ * allows that property to be changed. You can use Javascript dotted
++ * object notation to get a data source for multiple levels of nesting, or
++ * it my be used as a function. As a function it takes a single parameter,
++ * the JSON returned from the server, which can be manipulated as
++ * required, with the returned value being that used by DataTables as the
++ * data source for the table. This supersedes `sAjaxDataProp` from
++ * DataTables 1.9-.
++ *
++ * * `success` - Should not be overridden it is used internally in
++ * DataTables. To manipulate / transform the data returned by the server
++ * use `ajax.dataSrc`, or use `ajax` as a function (see below).
++ *
++ * `function`
++ * ----------
++ *
++ * As a function, making the Ajax call is left up to yourself allowing
++ * complete control of the Ajax request. Indeed, if desired, a method other
++ * than Ajax could be used to obtain the required data, such as Web storage
++ * or an AIR database.
++ *
++ * The function is given four parameters and no return is required. The
++ * parameters are:
++ *
++ * 1. _object_ - Data to send to the server
++ * 2. _function_ - Callback function that must be executed when the required
++ * data has been obtained. That data should be passed into the callback
++ * as the only parameter
++ * 3. _object_ - DataTables settings object for the table
++ *
++ * Note that this supersedes `fnServerData` from DataTables 1.9-.
++ *
++ * @type string|object|function
++ * @default null
++ *
++ * @dtopt Option
++ * @name DataTable.defaults.ajax
++ * @since 1.10.0
++ *
++ * @example
++ * // Get JSON data from a file via Ajax.
++ * // Note DataTables expects data in the form `{ data: [ ...data... ] }` by default).
++ * $('#example').dataTable( {
++ * "ajax": "data.json"
++ * } );
++ *
++ * @example
++ * // Get JSON data from a file via Ajax, using `dataSrc` to change
++ * // `data` to `tableData` (i.e. `{ tableData: [ ...data... ] }`)
++ * $('#example').dataTable( {
++ * "ajax": {
++ * "url": "data.json",
++ * "dataSrc": "tableData"
++ * }
++ * } );
++ *
++ * @example
++ * // Get JSON data from a file via Ajax, using `dataSrc` to read data
++ * // from a plain array rather than an array in an object
++ * $('#example').dataTable( {
++ * "ajax": {
++ * "url": "data.json",
++ * "dataSrc": ""
++ * }
++ * } );
++ *
++ * @example
++ * // Manipulate the data returned from the server - add a link to data
++ * // (note this can, should, be done using `render` for the column - this
++ * // is just a simple example of how the data can be manipulated).
++ * $('#example').dataTable( {
++ * "ajax": {
++ * "url": "data.json",
++ * "dataSrc": function ( json ) {
++ * for ( var i=0, ien=json.length ; i<ien ; i++ ) {
++ * json[i][0] = '<a href="/message/'+json[i][0]+'>View message</a>';
++ * }
++ * return json;
++ * }
++ * }
++ * } );
++ *
++ * @example
++ * // Add data to the request
++ * $('#example').dataTable( {
++ * "ajax": {
++ * "url": "data.json",
++ * "data": function ( d ) {
++ * return {
++ * "extra_search": $('#extra').val()
++ * };
++ * }
++ * }
++ * } );
++ *
++ * @example
++ * // Send request as POST
++ * $('#example').dataTable( {
++ * "ajax": {
++ * "url": "data.json",
++ * "type": "POST"
++ * }
++ * } );
++ *
++ * @example
++ * // Get the data from localStorage (could interface with a form for
++ * // adding, editing and removing rows).
++ * $('#example').dataTable( {
++ * "ajax": function (data, callback, settings) {
++ * callback(
++ * JSON.parse( localStorage.getItem('dataTablesData') )
++ * );
++ * }
++ * } );
++ */
++ "ajax": null,
++
++
++ /**
++ * This parameter allows you to readily specify the entries in the length drop
++ * down menu that DataTables shows when pagination is enabled. It can be
++ * either a 1D array of options which will be used for both the displayed
++ * option and the value, or a 2D array which will use the array in the first
++ * position as the value, and the array in the second position as the
++ * displayed options (useful for language strings such as 'All').
++ *
++ * Note that the `pageLength` property will be automatically set to the
++ * first value given in this array, unless `pageLength` is also provided.
++ * @type array
++ * @default [ 10, 25, 50, 100 ]
++ *
++ * @dtopt Option
++ * @name DataTable.defaults.lengthMenu
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "lengthMenu": [[10, 25, 50, -1], [10, 25, 50, "All"]]
++ * } );
++ * } );
++ */
++ "aLengthMenu": [ 10, 25, 50, 100 ],
++
++
++ /**
++ * The `columns` option in the initialisation parameter allows you to define
++ * details about the way individual columns behave. For a full list of
++ * column options that can be set, please see
++ * {@link DataTable.defaults.column}. Note that if you use `columns` to
++ * define your columns, you must have an entry in the array for every single
++ * column that you have in your table (these can be null if you don't which
++ * to specify any options).
++ * @member
++ *
++ * @name DataTable.defaults.column
++ */
++ "aoColumns": null,
++
++ /**
++ * Very similar to `columns`, `columnDefs` allows you to target a specific
++ * column, multiple columns, or all columns, using the `targets` property of
++ * each object in the array. This allows great flexibility when creating
++ * tables, as the `columnDefs` arrays can be of any length, targeting the
++ * columns you specifically want. `columnDefs` may use any of the column
++ * options available: {@link DataTable.defaults.column}, but it _must_
++ * have `targets` defined in each object in the array. Values in the `targets`
++ * array may be:
++ * <ul>
++ * <li>a string - class name will be matched on the TH for the column</li>
++ * <li>0 or a positive integer - column index counting from the left</li>
++ * <li>a negative integer - column index counting from the right</li>
++ * <li>the string "_all" - all columns (i.e. assign a default)</li>
++ * </ul>
++ * @member
++ *
++ * @name DataTable.defaults.columnDefs
++ */
++ "aoColumnDefs": null,
++
++
++ /**
++ * Basically the same as `search`, this parameter defines the individual column
++ * filtering state at initialisation time. The array must be of the same size
++ * as the number of columns, and each element be an object with the parameters
++ * `search` and `escapeRegex` (the latter is optional). 'null' is also
++ * accepted and the default will be used.
++ * @type array
++ * @default []
++ *
++ * @dtopt Option
++ * @name DataTable.defaults.searchCols
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "searchCols": [
++ * null,
++ * { "search": "My filter" },
++ * null,
++ * { "search": "^[0-9]", "escapeRegex": false }
++ * ]
++ * } );
++ * } )
++ */
++ "aoSearchCols": [],
++
++
++ /**
++ * An array of CSS classes that should be applied to displayed rows. This
++ * array may be of any length, and DataTables will apply each class
++ * sequentially, looping when required.
++ * @type array
++ * @default null <i>Will take the values determined by the `oClasses.stripe*`
++ * options</i>
++ *
++ * @dtopt Option
++ * @name DataTable.defaults.stripeClasses
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "stripeClasses": [ 'strip1', 'strip2', 'strip3' ]
++ * } );
++ * } )
++ */
++ "asStripeClasses": null,
++
++
++ /**
++ * Enable or disable automatic column width calculation. This can be disabled
++ * as an optimisation (it takes some time to calculate the widths) if the
++ * tables widths are passed in using `columns`.
++ * @type boolean
++ * @default true
++ *
++ * @dtopt Features
++ * @name DataTable.defaults.autoWidth
++ *
++ * @example
++ * $(document).ready( function () {
++ * $('#example').dataTable( {
++ * "autoWidth": false
++ * } );
++ * } );
++ */
++ "bAutoWidth": true,
++
++
++ /**
++ * Deferred rendering can provide DataTables with a huge speed boost when you
++ * are using an Ajax or JS data source for the table. This option, when set to
++ * true, will cause DataTables to defer the creation of the table elements for
++ * each row until they are needed for a draw - saving a significant amount of
++ * time.
++ * @type boolean
++ * @default false
++ *
++ * @dtopt Features
++ * @name DataTable.defaults.deferRender
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "ajax": "sources/arrays.txt",
++ * "deferRender": true
++ * } );
++ * } );
++ */
++ "bDeferRender": false,
++
++
++ /**
++ * Replace a DataTable which matches the given selector and replace it with
++ * one which has the properties of the new initialisation object passed. If no
++ * table matches the selector, then the new DataTable will be constructed as
++ * per normal.
++ * @type boolean
++ * @default false
++ *
++ * @dtopt Options
++ * @name DataTable.defaults.destroy
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "srollY": "200px",
++ * "paginate": false
++ * } );
++ *
++ * // Some time later....
++ * $('#example').dataTable( {
++ * "filter": false,
++ * "destroy": true
++ * } );
++ * } );
++ */
++ "bDestroy": false,
++
++
++ /**
++ * Enable or disable filtering of data. Filtering in DataTables is "smart" in
++ * that it allows the end user to input multiple words (space separated) and
++ * will match a row containing those words, even if not in the order that was
++ * specified (this allow matching across multiple columns). Note that if you
++ * wish to use filtering in DataTables this must remain 'true' - to remove the
++ * default filtering input box and retain filtering abilities, please use
++ * {@link DataTable.defaults.dom}.
++ * @type boolean
++ * @default true
++ *
++ * @dtopt Features
++ * @name DataTable.defaults.searching
++ *
++ * @example
++ * $(document).ready( function () {
++ * $('#example').dataTable( {
++ * "searching": false
++ * } );
++ * } );
++ */
++ "bFilter": true,
++
++
++ /**
++ * Enable or disable the table information display. This shows information
++ * about the data that is currently visible on the page, including information
++ * about filtered data if that action is being performed.
++ * @type boolean
++ * @default true
++ *
++ * @dtopt Features
++ * @name DataTable.defaults.info
++ *
++ * @example
++ * $(document).ready( function () {
++ * $('#example').dataTable( {
++ * "info": false
++ * } );
++ * } );
++ */
++ "bInfo": true,
++
++
++ /**
++ * Enable jQuery UI ThemeRoller support (required as ThemeRoller requires some
++ * slightly different and additional mark-up from what DataTables has
++ * traditionally used).
++ * @type boolean
++ * @default false
++ *
++ * @dtopt Features
++ * @name DataTable.defaults.jQueryUI
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "jQueryUI": true
++ * } );
++ * } );
++ */
++ "bJQueryUI": false,
++
++
++ /**
++ * Allows the end user to select the size of a formatted page from a select
++ * menu (sizes are 10, 25, 50 and 100). Requires pagination (`paginate`).
++ * @type boolean
++ * @default true
++ *
++ * @dtopt Features
++ * @name DataTable.defaults.lengthChange
++ *
++ * @example
++ * $(document).ready( function () {
++ * $('#example').dataTable( {
++ * "lengthChange": false
++ * } );
++ * } );
++ */
++ "bLengthChange": true,
++
++
++ /**
++ * Enable or disable pagination.
++ * @type boolean
++ * @default true
++ *
++ * @dtopt Features
++ * @name DataTable.defaults.paging
++ *
++ * @example
++ * $(document).ready( function () {
++ * $('#example').dataTable( {
++ * "paging": false
++ * } );
++ * } );
++ */
++ "bPaginate": true,
++
++
++ /**
++ * Enable or disable the display of a 'processing' indicator when the table is
++ * being processed (e.g. a sort). This is particularly useful for tables with
++ * large amounts of data where it can take a noticeable amount of time to sort
++ * the entries.
++ * @type boolean
++ * @default false
++ *
++ * @dtopt Features
++ * @name DataTable.defaults.processing
++ *
++ * @example
++ * $(document).ready( function () {
++ * $('#example').dataTable( {
++ * "processing": true
++ * } );
++ * } );
++ */
++ "bProcessing": false,
++
++
++ /**
++ * Retrieve the DataTables object for the given selector. Note that if the
++ * table has already been initialised, this parameter will cause DataTables
++ * to simply return the object that has already been set up - it will not take
++ * account of any changes you might have made to the initialisation object
++ * passed to DataTables (setting this parameter to true is an acknowledgement
++ * that you understand this). `destroy` can be used to reinitialise a table if
++ * you need.
++ * @type boolean
++ * @default false
++ *
++ * @dtopt Options
++ * @name DataTable.defaults.retrieve
++ *
++ * @example
++ * $(document).ready( function() {
++ * initTable();
++ * tableActions();
++ * } );
++ *
++ * function initTable ()
++ * {
++ * return $('#example').dataTable( {
++ * "scrollY": "200px",
++ * "paginate": false,
++ * "retrieve": true
++ * } );
++ * }
++ *
++ * function tableActions ()
++ * {
++ * var table = initTable();
++ * // perform API operations with oTable
++ * }
++ */
++ "bRetrieve": false,
++
++
++ /**
++ * When vertical (y) scrolling is enabled, DataTables will force the height of
++ * the table's viewport to the given height at all times (useful for layout).
++ * However, this can look odd when filtering data down to a small data set,
++ * and the footer is left "floating" further down. This parameter (when
++ * enabled) will cause DataTables to collapse the table's viewport down when
++ * the result set will fit within the given Y height.
++ * @type boolean
++ * @default false
++ *
++ * @dtopt Options
++ * @name DataTable.defaults.scrollCollapse
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "scrollY": "200",
++ * "scrollCollapse": true
++ * } );
++ * } );
++ */
++ "bScrollCollapse": false,
++
++
++ /**
++ * Configure DataTables to use server-side processing. Note that the
++ * `ajax` parameter must also be given in order to give DataTables a
++ * source to obtain the required data for each draw.
++ * @type boolean
++ * @default false
++ *
++ * @dtopt Features
++ * @dtopt Server-side
++ * @name DataTable.defaults.serverSide
++ *
++ * @example
++ * $(document).ready( function () {
++ * $('#example').dataTable( {
++ * "serverSide": true,
++ * "ajax": "xhr.php"
++ * } );
++ * } );
++ */
++ "bServerSide": false,
++
++
++ /**
++ * Enable or disable sorting of columns. Sorting of individual columns can be
++ * disabled by the `sortable` option for each column.
++ * @type boolean
++ * @default true
++ *
++ * @dtopt Features
++ * @name DataTable.defaults.ordering
++ *
++ * @example
++ * $(document).ready( function () {
++ * $('#example').dataTable( {
++ * "ordering": false
++ * } );
++ * } );
++ */
++ "bSort": true,
++
++
++ /**
++ * Enable or display DataTables' ability to sort multiple columns at the
++ * same time (activated by shift-click by the user).
++ * @type boolean
++ * @default true
++ *
++ * @dtopt Options
++ * @name DataTable.defaults.orderMulti
++ *
++ * @example
++ * // Disable multiple column sorting ability
++ * $(document).ready( function () {
++ * $('#example').dataTable( {
++ * "orderMulti": false
++ * } );
++ * } );
++ */
++ "bSortMulti": true,
++
++
++ /**
++ * Allows control over whether DataTables should use the top (true) unique
++ * cell that is found for a single column, or the bottom (false - default).
++ * This is useful when using complex headers.
++ * @type boolean
++ * @default false
++ *
++ * @dtopt Options
++ * @name DataTable.defaults.orderCellsTop
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "orderCellsTop": true
++ * } );
++ * } );
++ */
++ "bSortCellsTop": false,
++
++
++ /**
++ * Enable or disable the addition of the classes `sorting\_1`, `sorting\_2` and
++ * `sorting\_3` to the columns which are currently being sorted on. This is
++ * presented as a feature switch as it can increase processing time (while
++ * classes are removed and added) so for large data sets you might want to
++ * turn this off.
++ * @type boolean
++ * @default true
++ *
++ * @dtopt Features
++ * @name DataTable.defaults.orderClasses
++ *
++ * @example
++ * $(document).ready( function () {
++ * $('#example').dataTable( {
++ * "orderClasses": false
++ * } );
++ * } );
++ */
++ "bSortClasses": true,
++
++
++ /**
++ * Enable or disable state saving. When enabled HTML5 `localStorage` will be
++ * used to save table display information such as pagination information,
++ * display length, filtering and sorting. As such when the end user reloads
++ * the page the display display will match what thy had previously set up.
++ *
++ * Due to the use of `localStorage` the default state saving is not supported
++ * in IE6 or 7. If state saving is required in those browsers, use
++ * `stateSaveCallback` to provide a storage solution such as cookies.
++ * @type boolean
++ * @default false
++ *
++ * @dtopt Features
++ * @name DataTable.defaults.stateSave
++ *
++ * @example
++ * $(document).ready( function () {
++ * $('#example').dataTable( {
++ * "stateSave": true
++ * } );
++ * } );
++ */
++ "bStateSave": false,
++
++
++ /**
++ * This function is called when a TR element is created (and all TD child
++ * elements have been inserted), or registered if using a DOM source, allowing
++ * manipulation of the TR element (adding classes etc).
++ * @type function
++ * @param {node} row "TR" element for the current row
++ * @param {array} data Raw data array for this row
++ * @param {int} dataIndex The index of this row in the internal aoData array
++ *
++ * @dtopt Callbacks
++ * @name DataTable.defaults.createdRow
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "createdRow": function( row, data, dataIndex ) {
++ * // Bold the grade for all 'A' grade browsers
++ * if ( data[4] == "A" )
++ * {
++ * $('td:eq(4)', row).html( '<b>A</b>' );
++ * }
++ * }
++ * } );
++ * } );
++ */
++ "fnCreatedRow": null,
++
++
++ /**
++ * This function is called on every 'draw' event, and allows you to
++ * dynamically modify any aspect you want about the created DOM.
++ * @type function
++ * @param {object} settings DataTables settings object
++ *
++ * @dtopt Callbacks
++ * @name DataTable.defaults.drawCallback
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "drawCallback": function( settings ) {
++ * alert( 'DataTables has redrawn the table' );
++ * }
++ * } );
++ * } );
++ */
++ "fnDrawCallback": null,
++
++
++ /**
++ * Identical to fnHeaderCallback() but for the table footer this function
++ * allows you to modify the table footer on every 'draw' event.
++ * @type function
++ * @param {node} foot "TR" element for the footer
++ * @param {array} data Full table data (as derived from the original HTML)
++ * @param {int} start Index for the current display starting point in the
++ * display array
++ * @param {int} end Index for the current display ending point in the
++ * display array
++ * @param {array int} display Index array to translate the visual position
++ * to the full data array
++ *
++ * @dtopt Callbacks
++ * @name DataTable.defaults.footerCallback
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "footerCallback": function( tfoot, data, start, end, display ) {
++ * tfoot.getElementsByTagName('th')[0].innerHTML = "Starting index is "+start;
++ * }
++ * } );
++ * } )
++ */
++ "fnFooterCallback": null,
++
++
++ /**
++ * When rendering large numbers in the information element for the table
++ * (i.e. "Showing 1 to 10 of 57 entries") DataTables will render large numbers
++ * to have a comma separator for the 'thousands' units (e.g. 1 million is
++ * rendered as "1,000,000") to help readability for the end user. This
++ * function will override the default method DataTables uses.
++ * @type function
++ * @member
++ * @param {int} toFormat number to be formatted
++ * @returns {string} formatted string for DataTables to show the number
++ *
++ * @dtopt Callbacks
++ * @name DataTable.defaults.formatNumber
++ *
++ * @example
++ * // Format a number using a single quote for the separator (note that
++ * // this can also be done with the language.thousands option)
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "formatNumber": function ( toFormat ) {
++ * return toFormat.toString().replace(
++ * /\B(?=(\d{3})+(?!\d))/g, "'"
++ * );
++ * };
++ * } );
++ * } );
++ */
++ "fnFormatNumber": function ( toFormat ) {
++ return toFormat.toString().replace(
++ /\B(?=(\d{3})+(?!\d))/g,
++ this.oLanguage.sThousands
++ );
++ },
++
++
++ /**
++ * This function is called on every 'draw' event, and allows you to
++ * dynamically modify the header row. This can be used to calculate and
++ * display useful information about the table.
++ * @type function
++ * @param {node} head "TR" element for the header
++ * @param {array} data Full table data (as derived from the original HTML)
++ * @param {int} start Index for the current display starting point in the
++ * display array
++ * @param {int} end Index for the current display ending point in the
++ * display array
++ * @param {array int} display Index array to translate the visual position
++ * to the full data array
++ *
++ * @dtopt Callbacks
++ * @name DataTable.defaults.headerCallback
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "fheaderCallback": function( head, data, start, end, display ) {
++ * head.getElementsByTagName('th')[0].innerHTML = "Displaying "+(end-start)+" records";
++ * }
++ * } );
++ * } )
++ */
++ "fnHeaderCallback": null,
++
++
++ /**
++ * The information element can be used to convey information about the current
++ * state of the table. Although the internationalisation options presented by
++ * DataTables are quite capable of dealing with most customisations, there may
++ * be times where you wish to customise the string further. This callback
++ * allows you to do exactly that.
++ * @type function
++ * @param {object} oSettings DataTables settings object
++ * @param {int} start Starting position in data for the draw
++ * @param {int} end End position in data for the draw
++ * @param {int} max Total number of rows in the table (regardless of
++ * filtering)
++ * @param {int} total Total number of rows in the data set, after filtering
++ * @param {string} pre The string that DataTables has formatted using it's
++ * own rules
++ * @returns {string} The string to be displayed in the information element.
++ *
++ * @dtopt Callbacks
++ * @name DataTable.defaults.infoCallback
++ *
++ * @example
++ * $('#example').dataTable( {
++ * "infoCallback": function( settings, start, end, max, total, pre ) {
++ * return start +" to "+ end;
++ * }
++ * } );
++ */
++ "fnInfoCallback": null,
++
++
++ /**
++ * Called when the table has been initialised. Normally DataTables will
++ * initialise sequentially and there will be no need for this function,
++ * however, this does not hold true when using external language information
++ * since that is obtained using an async XHR call.
++ * @type function
++ * @param {object} settings DataTables settings object
++ * @param {object} json The JSON object request from the server - only
++ * present if client-side Ajax sourced data is used
++ *
++ * @dtopt Callbacks
++ * @name DataTable.defaults.initComplete
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "initComplete": function(settings, json) {
++ * alert( 'DataTables has finished its initialisation.' );
++ * }
++ * } );
++ * } )
++ */
++ "fnInitComplete": null,
++
++
++ /**
++ * Called at the very start of each table draw and can be used to cancel the
++ * draw by returning false, any other return (including undefined) results in
++ * the full draw occurring).
++ * @type function
++ * @param {object} settings DataTables settings object
++ * @returns {boolean} False will cancel the draw, anything else (including no
++ * return) will allow it to complete.
++ *
++ * @dtopt Callbacks
++ * @name DataTable.defaults.preDrawCallback
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "preDrawCallback": function( settings ) {
++ * if ( $('#test').val() == 1 ) {
++ * return false;
++ * }
++ * }
++ * } );
++ * } );
++ */
++ "fnPreDrawCallback": null,
++
++
++ /**
++ * This function allows you to 'post process' each row after it have been
++ * generated for each table draw, but before it is rendered on screen. This
++ * function might be used for setting the row class name etc.
++ * @type function
++ * @param {node} row "TR" element for the current row
++ * @param {array} data Raw data array for this row
++ * @param {int} displayIndex The display index for the current table draw
++ * @param {int} displayIndexFull The index of the data in the full list of
++ * rows (after filtering)
++ *
++ * @dtopt Callbacks
++ * @name DataTable.defaults.rowCallback
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "rowCallback": function( row, data, displayIndex, displayIndexFull ) {
++ * // Bold the grade for all 'A' grade browsers
++ * if ( data[4] == "A" ) {
++ * $('td:eq(4)', row).html( '<b>A</b>' );
++ * }
++ * }
++ * } );
++ * } );
++ */
++ "fnRowCallback": null,
++
++
++ /**
++ * __Deprecated__ The functionality provided by this parameter has now been
++ * superseded by that provided through `ajax`, which should be used instead.
++ *
++ * This parameter allows you to override the default function which obtains
++ * the data from the server so something more suitable for your application.
++ * For example you could use POST data, or pull information from a Gears or
++ * AIR database.
++ * @type function
++ * @member
++ * @param {string} source HTTP source to obtain the data from (`ajax`)
++ * @param {array} data A key/value pair object containing the data to send
++ * to the server
++ * @param {function} callback to be called on completion of the data get
++ * process that will draw the data on the page.
++ * @param {object} settings DataTables settings object
++ *
++ * @dtopt Callbacks
++ * @dtopt Server-side
++ * @name DataTable.defaults.serverData
++ *
++ * @deprecated 1.10. Please use `ajax` for this functionality now.
++ */
++ "fnServerData": null,
++
++
++ /**
++ * __Deprecated__ The functionality provided by this parameter has now been
++ * superseded by that provided through `ajax`, which should be used instead.
++ *
++ * It is often useful to send extra data to the server when making an Ajax
++ * request - for example custom filtering information, and this callback
++ * function makes it trivial to send extra information to the server. The
++ * passed in parameter is the data set that has been constructed by
++ * DataTables, and you can add to this or modify it as you require.
++ * @type function
++ * @param {array} data Data array (array of objects which are name/value
++ * pairs) that has been constructed by DataTables and will be sent to the
++ * server. In the case of Ajax sourced data with server-side processing
++ * this will be an empty array, for server-side processing there will be a
++ * significant number of parameters!
++ * @returns {undefined} Ensure that you modify the data array passed in,
++ * as this is passed by reference.
++ *
++ * @dtopt Callbacks
++ * @dtopt Server-side
++ * @name DataTable.defaults.serverParams
++ *
++ * @deprecated 1.10. Please use `ajax` for this functionality now.
++ */
++ "fnServerParams": null,
++
++
++ /**
++ * Load the table state. With this function you can define from where, and how, the
++ * state of a table is loaded. By default DataTables will load from `localStorage`
++ * but you might wish to use a server-side database or cookies.
++ * @type function
++ * @member
++ * @param {object} settings DataTables settings object
++ * @return {object} The DataTables state object to be loaded
++ *
++ * @dtopt Callbacks
++ * @name DataTable.defaults.stateLoadCallback
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "stateSave": true,
++ * "stateLoadCallback": function (settings) {
++ * var o;
++ *
++ * // Send an Ajax request to the server to get the data. Note that
++ * // this is a synchronous request.
++ * $.ajax( {
++ * "url": "/state_load",
++ * "async": false,
++ * "dataType": "json",
++ * "success": function (json) {
++ * o = json;
++ * }
++ * } );
++ *
++ * return o;
++ * }
++ * } );
++ * } );
++ */
++ "fnStateLoadCallback": function ( settings ) {
++ try {
++ return JSON.parse(
++ (settings.iStateDuration === -1 ? sessionStorage : localStorage).getItem(
++ 'DataTables_'+settings.sInstance+'_'+location.pathname
++ )
++ );
++ } catch (e) {}
++ },
++
++
++ /**
++ * Callback which allows modification of the saved state prior to loading that state.
++ * This callback is called when the table is loading state from the stored data, but
++ * prior to the settings object being modified by the saved state. Note that for
++ * plug-in authors, you should use the `stateLoadParams` event to load parameters for
++ * a plug-in.
++ * @type function
++ * @param {object} settings DataTables settings object
++ * @param {object} data The state object that is to be loaded
++ *
++ * @dtopt Callbacks
++ * @name DataTable.defaults.stateLoadParams
++ *
++ * @example
++ * // Remove a saved filter, so filtering is never loaded
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "stateSave": true,
++ * "stateLoadParams": function (settings, data) {
++ * data.oSearch.sSearch = "";
++ * }
++ * } );
++ * } );
++ *
++ * @example
++ * // Disallow state loading by returning false
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "stateSave": true,
++ * "stateLoadParams": function (settings, data) {
++ * return false;
++ * }
++ * } );
++ * } );
++ */
++ "fnStateLoadParams": null,
++
++
++ /**
++ * Callback that is called when the state has been loaded from the state saving method
++ * and the DataTables settings object has been modified as a result of the loaded state.
++ * @type function
++ * @param {object} settings DataTables settings object
++ * @param {object} data The state object that was loaded
++ *
++ * @dtopt Callbacks
++ * @name DataTable.defaults.stateLoaded
++ *
++ * @example
++ * // Show an alert with the filtering value that was saved
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "stateSave": true,
++ * "stateLoaded": function (settings, data) {
++ * alert( 'Saved filter was: '+data.oSearch.sSearch );
++ * }
++ * } );
++ * } );
++ */
++ "fnStateLoaded": null,
++
++
++ /**
++ * Save the table state. This function allows you to define where and how the state
++ * information for the table is stored By default DataTables will use `localStorage`
++ * but you might wish to use a server-side database or cookies.
++ * @type function
++ * @member
++ * @param {object} settings DataTables settings object
++ * @param {object} data The state object to be saved
++ *
++ * @dtopt Callbacks
++ * @name DataTable.defaults.stateSaveCallback
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "stateSave": true,
++ * "stateSaveCallback": function (settings, data) {
++ * // Send an Ajax request to the server with the state object
++ * $.ajax( {
++ * "url": "/state_save",
++ * "data": data,
++ * "dataType": "json",
++ * "method": "POST"
++ * "success": function () {}
++ * } );
++ * }
++ * } );
++ * } );
++ */
++ "fnStateSaveCallback": function ( settings, data ) {
++ try {
++ (settings.iStateDuration === -1 ? sessionStorage : localStorage).setItem(
++ 'DataTables_'+settings.sInstance+'_'+location.pathname,
++ JSON.stringify( data )
++ );
++ } catch (e) {}
++ },
++
++
++ /**
++ * Callback which allows modification of the state to be saved. Called when the table
++ * has changed state a new state save is required. This method allows modification of
++ * the state saving object prior to actually doing the save, including addition or
++ * other state properties or modification. Note that for plug-in authors, you should
++ * use the `stateSaveParams` event to save parameters for a plug-in.
++ * @type function
++ * @param {object} settings DataTables settings object
++ * @param {object} data The state object to be saved
++ *
++ * @dtopt Callbacks
++ * @name DataTable.defaults.stateSaveParams
++ *
++ * @example
++ * // Remove a saved filter, so filtering is never saved
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "stateSave": true,
++ * "stateSaveParams": function (settings, data) {
++ * data.oSearch.sSearch = "";
++ * }
++ * } );
++ * } );
++ */
++ "fnStateSaveParams": null,
++
++
++ /**
++ * Duration for which the saved state information is considered valid. After this period
++ * has elapsed the state will be returned to the default.
++ * Value is given in seconds.
++ * @type int
++ * @default 7200 <i>(2 hours)</i>
++ *
++ * @dtopt Options
++ * @name DataTable.defaults.stateDuration
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "stateDuration": 60*60*24; // 1 day
++ * } );
++ * } )
++ */
++ "iStateDuration": 7200,
++
++
++ /**
++ * When enabled DataTables will not make a request to the server for the first
++ * page draw - rather it will use the data already on the page (no sorting etc
++ * will be applied to it), thus saving on an XHR at load time. `deferLoading`
++ * is used to indicate that deferred loading is required, but it is also used
++ * to tell DataTables how many records there are in the full table (allowing
++ * the information element and pagination to be displayed correctly). In the case
++ * where a filtering is applied to the table on initial load, this can be
++ * indicated by giving the parameter as an array, where the first element is
++ * the number of records available after filtering and the second element is the
++ * number of records without filtering (allowing the table information element
++ * to be shown correctly).
++ * @type int | array
++ * @default null
++ *
++ * @dtopt Options
++ * @name DataTable.defaults.deferLoading
++ *
++ * @example
++ * // 57 records available in the table, no filtering applied
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "serverSide": true,
++ * "ajax": "scripts/server_processing.php",
++ * "deferLoading": 57
++ * } );
++ * } );
++ *
++ * @example
++ * // 57 records after filtering, 100 without filtering (an initial filter applied)
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "serverSide": true,
++ * "ajax": "scripts/server_processing.php",
++ * "deferLoading": [ 57, 100 ],
++ * "search": {
++ * "search": "my_filter"
++ * }
++ * } );
++ * } );
++ */
++ "iDeferLoading": null,
++
++
++ /**
++ * Number of rows to display on a single page when using pagination. If
++ * feature enabled (`lengthChange`) then the end user will be able to override
++ * this to a custom setting using a pop-up menu.
++ * @type int
++ * @default 10
++ *
++ * @dtopt Options
++ * @name DataTable.defaults.pageLength
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "pageLength": 50
++ * } );
++ * } )
++ */
++ "iDisplayLength": 10,
++
++
++ /**
++ * Define the starting point for data display when using DataTables with
++ * pagination. Note that this parameter is the number of records, rather than
++ * the page number, so if you have 10 records per page and want to start on
++ * the third page, it should be "20".
++ * @type int
++ * @default 0
++ *
++ * @dtopt Options
++ * @name DataTable.defaults.displayStart
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "displayStart": 20
++ * } );
++ * } )
++ */
++ "iDisplayStart": 0,
++
++
++ /**
++ * By default DataTables allows keyboard navigation of the table (sorting, paging,
++ * and filtering) by adding a `tabindex` attribute to the required elements. This
++ * allows you to tab through the controls and press the enter key to activate them.
++ * The tabindex is default 0, meaning that the tab follows the flow of the document.
++ * You can overrule this using this parameter if you wish. Use a value of -1 to
++ * disable built-in keyboard navigation.
++ * @type int
++ * @default 0
++ *
++ * @dtopt Options
++ * @name DataTable.defaults.tabIndex
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "tabIndex": 1
++ * } );
++ * } );
++ */
++ "iTabIndex": 0,
++
++
++ /**
++ * Classes that DataTables assigns to the various components and features
++ * that it adds to the HTML table. This allows classes to be configured
++ * during initialisation in addition to through the static
++ * {@link DataTable.ext.oStdClasses} object).
++ * @namespace
++ * @name DataTable.defaults.classes
++ */
++ "oClasses": {},
++
++
++ /**
++ * All strings that DataTables uses in the user interface that it creates
++ * are defined in this object, allowing you to modified them individually or
++ * completely replace them all as required.
++ * @namespace
++ * @name DataTable.defaults.language
++ */
++ "oLanguage": {
++ /**
++ * Strings that are used for WAI-ARIA labels and controls only (these are not
++ * actually visible on the page, but will be read by screenreaders, and thus
++ * must be internationalised as well).
++ * @namespace
++ * @name DataTable.defaults.language.aria
++ */
++ "oAria": {
++ /**
++ * ARIA label that is added to the table headers when the column may be
++ * sorted ascending by activing the column (click or return when focused).
++ * Note that the column header is prefixed to this string.
++ * @type string
++ * @default : activate to sort column ascending
++ *
++ * @dtopt Language
++ * @name DataTable.defaults.language.aria.sortAscending
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "language": {
++ * "aria": {
++ * "sortAscending": " - click/return to sort ascending"
++ * }
++ * }
++ * } );
++ * } );
++ */
++ "sSortAscending": ": activate to sort column ascending",
++
++ /**
++ * ARIA label that is added to the table headers when the column may be
++ * sorted descending by activing the column (click or return when focused).
++ * Note that the column header is prefixed to this string.
++ * @type string
++ * @default : activate to sort column ascending
++ *
++ * @dtopt Language
++ * @name DataTable.defaults.language.aria.sortDescending
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "language": {
++ * "aria": {
++ * "sortDescending": " - click/return to sort descending"
++ * }
++ * }
++ * } );
++ * } );
++ */
++ "sSortDescending": ": activate to sort column descending"
++ },
++
++ /**
++ * Pagination string used by DataTables for the built-in pagination
++ * control types.
++ * @namespace
++ * @name DataTable.defaults.language.paginate
++ */
++ "oPaginate": {
++ /**
++ * Text to use when using the 'full_numbers' type of pagination for the
++ * button to take the user to the first page.
++ * @type string
++ * @default First
++ *
++ * @dtopt Language
++ * @name DataTable.defaults.language.paginate.first
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "language": {
++ * "paginate": {
++ * "first": "First page"
++ * }
++ * }
++ * } );
++ * } );
++ */
++ "sFirst": "First",
++
++
++ /**
++ * Text to use when using the 'full_numbers' type of pagination for the
++ * button to take the user to the last page.
++ * @type string
++ * @default Last
++ *
++ * @dtopt Language
++ * @name DataTable.defaults.language.paginate.last
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "language": {
++ * "paginate": {
++ * "last": "Last page"
++ * }
++ * }
++ * } );
++ * } );
++ */
++ "sLast": "Last",
++
++
++ /**
++ * Text to use for the 'next' pagination button (to take the user to the
++ * next page).
++ * @type string
++ * @default Next
++ *
++ * @dtopt Language
++ * @name DataTable.defaults.language.paginate.next
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "language": {
++ * "paginate": {
++ * "next": "Next page"
++ * }
++ * }
++ * } );
++ * } );
++ */
++ "sNext": "Next",
++
++
++ /**
++ * Text to use for the 'previous' pagination button (to take the user to
++ * the previous page).
++ * @type string
++ * @default Previous
++ *
++ * @dtopt Language
++ * @name DataTable.defaults.language.paginate.previous
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "language": {
++ * "paginate": {
++ * "previous": "Previous page"
++ * }
++ * }
++ * } );
++ * } );
++ */
++ "sPrevious": "Previous"
++ },
++
++ /**
++ * This string is shown in preference to `zeroRecords` when the table is
++ * empty of data (regardless of filtering). Note that this is an optional
++ * parameter - if it is not given, the value of `zeroRecords` will be used
++ * instead (either the default or given value).
++ * @type string
++ * @default No data available in table
++ *
++ * @dtopt Language
++ * @name DataTable.defaults.language.emptyTable
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "language": {
++ * "emptyTable": "No data available in table"
++ * }
++ * } );
++ * } );
++ */
++ "sEmptyTable": "No data available in table",
++
++
++ /**
++ * This string gives information to the end user about the information
++ * that is current on display on the page. The following tokens can be
++ * used in the string and will be dynamically replaced as the table
++ * display updates. This tokens can be placed anywhere in the string, or
++ * removed as needed by the language requires:
++ *
++ * * `\_START\_` - Display index of the first record on the current page
++ * * `\_END\_` - Display index of the last record on the current page
++ * * `\_TOTAL\_` - Number of records in the table after filtering
++ * * `\_MAX\_` - Number of records in the table without filtering
++ * * `\_PAGE\_` - Current page number
++ * * `\_PAGES\_` - Total number of pages of data in the table
++ *
++ * @type string
++ * @default Showing _START_ to _END_ of _TOTAL_ entries
++ *
++ * @dtopt Language
++ * @name DataTable.defaults.language.info
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "language": {
++ * "info": "Showing page _PAGE_ of _PAGES_"
++ * }
++ * } );
++ * } );
++ */
++ "sInfo": "Showing _START_ to _END_ of _TOTAL_ entries",
++
++
++ /**
++ * Display information string for when the table is empty. Typically the
++ * format of this string should match `info`.
++ * @type string
++ * @default Showing 0 to 0 of 0 entries
++ *
++ * @dtopt Language
++ * @name DataTable.defaults.language.infoEmpty
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "language": {
++ * "infoEmpty": "No entries to show"
++ * }
++ * } );
++ * } );
++ */
++ "sInfoEmpty": "Showing 0 to 0 of 0 entries",
++
++
++ /**
++ * When a user filters the information in a table, this string is appended
++ * to the information (`info`) to give an idea of how strong the filtering
++ * is. The variable _MAX_ is dynamically updated.
++ * @type string
++ * @default (filtered from _MAX_ total entries)
++ *
++ * @dtopt Language
++ * @name DataTable.defaults.language.infoFiltered
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "language": {
++ * "infoFiltered": " - filtering from _MAX_ records"
++ * }
++ * } );
++ * } );
++ */
++ "sInfoFiltered": "(filtered from _MAX_ total entries)",
++
++
++ /**
++ * If can be useful to append extra information to the info string at times,
++ * and this variable does exactly that. This information will be appended to
++ * the `info` (`infoEmpty` and `infoFiltered` in whatever combination they are
++ * being used) at all times.
++ * @type string
++ * @default <i>Empty string</i>
++ *
++ * @dtopt Language
++ * @name DataTable.defaults.language.infoPostFix
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "language": {
++ * "infoPostFix": "All records shown are derived from real information."
++ * }
++ * } );
++ * } );
++ */
++ "sInfoPostFix": "",
++
++
++ /**
++ * This decimal place operator is a little different from the other
++ * language options since DataTables doesn't output floating point
++ * numbers, so it won't ever use this for display of a number. Rather,
++ * what this parameter does is modify the sort methods of the table so
++ * that numbers which are in a format which has a character other than
++ * a period (`.`) as a decimal place will be sorted numerically.
++ *
++ * Note that numbers with different decimal places cannot be shown in
++ * the same table and still be sortable, the table must be consistent.
++ * However, multiple different tables on the page can use different
++ * decimal place characters.
++ * @type string
++ * @default
++ *
++ * @dtopt Language
++ * @name DataTable.defaults.language.decimal
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "language": {
++ * "decimal": ","
++ * "thousands": "."
++ * }
++ * } );
++ * } );
++ */
++ "sDecimal": "",
++
++
++ /**
++ * DataTables has a build in number formatter (`formatNumber`) which is
++ * used to format large numbers that are used in the table information.
++ * By default a comma is used, but this can be trivially changed to any
++ * character you wish with this parameter.
++ * @type string
++ * @default ,
++ *
++ * @dtopt Language
++ * @name DataTable.defaults.language.thousands
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "language": {
++ * "thousands": "'"
++ * }
++ * } );
++ * } );
++ */
++ "sThousands": ",",
++
++
++ /**
++ * Detail the action that will be taken when the drop down menu for the
++ * pagination length option is changed. The '_MENU_' variable is replaced
++ * with a default select list of 10, 25, 50 and 100, and can be replaced
++ * with a custom select box if required.
++ * @type string
++ * @default Show _MENU_ entries
++ *
++ * @dtopt Language
++ * @name DataTable.defaults.language.lengthMenu
++ *
++ * @example
++ * // Language change only
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "language": {
++ * "lengthMenu": "Display _MENU_ records"
++ * }
++ * } );
++ * } );
++ *
++ * @example
++ * // Language and options change
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "language": {
++ * "lengthMenu": 'Display <select>'+
++ * '<option value="10">10</option>'+
++ * '<option value="20">20</option>'+
++ * '<option value="30">30</option>'+
++ * '<option value="40">40</option>'+
++ * '<option value="50">50</option>'+
++ * '<option value="-1">All</option>'+
++ * '</select> records'
++ * }
++ * } );
++ * } );
++ */
++ "sLengthMenu": "Show _MENU_ entries",
++
++
++ /**
++ * When using Ajax sourced data and during the first draw when DataTables is
++ * gathering the data, this message is shown in an empty row in the table to
++ * indicate to the end user the the data is being loaded. Note that this
++ * parameter is not used when loading data by server-side processing, just
++ * Ajax sourced data with client-side processing.
++ * @type string
++ * @default Loading...
++ *
++ * @dtopt Language
++ * @name DataTable.defaults.language.loadingRecords
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "language": {
++ * "loadingRecords": "Please wait - loading..."
++ * }
++ * } );
++ * } );
++ */
++ "sLoadingRecords": "Loading...",
++
++
++ /**
++ * Text which is displayed when the table is processing a user action
++ * (usually a sort command or similar).
++ * @type string
++ * @default Processing...
++ *
++ * @dtopt Language
++ * @name DataTable.defaults.language.processing
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "language": {
++ * "processing": "DataTables is currently busy"
++ * }
++ * } );
++ * } );
++ */
++ "sProcessing": "Processing...",
++
++
++ /**
++ * Details the actions that will be taken when the user types into the
++ * filtering input text box. The variable "_INPUT_", if used in the string,
++ * is replaced with the HTML text box for the filtering input allowing
++ * control over where it appears in the string. If "_INPUT_" is not given
++ * then the input box is appended to the string automatically.
++ * @type string
++ * @default Search:
++ *
++ * @dtopt Language
++ * @name DataTable.defaults.language.search
++ *
++ * @example
++ * // Input text box will be appended at the end automatically
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "language": {
++ * "search": "Filter records:"
++ * }
++ * } );
++ * } );
++ *
++ * @example
++ * // Specify where the filter should appear
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "language": {
++ * "search": "Apply filter _INPUT_ to table"
++ * }
++ * } );
++ * } );
++ */
++ "sSearch": "Search:",
++
++
++ /**
++ * Assign a `placeholder` attribute to the search `input` element
++ * @type string
++ * @default
++ *
++ * @dtopt Language
++ * @name DataTable.defaults.language.searchPlaceholder
++ */
++ "sSearchPlaceholder": "",
++
++
++ /**
++ * All of the language information can be stored in a file on the
++ * server-side, which DataTables will look up if this parameter is passed.
++ * It must store the URL of the language file, which is in a JSON format,
++ * and the object has the same properties as the oLanguage object in the
++ * initialiser object (i.e. the above parameters). Please refer to one of
++ * the example language files to see how this works in action.
++ * @type string
++ * @default <i>Empty string - i.e. disabled</i>
++ *
++ * @dtopt Language
++ * @name DataTable.defaults.language.url
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "language": {
++ * "url": "http://www.sprymedia.co.uk/dataTables/lang.txt"
++ * }
++ * } );
++ * } );
++ */
++ "sUrl": "",
++
++
++ /**
++ * Text shown inside the table records when the is no information to be
++ * displayed after filtering. `emptyTable` is shown when there is simply no
++ * information in the table at all (regardless of filtering).
++ * @type string
++ * @default No matching records found
++ *
++ * @dtopt Language
++ * @name DataTable.defaults.language.zeroRecords
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "language": {
++ * "zeroRecords": "No records to display"
++ * }
++ * } );
++ * } );
++ */
++ "sZeroRecords": "No matching records found"
++ },
++
++
++ /**
++ * This parameter allows you to have define the global filtering state at
++ * initialisation time. As an object the `search` parameter must be
++ * defined, but all other parameters are optional. When `regex` is true,
++ * the search string will be treated as a regular expression, when false
++ * (default) it will be treated as a straight string. When `smart`
++ * DataTables will use it's smart filtering methods (to word match at
++ * any point in the data), when false this will not be done.
++ * @namespace
++ * @extends DataTable.models.oSearch
++ *
++ * @dtopt Options
++ * @name DataTable.defaults.search
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "search": {"search": "Initial search"}
++ * } );
++ * } )
++ */
++ "oSearch": $.extend( {}, DataTable.models.oSearch ),
++
++
++ /**
++ * __Deprecated__ The functionality provided by this parameter has now been
++ * superseded by that provided through `ajax`, which should be used instead.
++ *
++ * By default DataTables will look for the property `data` (or `aaData` for
++ * compatibility with DataTables 1.9-) when obtaining data from an Ajax
++ * source or for server-side processing - this parameter allows that
++ * property to be changed. You can use Javascript dotted object notation to
++ * get a data source for multiple levels of nesting.
++ * @type string
++ * @default data
++ *
++ * @dtopt Options
++ * @dtopt Server-side
++ * @name DataTable.defaults.ajaxDataProp
++ *
++ * @deprecated 1.10. Please use `ajax` for this functionality now.
++ */
++ "sAjaxDataProp": "data",
++
++
++ /**
++ * __Deprecated__ The functionality provided by this parameter has now been
++ * superseded by that provided through `ajax`, which should be used instead.
++ *
++ * You can instruct DataTables to load data from an external
++ * source using this parameter (use aData if you want to pass data in you
++ * already have). Simply provide a url a JSON object can be obtained from.
++ * @type string
++ * @default null
++ *
++ * @dtopt Options
++ * @dtopt Server-side
++ * @name DataTable.defaults.ajaxSource
++ *
++ * @deprecated 1.10. Please use `ajax` for this functionality now.
++ */
++ "sAjaxSource": null,
++
++
++ /**
++ * This initialisation variable allows you to specify exactly where in the
++ * DOM you want DataTables to inject the various controls it adds to the page
++ * (for example you might want the pagination controls at the top of the
++ * table). DIV elements (with or without a custom class) can also be added to
++ * aid styling. The follow syntax is used:
++ * <ul>
++ * <li>The following options are allowed:
++ * <ul>
++ * <li>'l' - Length changing</li>
++ * <li>'f' - Filtering input</li>
++ * <li>'t' - The table!</li>
++ * <li>'i' - Information</li>
++ * <li>'p' - Pagination</li>
++ * <li>'r' - pRocessing</li>
++ * </ul>
++ * </li>
++ * <li>The following constants are allowed:
++ * <ul>
++ * <li>'H' - jQueryUI theme "header" classes ('fg-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix')</li>
++ * <li>'F' - jQueryUI theme "footer" classes ('fg-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix')</li>
++ * </ul>
++ * </li>
++ * <li>The following syntax is expected:
++ * <ul>
++ * <li>'<' and '>' - div elements</li>
++ * <li>'<"class" and '>' - div with a class</li>
++ * <li>'<"#id" and '>' - div with an ID</li>
++ * </ul>
++ * </li>
++ * <li>Examples:
++ * <ul>
++ * <li>'<"wrapper"flipt>'</li>
++ * <li>'<lf<t>ip>'</li>
++ * </ul>
++ * </li>
++ * </ul>
++ * @type string
++ * @default lfrtip <i>(when `jQueryUI` is false)</i> <b>or</b>
++ * <"H"lfr>t<"F"ip> <i>(when `jQueryUI` is true)</i>
++ *
++ * @dtopt Options
++ * @name DataTable.defaults.dom
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "dom": '<"top"i>rt<"bottom"flp><"clear">'
++ * } );
++ * } );
++ */
++ "sDom": "lfrtip",
++
++
++ /**
++ * Search delay option. This will throttle full table searches that use the
++ * DataTables provided search input element (it does not effect calls to
++ * `dt-api search()`, providing a delay before the search is made.
++ * @type integer
++ * @default 0
++ *
++ * @dtopt Options
++ * @name DataTable.defaults.searchDelay
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "searchDelay": 200
++ * } );
++ * } )
++ */
++ "searchDelay": null,
++
++
++ /**
++ * DataTables features four different built-in options for the buttons to
++ * display for pagination control:
++ *
++ * * `simple` - 'Previous' and 'Next' buttons only
++ * * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers
++ * * `full` - 'First', 'Previous', 'Next' and 'Last' buttons
++ * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus
++ * page numbers
++ *
++ * Further methods can be added using {@link DataTable.ext.oPagination}.
++ * @type string
++ * @default simple_numbers
++ *
++ * @dtopt Options
++ * @name DataTable.defaults.pagingType
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "pagingType": "full_numbers"
++ * } );
++ * } )
++ */
++ "sPaginationType": "simple_numbers",
++
++
++ /**
++ * Enable horizontal scrolling. When a table is too wide to fit into a
++ * certain layout, or you have a large number of columns in the table, you
++ * can enable x-scrolling to show the table in a viewport, which can be
++ * scrolled. This property can be `true` which will allow the table to
++ * scroll horizontally when needed, or any CSS unit, or a number (in which
++ * case it will be treated as a pixel measurement). Setting as simply `true`
++ * is recommended.
++ * @type boolean|string
++ * @default <i>blank string - i.e. disabled</i>
++ *
++ * @dtopt Features
++ * @name DataTable.defaults.scrollX
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "scrollX": true,
++ * "scrollCollapse": true
++ * } );
++ * } );
++ */
++ "sScrollX": "",
++
++
++ /**
++ * This property can be used to force a DataTable to use more width than it
++ * might otherwise do when x-scrolling is enabled. For example if you have a
++ * table which requires to be well spaced, this parameter is useful for
++ * "over-sizing" the table, and thus forcing scrolling. This property can by
++ * any CSS unit, or a number (in which case it will be treated as a pixel
++ * measurement).
++ * @type string
++ * @default <i>blank string - i.e. disabled</i>
++ *
++ * @dtopt Options
++ * @name DataTable.defaults.scrollXInner
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "scrollX": "100%",
++ * "scrollXInner": "110%"
++ * } );
++ * } );
++ */
++ "sScrollXInner": "",
++
++
++ /**
++ * Enable vertical scrolling. Vertical scrolling will constrain the DataTable
++ * to the given height, and enable scrolling for any data which overflows the
++ * current viewport. This can be used as an alternative to paging to display
++ * a lot of data in a small area (although paging and scrolling can both be
++ * enabled at the same time). This property can be any CSS unit, or a number
++ * (in which case it will be treated as a pixel measurement).
++ * @type string
++ * @default <i>blank string - i.e. disabled</i>
++ *
++ * @dtopt Features
++ * @name DataTable.defaults.scrollY
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "scrollY": "200px",
++ * "paginate": false
++ * } );
++ * } );
++ */
++ "sScrollY": "",
++
++
++ /**
++ * __Deprecated__ The functionality provided by this parameter has now been
++ * superseded by that provided through `ajax`, which should be used instead.
++ *
++ * Set the HTTP method that is used to make the Ajax call for server-side
++ * processing or Ajax sourced data.
++ * @type string
++ * @default GET
++ *
++ * @dtopt Options
++ * @dtopt Server-side
++ * @name DataTable.defaults.serverMethod
++ *
++ * @deprecated 1.10. Please use `ajax` for this functionality now.
++ */
++ "sServerMethod": "GET",
++
++
++ /**
++ * DataTables makes use of renderers when displaying HTML elements for
++ * a table. These renderers can be added or modified by plug-ins to
++ * generate suitable mark-up for a site. For example the Bootstrap
++ * integration plug-in for DataTables uses a paging button renderer to
++ * display pagination buttons in the mark-up required by Bootstrap.
++ *
++ * For further information about the renderers available see
++ * DataTable.ext.renderer
++ * @type string|object
++ * @default null
++ *
++ * @name DataTable.defaults.renderer
++ *
++ */
++ "renderer": null
++ };
++
++ _fnHungarianMap( DataTable.defaults );
++
++
++
++ /*
++ * Developer note - See note in model.defaults.js about the use of Hungarian
++ * notation and camel case.
++ */
++
++ /**
++ * Column options that can be given to DataTables at initialisation time.
++ * @namespace
++ */
++ DataTable.defaults.column = {
++ /**
++ * Define which column(s) an order will occur on for this column. This
++ * allows a column's ordering to take multiple columns into account when
++ * doing a sort or use the data from a different column. For example first
++ * name / last name columns make sense to do a multi-column sort over the
++ * two columns.
++ * @type array|int
++ * @default null <i>Takes the value of the column index automatically</i>
++ *
++ * @name DataTable.defaults.column.orderData
++ * @dtopt Columns
++ *
++ * @example
++ * // Using `columnDefs`
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "columnDefs": [
++ * { "orderData": [ 0, 1 ], "targets": [ 0 ] },
++ * { "orderData": [ 1, 0 ], "targets": [ 1 ] },
++ * { "orderData": 2, "targets": [ 2 ] }
++ * ]
++ * } );
++ * } );
++ *
++ * @example
++ * // Using `columns`
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "columns": [
++ * { "orderData": [ 0, 1 ] },
++ * { "orderData": [ 1, 0 ] },
++ * { "orderData": 2 },
++ * null,
++ * null
++ * ]
++ * } );
++ * } );
++ */
++ "aDataSort": null,
++ "iDataSort": -1,
++
++
++ /**
++ * You can control the default ordering direction, and even alter the
++ * behaviour of the sort handler (i.e. only allow ascending ordering etc)
++ * using this parameter.
++ * @type array
++ * @default [ 'asc', 'desc' ]
++ *
++ * @name DataTable.defaults.column.orderSequence
++ * @dtopt Columns
++ *
++ * @example
++ * // Using `columnDefs`
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "columnDefs": [
++ * { "orderSequence": [ "asc" ], "targets": [ 1 ] },
++ * { "orderSequence": [ "desc", "asc", "asc" ], "targets": [ 2 ] },
++ * { "orderSequence": [ "desc" ], "targets": [ 3 ] }
++ * ]
++ * } );
++ * } );
++ *
++ * @example
++ * // Using `columns`
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "columns": [
++ * null,
++ * { "orderSequence": [ "asc" ] },
++ * { "orderSequence": [ "desc", "asc", "asc" ] },
++ * { "orderSequence": [ "desc" ] },
++ * null
++ * ]
++ * } );
++ * } );
++ */
++ "asSorting": [ 'asc', 'desc' ],
++
++
++ /**
++ * Enable or disable filtering on the data in this column.
++ * @type boolean
++ * @default true
++ *
++ * @name DataTable.defaults.column.searchable
++ * @dtopt Columns
++ *
++ * @example
++ * // Using `columnDefs`
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "columnDefs": [
++ * { "searchable": false, "targets": [ 0 ] }
++ * ] } );
++ * } );
++ *
++ * @example
++ * // Using `columns`
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "columns": [
++ * { "searchable": false },
++ * null,
++ * null,
++ * null,
++ * null
++ * ] } );
++ * } );
++ */
++ "bSearchable": true,
++
++
++ /**
++ * Enable or disable ordering on this column.
++ * @type boolean
++ * @default true
++ *
++ * @name DataTable.defaults.column.orderable
++ * @dtopt Columns
++ *
++ * @example
++ * // Using `columnDefs`
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "columnDefs": [
++ * { "orderable": false, "targets": [ 0 ] }
++ * ] } );
++ * } );
++ *
++ * @example
++ * // Using `columns`
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "columns": [
++ * { "orderable": false },
++ * null,
++ * null,
++ * null,
++ * null
++ * ] } );
++ * } );
++ */
++ "bSortable": true,
++
++
++ /**
++ * Enable or disable the display of this column.
++ * @type boolean
++ * @default true
++ *
++ * @name DataTable.defaults.column.visible
++ * @dtopt Columns
++ *
++ * @example
++ * // Using `columnDefs`
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "columnDefs": [
++ * { "visible": false, "targets": [ 0 ] }
++ * ] } );
++ * } );
++ *
++ * @example
++ * // Using `columns`
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "columns": [
++ * { "visible": false },
++ * null,
++ * null,
++ * null,
++ * null
++ * ] } );
++ * } );
++ */
++ "bVisible": true,
++
++
++ /**
++ * Developer definable function that is called whenever a cell is created (Ajax source,
++ * etc) or processed for input (DOM source). This can be used as a compliment to mRender
++ * allowing you to modify the DOM element (add background colour for example) when the
++ * element is available.
++ * @type function
++ * @param {element} td The TD node that has been created
++ * @param {*} cellData The Data for the cell
++ * @param {array|object} rowData The data for the whole row
++ * @param {int} row The row index for the aoData data store
++ * @param {int} col The column index for aoColumns
++ *
++ * @name DataTable.defaults.column.createdCell
++ * @dtopt Columns
++ *
++ * @example
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "columnDefs": [ {
++ * "targets": [3],
++ * "createdCell": function (td, cellData, rowData, row, col) {
++ * if ( cellData == "1.7" ) {
++ * $(td).css('color', 'blue')
++ * }
++ * }
++ * } ]
++ * });
++ * } );
++ */
++ "fnCreatedCell": null,
++
++
++ /**
++ * This parameter has been replaced by `data` in DataTables to ensure naming
++ * consistency. `dataProp` can still be used, as there is backwards
++ * compatibility in DataTables for this option, but it is strongly
++ * recommended that you use `data` in preference to `dataProp`.
++ * @name DataTable.defaults.column.dataProp
++ */
++
++
++ /**
++ * This property can be used to read data from any data source property,
++ * including deeply nested objects / properties. `data` can be given in a
++ * number of different ways which effect its behaviour:
++ *
++ * * `integer` - treated as an array index for the data source. This is the
++ * default that DataTables uses (incrementally increased for each column).
++ * * `string` - read an object property from the data source. There are
++ * three 'special' options that can be used in the string to alter how
++ * DataTables reads the data from the source object:
++ * * `.` - Dotted Javascript notation. Just as you use a `.` in
++ * Javascript to read from nested objects, so to can the options
++ * specified in `data`. For example: `browser.version` or
++ * `browser.name`. If your object parameter name contains a period, use
++ * `\\` to escape it - i.e. `first\\.name`.
++ * * `[]` - Array notation. DataTables can automatically combine data
++ * from and array source, joining the data with the characters provided
++ * between the two brackets. For example: `name[, ]` would provide a
++ * comma-space separated list from the source array. If no characters
++ * are provided between the brackets, the original array source is
++ * returned.
++ * * `()` - Function notation. Adding `()` to the end of a parameter will
++ * execute a function of the name given. For example: `browser()` for a
++ * simple function on the data source, `browser.version()` for a
++ * function in a nested property or even `browser().version` to get an
++ * object property if the function called returns an object. Note that
++ * function notation is recommended for use in `render` rather than
++ * `data` as it is much simpler to use as a renderer.
++ * * `null` - use the original data source for the row rather than plucking
++ * data directly from it. This action has effects on two other
++ * initialisation options:
++ * * `defaultContent` - When null is given as the `data` option and
++ * `defaultContent` is specified for the column, the value defined by
++ * `defaultContent` will be used for the cell.
++ * * `render` - When null is used for the `data` option and the `render`
++ * option is specified for the column, the whole data source for the
++ * row is used for the renderer.
++ * * `function` - the function given will be executed whenever DataTables
++ * needs to set or get the data for a cell in the column. The function
++ * takes three parameters:
++ * * Parameters:
++ * * `{array|object}` The data source for the row
++ * * `{string}` The type call data requested - this will be 'set' when
++ * setting data or 'filter', 'display', 'type', 'sort' or undefined
++ * when gathering data. Note that when `undefined` is given for the
++ * type DataTables expects to get the raw data for the object back<
++ * * `{*}` Data to set when the second parameter is 'set'.
++ * * Return:
++ * * The return value from the function is not required when 'set' is
++ * the type of call, but otherwise the return is what will be used
++ * for the data requested.
++ *
++ * Note that `data` is a getter and setter option. If you just require
++ * formatting of data for output, you will likely want to use `render` which
++ * is simply a getter and thus simpler to use.
++ *
++ * Note that prior to DataTables 1.9.2 `data` was called `mDataProp`. The
++ * name change reflects the flexibility of this property and is consistent
++ * with the naming of mRender. If 'mDataProp' is given, then it will still
++ * be used by DataTables, as it automatically maps the old name to the new
++ * if required.
++ *
++ * @type string|int|function|null
++ * @default null <i>Use automatically calculated column index</i>
++ *
++ * @name DataTable.defaults.column.data
++ * @dtopt Columns
++ *
++ * @example
++ * // Read table data from objects
++ * // JSON structure for each row:
++ * // {
++ * // "engine": {value},
++ * // "browser": {value},
++ * // "platform": {value},
++ * // "version": {value},
++ * // "grade": {value}
++ * // }
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "ajaxSource": "sources/objects.txt",
++ * "columns": [
++ * { "data": "engine" },
++ * { "data": "browser" },
++ * { "data": "platform" },
++ * { "data": "version" },
++ * { "data": "grade" }
++ * ]
++ * } );
++ * } );
++ *
++ * @example
++ * // Read information from deeply nested objects
++ * // JSON structure for each row:
++ * // {
++ * // "engine": {value},
++ * // "browser": {value},
++ * // "platform": {
++ * // "inner": {value}
++ * // },
++ * // "details": [
++ * // {value}, {value}
++ * // ]
++ * // }
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "ajaxSource": "sources/deep.txt",
++ * "columns": [
++ * { "data": "engine" },
++ * { "data": "browser" },
++ * { "data": "platform.inner" },
++ * { "data": "platform.details.0" },
++ * { "data": "platform.details.1" }
++ * ]
++ * } );
++ * } );
++ *
++ * @example
++ * // Using `data` as a function to provide different information for
++ * // sorting, filtering and display. In this case, currency (price)
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "columnDefs": [ {
++ * "targets": [ 0 ],
++ * "data": function ( source, type, val ) {
++ * if (type === 'set') {
++ * source.price = val;
++ * // Store the computed dislay and filter values for efficiency
++ * source.price_display = val=="" ? "" : "$"+numberFormat(val);
++ * source.price_filter = val=="" ? "" : "$"+numberFormat(val)+" "+val;
++ * return;
++ * }
++ * else if (type === 'display') {
++ * return source.price_display;
++ * }
++ * else if (type === 'filter') {
++ * return source.price_filter;
++ * }
++ * // 'sort', 'type' and undefined all just use the integer
++ * return source.price;
++ * }
++ * } ]
++ * } );
++ * } );
++ *
++ * @example
++ * // Using default content
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "columnDefs": [ {
++ * "targets": [ 0 ],
++ * "data": null,
++ * "defaultContent": "Click to edit"
++ * } ]
++ * } );
++ * } );
++ *
++ * @example
++ * // Using array notation - outputting a list from an array
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "columnDefs": [ {
++ * "targets": [ 0 ],
++ * "data": "name[, ]"
++ * } ]
++ * } );
++ * } );
++ *
++ */
++ "mData": null,
++
++
++ /**
++ * This property is the rendering partner to `data` and it is suggested that
++ * when you want to manipulate data for display (including filtering,
++ * sorting etc) without altering the underlying data for the table, use this
++ * property. `render` can be considered to be the the read only companion to
++ * `data` which is read / write (then as such more complex). Like `data`
++ * this option can be given in a number of different ways to effect its
++ * behaviour:
++ *
++ * * `integer` - treated as an array index for the data source. This is the
++ * default that DataTables uses (incrementally increased for each column).
++ * * `string` - read an object property from the data source. There are
++ * three 'special' options that can be used in the string to alter how
++ * DataTables reads the data from the source object:
++ * * `.` - Dotted Javascript notation. Just as you use a `.` in
++ * Javascript to read from nested objects, so to can the options
++ * specified in `data`. For example: `browser.version` or
++ * `browser.name`. If your object parameter name contains a period, use
++ * `\\` to escape it - i.e. `first\\.name`.
++ * * `[]` - Array notation. DataTables can automatically combine data
++ * from and array source, joining the data with the characters provided
++ * between the two brackets. For example: `name[, ]` would provide a
++ * comma-space separated list from the source array. If no characters
++ * are provided between the brackets, the original array source is
++ * returned.
++ * * `()` - Function notation. Adding `()` to the end of a parameter will
++ * execute a function of the name given. For example: `browser()` for a
++ * simple function on the data source, `browser.version()` for a
++ * function in a nested property or even `browser().version` to get an
++ * object property if the function called returns an object.
++ * * `object` - use different data for the different data types requested by
++ * DataTables ('filter', 'display', 'type' or 'sort'). The property names
++ * of the object is the data type the property refers to and the value can
++ * defined using an integer, string or function using the same rules as
++ * `render` normally does. Note that an `_` option _must_ be specified.
++ * This is the default value to use if you haven't specified a value for
++ * the data type requested by DataTables.
++ * * `function` - the function given will be executed whenever DataTables
++ * needs to set or get the data for a cell in the column. The function
++ * takes three parameters:
++ * * Parameters:
++ * * {array|object} The data source for the row (based on `data`)
++ * * {string} The type call data requested - this will be 'filter',
++ * 'display', 'type' or 'sort'.
++ * * {array|object} The full data source for the row (not based on
++ * `data`)
++ * * Return:
++ * * The return value from the function is what will be used for the
++ * data requested.
++ *
++ * @type string|int|function|object|null
++ * @default null Use the data source value.
++ *
++ * @name DataTable.defaults.column.render
++ * @dtopt Columns
++ *
++ * @example
++ * // Create a comma separated list from an array of objects
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "ajaxSource": "sources/deep.txt",
++ * "columns": [
++ * { "data": "engine" },
++ * { "data": "browser" },
++ * {
++ * "data": "platform",
++ * "render": "[, ].name"
++ * }
++ * ]
++ * } );
++ * } );
++ *
++ * @example
++ * // Execute a function to obtain data
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "columnDefs": [ {
++ * "targets": [ 0 ],
++ * "data": null, // Use the full data source object for the renderer's source
++ * "render": "browserName()"
++ * } ]
++ * } );
++ * } );
++ *
++ * @example
++ * // As an object, extracting different data for the different types
++ * // This would be used with a data source such as:
++ * // { "phone": 5552368, "phone_filter": "5552368 555-2368", "phone_display": "555-2368" }
++ * // Here the `phone` integer is used for sorting and type detection, while `phone_filter`
++ * // (which has both forms) is used for filtering for if a user inputs either format, while
++ * // the formatted phone number is the one that is shown in the table.
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "columnDefs": [ {
++ * "targets": [ 0 ],
++ * "data": null, // Use the full data source object for the renderer's source
++ * "render": {
++ * "_": "phone",
++ * "filter": "phone_filter",
++ * "display": "phone_display"
++ * }
++ * } ]
++ * } );
++ * } );
++ *
++ * @example
++ * // Use as a function to create a link from the data source
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "columnDefs": [ {
++ * "targets": [ 0 ],
++ * "data": "download_link",
++ * "render": function ( data, type, full ) {
++ * return '<a href="'+data+'">Download</a>';
++ * }
++ * } ]
++ * } );
++ * } );
++ */
++ "mRender": null,
++
++
++ /**
++ * Change the cell type created for the column - either TD cells or TH cells. This
++ * can be useful as TH cells have semantic meaning in the table body, allowing them
++ * to act as a header for a row (you may wish to add scope='row' to the TH elements).
++ * @type string
++ * @default td
++ *
++ * @name DataTable.defaults.column.cellType
++ * @dtopt Columns
++ *
++ * @example
++ * // Make the first column use TH cells
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "columnDefs": [ {
++ * "targets": [ 0 ],
++ * "cellType": "th"
++ * } ]
++ * } );
++ * } );
++ */
++ "sCellType": "td",
++
++
++ /**
++ * Class to give to each cell in this column.
++ * @type string
++ * @default <i>Empty string</i>
++ *
++ * @name DataTable.defaults.column.class
++ * @dtopt Columns
++ *
++ * @example
++ * // Using `columnDefs`
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "columnDefs": [
++ * { "class": "my_class", "targets": [ 0 ] }
++ * ]
++ * } );
++ * } );
++ *
++ * @example
++ * // Using `columns`
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "columns": [
++ * { "class": "my_class" },
++ * null,
++ * null,
++ * null,
++ * null
++ * ]
++ * } );
++ * } );
++ */
++ "sClass": "",
++
++ /**
++ * When DataTables calculates the column widths to assign to each column,
++ * it finds the longest string in each column and then constructs a
++ * temporary table and reads the widths from that. The problem with this
++ * is that "mmm" is much wider then "iiii", but the latter is a longer
++ * string - thus the calculation can go wrong (doing it properly and putting
++ * it into an DOM object and measuring that is horribly(!) slow). Thus as
++ * a "work around" we provide this option. It will append its value to the
++ * text that is found to be the longest string for the column - i.e. padding.
++ * Generally you shouldn't need this!
++ * @type string
++ * @default <i>Empty string<i>
++ *
++ * @name DataTable.defaults.column.contentPadding
++ * @dtopt Columns
++ *
++ * @example
++ * // Using `columns`
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "columns": [
++ * null,
++ * null,
++ * null,
++ * {
++ * "contentPadding": "mmm"
++ * }
++ * ]
++ * } );
++ * } );
++ */
++ "sContentPadding": "",
++
++
++ /**
++ * Allows a default value to be given for a column's data, and will be used
++ * whenever a null data source is encountered (this can be because `data`
++ * is set to null, or because the data source itself is null).
++ * @type string
++ * @default null
++ *
++ * @name DataTable.defaults.column.defaultContent
++ * @dtopt Columns
++ *
++ * @example
++ * // Using `columnDefs`
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "columnDefs": [
++ * {
++ * "data": null,
++ * "defaultContent": "Edit",
++ * "targets": [ -1 ]
++ * }
++ * ]
++ * } );
++ * } );
++ *
++ * @example
++ * // Using `columns`
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "columns": [
++ * null,
++ * null,
++ * null,
++ * {
++ * "data": null,
++ * "defaultContent": "Edit"
++ * }
++ * ]
++ * } );
++ * } );
++ */
++ "sDefaultContent": null,
++
++
++ /**
++ * This parameter is only used in DataTables' server-side processing. It can
++ * be exceptionally useful to know what columns are being displayed on the
++ * client side, and to map these to database fields. When defined, the names
++ * also allow DataTables to reorder information from the server if it comes
++ * back in an unexpected order (i.e. if you switch your columns around on the
++ * client-side, your server-side code does not also need updating).
++ * @type string
++ * @default <i>Empty string</i>
++ *
++ * @name DataTable.defaults.column.name
++ * @dtopt Columns
++ *
++ * @example
++ * // Using `columnDefs`
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "columnDefs": [
++ * { "name": "engine", "targets": [ 0 ] },
++ * { "name": "browser", "targets": [ 1 ] },
++ * { "name": "platform", "targets": [ 2 ] },
++ * { "name": "version", "targets": [ 3 ] },
++ * { "name": "grade", "targets": [ 4 ] }
++ * ]
++ * } );
++ * } );
++ *
++ * @example
++ * // Using `columns`
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "columns": [
++ * { "name": "engine" },
++ * { "name": "browser" },
++ * { "name": "platform" },
++ * { "name": "version" },
++ * { "name": "grade" }
++ * ]
++ * } );
++ * } );
++ */
++ "sName": "",
++
++
++ /**
++ * Defines a data source type for the ordering which can be used to read
++ * real-time information from the table (updating the internally cached
++ * version) prior to ordering. This allows ordering to occur on user
++ * editable elements such as form inputs.
++ * @type string
++ * @default std
++ *
++ * @name DataTable.defaults.column.orderDataType
++ * @dtopt Columns
++ *
++ * @example
++ * // Using `columnDefs`
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "columnDefs": [
++ * { "orderDataType": "dom-text", "targets": [ 2, 3 ] },
++ * { "type": "numeric", "targets": [ 3 ] },
++ * { "orderDataType": "dom-select", "targets": [ 4 ] },
++ * { "orderDataType": "dom-checkbox", "targets": [ 5 ] }
++ * ]
++ * } );
++ * } );
++ *
++ * @example
++ * // Using `columns`
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "columns": [
++ * null,
++ * null,
++ * { "orderDataType": "dom-text" },
++ * { "orderDataType": "dom-text", "type": "numeric" },
++ * { "orderDataType": "dom-select" },
++ * { "orderDataType": "dom-checkbox" }
++ * ]
++ * } );
++ * } );
++ */
++ "sSortDataType": "std",
++
++
++ /**
++ * The title of this column.
++ * @type string
++ * @default null <i>Derived from the 'TH' value for this column in the
++ * original HTML table.</i>
++ *
++ * @name DataTable.defaults.column.title
++ * @dtopt Columns
++ *
++ * @example
++ * // Using `columnDefs`
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "columnDefs": [
++ * { "title": "My column title", "targets": [ 0 ] }
++ * ]
++ * } );
++ * } );
++ *
++ * @example
++ * // Using `columns`
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "columns": [
++ * { "title": "My column title" },
++ * null,
++ * null,
++ * null,
++ * null
++ * ]
++ * } );
++ * } );
++ */
++ "sTitle": null,
++
++
++ /**
++ * The type allows you to specify how the data for this column will be
++ * ordered. Four types (string, numeric, date and html (which will strip
++ * HTML tags before ordering)) are currently available. Note that only date
++ * formats understood by Javascript's Date() object will be accepted as type
++ * date. For example: "Mar 26, 2008 5:03 PM". May take the values: 'string',
++ * 'numeric', 'date' or 'html' (by default). Further types can be adding
++ * through plug-ins.
++ * @type string
++ * @default null <i>Auto-detected from raw data</i>
++ *
++ * @name DataTable.defaults.column.type
++ * @dtopt Columns
++ *
++ * @example
++ * // Using `columnDefs`
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "columnDefs": [
++ * { "type": "html", "targets": [ 0 ] }
++ * ]
++ * } );
++ * } );
++ *
++ * @example
++ * // Using `columns`
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "columns": [
++ * { "type": "html" },
++ * null,
++ * null,
++ * null,
++ * null
++ * ]
++ * } );
++ * } );
++ */
++ "sType": null,
++
++
++ /**
++ * Defining the width of the column, this parameter may take any CSS value
++ * (3em, 20px etc). DataTables applies 'smart' widths to columns which have not
++ * been given a specific width through this interface ensuring that the table
++ * remains readable.
++ * @type string
++ * @default null <i>Automatic</i>
++ *
++ * @name DataTable.defaults.column.width
++ * @dtopt Columns
++ *
++ * @example
++ * // Using `columnDefs`
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "columnDefs": [
++ * { "width": "20%", "targets": [ 0 ] }
++ * ]
++ * } );
++ * } );
++ *
++ * @example
++ * // Using `columns`
++ * $(document).ready( function() {
++ * $('#example').dataTable( {
++ * "columns": [
++ * { "width": "20%" },
++ * null,
++ * null,
++ * null,
++ * null
++ * ]
++ * } );
++ * } );
++ */
++ "sWidth": null
++ };
++
++ _fnHungarianMap( DataTable.defaults.column );
++
++
++
++ /**
++ * DataTables settings object - this holds all the information needed for a
++ * given table, including configuration, data and current application of the
++ * table options. DataTables does not have a single instance for each DataTable
++ * with the settings attached to that instance, but rather instances of the
++ * DataTable "class" are created on-the-fly as needed (typically by a
++ * $().dataTable() call) and the settings object is then applied to that
++ * instance.
++ *
++ * Note that this object is related to {@link DataTable.defaults} but this
++ * one is the internal data store for DataTables's cache of columns. It should
++ * NOT be manipulated outside of DataTables. Any configuration should be done
++ * through the initialisation options.
++ * @namespace
++ * @todo Really should attach the settings object to individual instances so we
++ * don't need to create new instances on each $().dataTable() call (if the
++ * table already exists). It would also save passing oSettings around and
++ * into every single function. However, this is a very significant
++ * architecture change for DataTables and will almost certainly break
++ * backwards compatibility with older installations. This is something that
++ * will be done in 2.0.
++ */
++ DataTable.models.oSettings = {
++ /**
++ * Primary features of DataTables and their enablement state.
++ * @namespace
++ */
++ "oFeatures": {
++
++ /**
++ * Flag to say if DataTables should automatically try to calculate the
++ * optimum table and columns widths (true) or not (false).
++ * Note that this parameter will be set by the initialisation routine. To
++ * set a default use {@link DataTable.defaults}.
++ * @type boolean
++ */
++ "bAutoWidth": null,
++
++ /**
++ * Delay the creation of TR and TD elements until they are actually
++ * needed by a driven page draw. This can give a significant speed
++ * increase for Ajax source and Javascript source data, but makes no
++ * difference at all fro DOM and server-side processing tables.
++ * Note that this parameter will be set by the initialisation routine. To
++ * set a default use {@link DataTable.defaults}.
++ * @type boolean
++ */
++ "bDeferRender": null,
++
++ /**
++ * Enable filtering on the table or not. Note that if this is disabled
++ * then there is no filtering at all on the table, including fnFilter.
++ * To just remove the filtering input use sDom and remove the 'f' option.
++ * Note that this parameter will be set by the initialisation routine. To
++ * set a default use {@link DataTable.defaults}.
++ * @type boolean
++ */
++ "bFilter": null,
++
++ /**
++ * Table information element (the 'Showing x of y records' div) enable
++ * flag.
++ * Note that this parameter will be set by the initialisation routine. To
++ * set a default use {@link DataTable.defaults}.
++ * @type boolean
++ */
++ "bInfo": null,
++
++ /**
++ * Present a user control allowing the end user to change the page size
++ * when pagination is enabled.
++ * Note that this parameter will be set by the initialisation routine. To
++ * set a default use {@link DataTable.defaults}.
++ * @type boolean
++ */
++ "bLengthChange": null,
++
++ /**
++ * Pagination enabled or not. Note that if this is disabled then length
++ * changing must also be disabled.
++ * Note that this parameter will be set by the initialisation routine. To
++ * set a default use {@link DataTable.defaults}.
++ * @type boolean
++ */
++ "bPaginate": null,
++
++ /**
++ * Processing indicator enable flag whenever DataTables is enacting a
++ * user request - typically an Ajax request for server-side processing.
++ * Note that this parameter will be set by the initialisation routine. To
++ * set a default use {@link DataTable.defaults}.
++ * @type boolean
++ */
++ "bProcessing": null,
++
++ /**
++ * Server-side processing enabled flag - when enabled DataTables will
++ * get all data from the server for every draw - there is no filtering,
++ * sorting or paging done on the client-side.
++ * Note that this parameter will be set by the initialisation routine. To
++ * set a default use {@link DataTable.defaults}.
++ * @type boolean
++ */
++ "bServerSide": null,
++
++ /**
++ * Sorting enablement flag.
++ * Note that this parameter will be set by the initialisation routine. To
++ * set a default use {@link DataTable.defaults}.
++ * @type boolean
++ */
++ "bSort": null,
++
++ /**
++ * Multi-column sorting
++ * Note that this parameter will be set by the initialisation routine. To
++ * set a default use {@link DataTable.defaults}.
++ * @type boolean
++ */
++ "bSortMulti": null,
++
++ /**
++ * Apply a class to the columns which are being sorted to provide a
++ * visual highlight or not. This can slow things down when enabled since
++ * there is a lot of DOM interaction.
++ * Note that this parameter will be set by the initialisation routine. To
++ * set a default use {@link DataTable.defaults}.
++ * @type boolean
++ */
++ "bSortClasses": null,
++
++ /**
++ * State saving enablement flag.
++ * Note that this parameter will be set by the initialisation routine. To
++ * set a default use {@link DataTable.defaults}.
++ * @type boolean
++ */
++ "bStateSave": null
++ },
++
++
++ /**
++ * Scrolling settings for a table.
++ * @namespace
++ */
++ "oScroll": {
++ /**
++ * When the table is shorter in height than sScrollY, collapse the
++ * table container down to the height of the table (when true).
++ * Note that this parameter will be set by the initialisation routine. To
++ * set a default use {@link DataTable.defaults}.
++ * @type boolean
++ */
++ "bCollapse": null,
++
++ /**
++ * Width of the scrollbar for the web-browser's platform. Calculated
++ * during table initialisation.
++ * @type int
++ * @default 0
++ */
++ "iBarWidth": 0,
++
++ /**
++ * Viewport width for horizontal scrolling. Horizontal scrolling is
++ * disabled if an empty string.
++ * Note that this parameter will be set by the initialisation routine. To
++ * set a default use {@link DataTable.defaults}.
++ * @type string
++ */
++ "sX": null,
++
++ /**
++ * Width to expand the table to when using x-scrolling. Typically you
++ * should not need to use this.
++ * Note that this parameter will be set by the initialisation routine. To
++ * set a default use {@link DataTable.defaults}.
++ * @type string
++ * @deprecated
++ */
++ "sXInner": null,
++
++ /**
++ * Viewport height for vertical scrolling. Vertical scrolling is disabled
++ * if an empty string.
++ * Note that this parameter will be set by the initialisation routine. To
++ * set a default use {@link DataTable.defaults}.
++ * @type string
++ */
++ "sY": null
++ },
++
++ /**
++ * Language information for the table.
++ * @namespace
++ * @extends DataTable.defaults.oLanguage
++ */
++ "oLanguage": {
++ /**
++ * Information callback function. See
++ * {@link DataTable.defaults.fnInfoCallback}
++ * @type function
++ * @default null
++ */
++ "fnInfoCallback": null
++ },
++
++ /**
++ * Browser support parameters
++ * @namespace
++ */
++ "oBrowser": {
++ /**
++ * Indicate if the browser incorrectly calculates width:100% inside a
++ * scrolling element (IE6/7)
++ * @type boolean
++ * @default false
++ */
++ "bScrollOversize": false,
++
++ /**
++ * Determine if the vertical scrollbar is on the right or left of the
++ * scrolling container - needed for rtl language layout, although not
++ * all browsers move the scrollbar (Safari).
++ * @type boolean
++ * @default false
++ */
++ "bScrollbarLeft": false
++ },
++
++
++ "ajax": null,
++
++
++ /**
++ * Array referencing the nodes which are used for the features. The
++ * parameters of this object match what is allowed by sDom - i.e.
++ * <ul>
++ * <li>'l' - Length changing</li>
++ * <li>'f' - Filtering input</li>
++ * <li>'t' - The table!</li>
++ * <li>'i' - Information</li>
++ * <li>'p' - Pagination</li>
++ * <li>'r' - pRocessing</li>
++ * </ul>
++ * @type array
++ * @default []
++ */
++ "aanFeatures": [],
++
++ /**
++ * Store data information - see {@link DataTable.models.oRow} for detailed
++ * information.
++ * @type array
++ * @default []
++ */
++ "aoData": [],
++
++ /**
++ * Array of indexes which are in the current display (after filtering etc)
++ * @type array
++ * @default []
++ */
++ "aiDisplay": [],
++
++ /**
++ * Array of indexes for display - no filtering
++ * @type array
++ * @default []
++ */
++ "aiDisplayMaster": [],
++
++ /**
++ * Store information about each column that is in use
++ * @type array
++ * @default []
++ */
++ "aoColumns": [],
++
++ /**
++ * Store information about the table's header
++ * @type array
++ * @default []
++ */
++ "aoHeader": [],
++
++ /**
++ * Store information about the table's footer
++ * @type array
++ * @default []
++ */
++ "aoFooter": [],
++
++ /**
++ * Store the applied global search information in case we want to force a
++ * research or compare the old search to a new one.
++ * Note that this parameter will be set by the initialisation routine. To
++ * set a default use {@link DataTable.defaults}.
++ * @namespace
++ * @extends DataTable.models.oSearch
++ */
++ "oPreviousSearch": {},
++
++ /**
++ * Store the applied search for each column - see
++ * {@link DataTable.models.oSearch} for the format that is used for the
++ * filtering information for each column.
++ * @type array
++ * @default []
++ */
++ "aoPreSearchCols": [],
++
++ /**
++ * Sorting that is applied to the table. Note that the inner arrays are
++ * used in the following manner:
++ * <ul>
++ * <li>Index 0 - column number</li>
++ * <li>Index 1 - current sorting direction</li>
++ * </ul>
++ * Note that this parameter will be set by the initialisation routine. To
++ * set a default use {@link DataTable.defaults}.
++ * @type array
++ * @todo These inner arrays should really be objects
++ */
++ "aaSorting": null,
++
++ /**
++ * Sorting that is always applied to the table (i.e. prefixed in front of
++ * aaSorting).
++ * Note that this parameter will be set by the initialisation routine. To
++ * set a default use {@link DataTable.defaults}.
++ * @type array
++ * @default []
++ */
++ "aaSortingFixed": [],
++
++ /**
++ * Classes to use for the striping of a table.
++ * Note that this parameter will be set by the initialisation routine. To
++ * set a default use {@link DataTable.defaults}.
++ * @type array
++ * @default []
++ */
++ "asStripeClasses": null,
++
++ /**
++ * If restoring a table - we should restore its striping classes as well
++ * @type array
++ * @default []
++ */
++ "asDestroyStripes": [],
++
++ /**
++ * If restoring a table - we should restore its width
++ * @type int
++ * @default 0
++ */
++ "sDestroyWidth": 0,
++
++ /**
++ * Callback functions array for every time a row is inserted (i.e. on a draw).
++ * @type array
++ * @default []
++ */
++ "aoRowCallback": [],
++
++ /**
++ * Callback functions for the header on each draw.
++ * @type array
++ * @default []
++ */
++ "aoHeaderCallback": [],
++
++ /**
++ * Callback function for the footer on each draw.
++ * @type array
++ * @default []
++ */
++ "aoFooterCallback": [],
++
++ /**
++ * Array of callback functions for draw callback functions
++ * @type array
++ * @default []
++ */
++ "aoDrawCallback": [],
++
++ /**
++ * Array of callback functions for row created function
++ * @type array
++ * @default []
++ */
++ "aoRowCreatedCallback": [],
++
++ /**
++ * Callback functions for just before the table is redrawn. A return of
++ * false will be used to cancel the draw.
++ * @type array
++ * @default []
++ */
++ "aoPreDrawCallback": [],
++
++ /**
++ * Callback functions for when the table has been initialised.
++ * @type array
++ * @default []
++ */
++ "aoInitComplete": [],
++
++
++ /**
++ * Callbacks for modifying the settings to be stored for state saving, prior to
++ * saving state.
++ * @type array
++ * @default []
++ */
++ "aoStateSaveParams": [],
++
++ /**
++ * Callbacks for modifying the settings that have been stored for state saving
++ * prior to using the stored values to restore the state.
++ * @type array
++ * @default []
++ */
++ "aoStateLoadParams": [],
++
++ /**
++ * Callbacks for operating on the settings object once the saved state has been
++ * loaded
++ * @type array
++ * @default []
++ */
++ "aoStateLoaded": [],
++
++ /**
++ * Cache the table ID for quick access
++ * @type string
++ * @default <i>Empty string</i>
++ */
++ "sTableId": "",
++
++ /**
++ * The TABLE node for the main table
++ * @type node
++ * @default null
++ */
++ "nTable": null,
++
++ /**
++ * Permanent ref to the thead element
++ * @type node
++ * @default null
++ */
++ "nTHead": null,
++
++ /**
++ * Permanent ref to the tfoot element - if it exists
++ * @type node
++ * @default null
++ */
++ "nTFoot": null,
++
++ /**
++ * Permanent ref to the tbody element
++ * @type node
++ * @default null
++ */
++ "nTBody": null,
++
++ /**
++ * Cache the wrapper node (contains all DataTables controlled elements)
++ * @type node
++ * @default null
++ */
++ "nTableWrapper": null,
++
++ /**
++ * Indicate if when using server-side processing the loading of data
++ * should be deferred until the second draw.
++ * Note that this parameter will be set by the initialisation routine. To
++ * set a default use {@link DataTable.defaults}.
++ * @type boolean
++ * @default false
++ */
++ "bDeferLoading": false,
++
++ /**
++ * Indicate if all required information has been read in
++ * @type boolean
++ * @default false
++ */
++ "bInitialised": false,
++
++ /**
++ * Information about open rows. Each object in the array has the parameters
++ * 'nTr' and 'nParent'
++ * @type array
++ * @default []
++ */
++ "aoOpenRows": [],
++
++ /**
++ * Dictate the positioning of DataTables' control elements - see
++ * {@link DataTable.model.oInit.sDom}.
++ * Note that this parameter will be set by the initialisation routine. To
++ * set a default use {@link DataTable.defaults}.
++ * @type string
++ * @default null
++ */
++ "sDom": null,
++
++ /**
++ * Search delay (in mS)
++ * @type integer
++ * @default null
++ */
++ "searchDelay": null,
++
++ /**
++ * Which type of pagination should be used.
++ * Note that this parameter will be set by the initialisation routine. To
++ * set a default use {@link DataTable.defaults}.
++ * @type string
++ * @default two_button
++ */
++ "sPaginationType": "two_button",
++
++ /**
++ * The state duration (for `stateSave`) in seconds.
++ * Note that this parameter will be set by the initialisation routine. To
++ * set a default use {@link DataTable.defaults}.
++ * @type int
++ * @default 0
++ */
++ "iStateDuration": 0,
++
++ /**
++ * Array of callback functions for state saving. Each array element is an
++ * object with the following parameters:
++ * <ul>
++ * <li>function:fn - function to call. Takes two parameters, oSettings
++ * and the JSON string to save that has been thus far created. Returns
++ * a JSON string to be inserted into a json object
++ * (i.e. '"param": [ 0, 1, 2]')</li>
++ * <li>string:sName - name of callback</li>
++ * </ul>
++ * @type array
++ * @default []
++ */
++ "aoStateSave": [],
++
++ /**
++ * Array of callback functions for state loading. Each array element is an
++ * object with the following parameters:
++ * <ul>
++ * <li>function:fn - function to call. Takes two parameters, oSettings
++ * and the object stored. May return false to cancel state loading</li>
++ * <li>string:sName - name of callback</li>
++ * </ul>
++ * @type array
++ * @default []
++ */
++ "aoStateLoad": [],
++
++ /**
++ * State that was saved. Useful for back reference
++ * @type object
++ * @default null
++ */
++ "oSavedState": null,
++
++ /**
++ * State that was loaded. Useful for back reference
++ * @type object
++ * @default null
++ */
++ "oLoadedState": null,
++
++ /**
++ * Source url for AJAX data for the table.
++ * Note that this parameter will be set by the initialisation routine. To
++ * set a default use {@link DataTable.defaults}.
++ * @type string
++ * @default null
++ */
++ "sAjaxSource": null,
++
++ /**
++ * Property from a given object from which to read the table data from. This
++ * can be an empty string (when not server-side processing), in which case
++ * it is assumed an an array is given directly.
++ * Note that this parameter will be set by the initialisation routine. To
++ * set a default use {@link DataTable.defaults}.
++ * @type string
++ */
++ "sAjaxDataProp": null,
++
++ /**
++ * Note if draw should be blocked while getting data
++ * @type boolean
++ * @default true
++ */
++ "bAjaxDataGet": true,
++
++ /**
++ * The last jQuery XHR object that was used for server-side data gathering.
++ * This can be used for working with the XHR information in one of the
++ * callbacks
++ * @type object
++ * @default null
++ */
++ "jqXHR": null,
++
++ /**
++ * JSON returned from the server in the last Ajax request
++ * @type object
++ * @default undefined
++ */
++ "json": undefined,
++
++ /**
++ * Data submitted as part of the last Ajax request
++ * @type object
++ * @default undefined
++ */
++ "oAjaxData": undefined,
++
++ /**
++ * Function to get the server-side data.
++ * Note that this parameter will be set by the initialisation routine. To
++ * set a default use {@link DataTable.defaults}.
++ * @type function
++ */
++ "fnServerData": null,
++
++ /**
++ * Functions which are called prior to sending an Ajax request so extra
++ * parameters can easily be sent to the server
++ * @type array
++ * @default []
++ */
++ "aoServerParams": [],
++
++ /**
++ * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if
++ * required).
++ * Note that this parameter will be set by the initialisation routine. To
++ * set a default use {@link DataTable.defaults}.
++ * @type string
++ */
++ "sServerMethod": null,
++
++ /**
++ * Format numbers for display.
++ * Note that this parameter will be set by the initialisation routine. To
++ * set a default use {@link DataTable.defaults}.
++ * @type function
++ */
++ "fnFormatNumber": null,
++
++ /**
++ * List of options that can be used for the user selectable length menu.
++ * Note that this parameter will be set by the initialisation routine. To
++ * set a default use {@link DataTable.defaults}.
++ * @type array
++ * @default []
++ */
++ "aLengthMenu": null,
++
++ /**
++ * Counter for the draws that the table does. Also used as a tracker for
++ * server-side processing
++ * @type int
++ * @default 0
++ */
++ "iDraw": 0,
++
++ /**
++ * Indicate if a redraw is being done - useful for Ajax
++ * @type boolean
++ * @default false
++ */
++ "bDrawing": false,
++
++ /**
++ * Draw index (iDraw) of the last error when parsing the returned data
++ * @type int
++ * @default -1
++ */
++ "iDrawError": -1,
++
++ /**
++ * Paging display length
++ * @type int
++ * @default 10
++ */
++ "_iDisplayLength": 10,
++
++ /**
++ * Paging start point - aiDisplay index
++ * @type int
++ * @default 0
++ */
++ "_iDisplayStart": 0,
++
++ /**
++ * Server-side processing - number of records in the result set
++ * (i.e. before filtering), Use fnRecordsTotal rather than
++ * this property to get the value of the number of records, regardless of
++ * the server-side processing setting.
++ * @type int
++ * @default 0
++ * @private
++ */
++ "_iRecordsTotal": 0,
++
++ /**
++ * Server-side processing - number of records in the current display set
++ * (i.e. after filtering). Use fnRecordsDisplay rather than
++ * this property to get the value of the number of records, regardless of
++ * the server-side processing setting.
++ * @type boolean
++ * @default 0
++ * @private
++ */
++ "_iRecordsDisplay": 0,
++
++ /**
++ * Flag to indicate if jQuery UI marking and classes should be used.
++ * Note that this parameter will be set by the initialisation routine. To
++ * set a default use {@link DataTable.defaults}.
++ * @type boolean
++ */
++ "bJUI": null,
++
++ /**
++ * The classes to use for the table
++ * @type object
++ * @default {}
++ */
++ "oClasses": {},
++
++ /**
++ * Flag attached to the settings object so you can check in the draw
++ * callback if filtering has been done in the draw. Deprecated in favour of
++ * events.
++ * @type boolean
++ * @default false
++ * @deprecated
++ */
++ "bFiltered": false,
++
++ /**
++ * Flag attached to the settings object so you can check in the draw
++ * callback if sorting has been done in the draw. Deprecated in favour of
++ * events.
++ * @type boolean
++ * @default false
++ * @deprecated
++ */
++ "bSorted": false,
++
++ /**
++ * Indicate that if multiple rows are in the header and there is more than
++ * one unique cell per column, if the top one (true) or bottom one (false)
++ * should be used for sorting / title by DataTables.
++ * Note that this parameter will be set by the initialisation routine. To
++ * set a default use {@link DataTable.defaults}.
++ * @type boolean
++ */
++ "bSortCellsTop": null,
++
++ /**
++ * Initialisation object that is used for the table
++ * @type object
++ * @default null
++ */
++ "oInit": null,
++
++ /**
++ * Destroy callback functions - for plug-ins to attach themselves to the
++ * destroy so they can clean up markup and events.
++ * @type array
++ * @default []
++ */
++ "aoDestroyCallback": [],
++
++
++ /**
++ * Get the number of records in the current record set, before filtering
++ * @type function
++ */
++ "fnRecordsTotal": function ()
++ {
++ return _fnDataSource( this ) == 'ssp' ?
++ this._iRecordsTotal * 1 :
++ this.aiDisplayMaster.length;
++ },
++
++ /**
++ * Get the number of records in the current record set, after filtering
++ * @type function
++ */
++ "fnRecordsDisplay": function ()
++ {
++ return _fnDataSource( this ) == 'ssp' ?
++ this._iRecordsDisplay * 1 :
++ this.aiDisplay.length;
++ },
++
++ /**
++ * Get the display end point - aiDisplay index
++ * @type function
++ */
++ "fnDisplayEnd": function ()
++ {
++ var
++ len = this._iDisplayLength,
++ start = this._iDisplayStart,
++ calc = start + len,
++ records = this.aiDisplay.length,
++ features = this.oFeatures,
++ paginate = features.bPaginate;
++
++ if ( features.bServerSide ) {
++ return paginate === false || len === -1 ?
++ start + records :
++ Math.min( start+len, this._iRecordsDisplay );
++ }
++ else {
++ return ! paginate || calc>records || len===-1 ?
++ records :
++ calc;
++ }
++ },
++
++ /**
++ * The DataTables object for this table
++ * @type object
++ * @default null
++ */
++ "oInstance": null,
++
++ /**
++ * Unique identifier for each instance of the DataTables object. If there
++ * is an ID on the table node, then it takes that value, otherwise an
++ * incrementing internal counter is used.
++ * @type string
++ * @default null
++ */
++ "sInstance": null,
++
++ /**
++ * tabindex attribute value that is added to DataTables control elements, allowing
++ * keyboard navigation of the table and its controls.
++ */
++ "iTabIndex": 0,
++
++ /**
++ * DIV container for the footer scrolling table if scrolling
++ */
++ "nScrollHead": null,
++
++ /**
++ * DIV container for the footer scrolling table if scrolling
++ */
++ "nScrollFoot": null,
++
++ /**
++ * Last applied sort
++ * @type array
++ * @default []
++ */
++ "aLastSort": [],
++
++ /**
++ * Stored plug-in instances
++ * @type object
++ * @default {}
++ */
++ "oPlugins": {}
++ };
++
++ /**
++ * Extension object for DataTables that is used to provide all extension
++ * options.
++ *
++ * Note that the `DataTable.ext` object is available through
++ * `jQuery.fn.dataTable.ext` where it may be accessed and manipulated. It is
++ * also aliased to `jQuery.fn.dataTableExt` for historic reasons.
++ * @namespace
++ * @extends DataTable.models.ext
++ */
++
++
++ /**
++ * DataTables extensions
++ *
++ * This namespace acts as a collection area for plug-ins that can be used to
++ * extend DataTables capabilities. Indeed many of the build in methods
++ * use this method to provide their own capabilities (sorting methods for
++ * example).
++ *
++ * Note that this namespace is aliased to `jQuery.fn.dataTableExt` for legacy
++ * reasons
++ *
++ * @namespace
++ */
++ DataTable.ext = _ext = {
++ /**
++ * Buttons. For use with the Buttons extension for DataTables. This is
++ * defined here so other extensions can define buttons regardless of load
++ * order. It is _not_ used by DataTables core.
++ *
++ * @type object
++ * @default {}
++ */
++ buttons: {},
++
++
++ /**
++ * Element class names
++ *
++ * @type object
++ * @default {}
++ */
++ classes: {},
++
++
++ /**
++ * Error reporting.
++ *
++ * How should DataTables report an error. Can take the value 'alert',
++ * 'throw', 'none' or a function.
++ *
++ * @type string|function
++ * @default alert
++ */
++ errMode: "alert",
++
++
++ /**
++ * Feature plug-ins.
++ *
++ * This is an array of objects which describe the feature plug-ins that are
++ * available to DataTables. These feature plug-ins are then available for
++ * use through the `dom` initialisation option.
++ *
++ * Each feature plug-in is described by an object which must have the
++ * following properties:
++ *
++ * * `fnInit` - function that is used to initialise the plug-in,
++ * * `cFeature` - a character so the feature can be enabled by the `dom`
++ * instillation option. This is case sensitive.
++ *
++ * The `fnInit` function has the following input parameters:
++ *
++ * 1. `{object}` DataTables settings object: see
++ * {@link DataTable.models.oSettings}
++ *
++ * And the following return is expected:
++ *
++ * * {node|null} The element which contains your feature. Note that the
++ * return may also be void if your plug-in does not require to inject any
++ * DOM elements into DataTables control (`dom`) - for example this might
++ * be useful when developing a plug-in which allows table control via
++ * keyboard entry
++ *
++ * @type array
++ *
++ * @example
++ * $.fn.dataTable.ext.features.push( {
++ * "fnInit": function( oSettings ) {
++ * return new TableTools( { "oDTSettings": oSettings } );
++ * },
++ * "cFeature": "T"
++ * } );
++ */
++ feature: [],
++
++
++ /**
++ * Row searching.
++ *
++ * This method of searching is complimentary to the default type based
++ * searching, and a lot more comprehensive as it allows you complete control
++ * over the searching logic. Each element in this array is a function
++ * (parameters described below) that is called for every row in the table,
++ * and your logic decides if it should be included in the searching data set
++ * or not.
++ *
++ * Searching functions have the following input parameters:
++ *
++ * 1. `{object}` DataTables settings object: see
++ * {@link DataTable.models.oSettings}
++ * 2. `{array|object}` Data for the row to be processed (same as the
++ * original format that was passed in as the data source, or an array
++ * from a DOM data source
++ * 3. `{int}` Row index ({@link DataTable.models.oSettings.aoData}), which
++ * can be useful to retrieve the `TR` element if you need DOM interaction.
++ *
++ * And the following return is expected:
++ *
++ * * {boolean} Include the row in the searched result set (true) or not
++ * (false)
++ *
++ * Note that as with the main search ability in DataTables, technically this
++ * is "filtering", since it is subtractive. However, for consistency in
++ * naming we call it searching here.
++ *
++ * @type array
++ * @default []
++ *
++ * @example
++ * // The following example shows custom search being applied to the
++ * // fourth column (i.e. the data[3] index) based on two input values
++ * // from the end-user, matching the data in a certain range.
++ * $.fn.dataTable.ext.search.push(
++ * function( settings, data, dataIndex ) {
++ * var min = document.getElementById('min').value * 1;
++ * var max = document.getElementById('max').value * 1;
++ * var version = data[3] == "-" ? 0 : data[3]*1;
++ *
++ * if ( min == "" && max == "" ) {
++ * return true;
++ * }
++ * else if ( min == "" && version < max ) {
++ * return true;
++ * }
++ * else if ( min < version && "" == max ) {
++ * return true;
++ * }
++ * else if ( min < version && version < max ) {
++ * return true;
++ * }
++ * return false;
++ * }
++ * );
++ */
++ search: [],
++
++
++ /**
++ * Selector extensions
++ *
++ * The `selector` option can be used to extend the options available for the
++ * selector modifier options (`selector-modifier` object data type) that
++ * each of the three built in selector types offer (row, column and cell +
++ * their plural counterparts). For example the Select extension uses this
++ * mechanism to provide an option to select only rows, columns and cells
++ * that have been marked as selected by the end user (`{selected: true}`),
++ * which can be used in conjunction with the existing built in selector
++ * options.
++ *
++ * Each property is an array to which functions can be pushed. The functions
++ * take three attributes:
++ *
++ * * Settings object for the host table
++ * * Options object (`selector-modifier` object type)
++ * * Array of selected item indexes
++ *
++ * The return is an array of the resulting item indexes after the custom
++ * selector has been applied.
++ *
++ * @type object
++ */
++ selector: {
++ cell: [],
++ column: [],
++ row: []
++ },
++
++
++ /**
++ * Internal functions, exposed for used in plug-ins.
++ *
++ * Please note that you should not need to use the internal methods for
++ * anything other than a plug-in (and even then, try to avoid if possible).
++ * The internal function may change between releases.
++ *
++ * @type object
++ * @default {}
++ */
++ internal: {},
++
++
++ /**
++ * Legacy configuration options. Enable and disable legacy options that
++ * are available in DataTables.
++ *
++ * @type object
++ */
++ legacy: {
++ /**
++ * Enable / disable DataTables 1.9 compatible server-side processing
++ * requests
++ *
++ * @type boolean
++ * @default null
++ */
++ ajax: null
++ },
++
++
++ /**
++ * Pagination plug-in methods.
++ *
++ * Each entry in this object is a function and defines which buttons should
++ * be shown by the pagination rendering method that is used for the table:
++ * {@link DataTable.ext.renderer.pageButton}. The renderer addresses how the
++ * buttons are displayed in the document, while the functions here tell it
++ * what buttons to display. This is done by returning an array of button
++ * descriptions (what each button will do).
++ *
++ * Pagination types (the four built in options and any additional plug-in
++ * options defined here) can be used through the `paginationType`
++ * initialisation parameter.
++ *
++ * The functions defined take two parameters:
++ *
++ * 1. `{int} page` The current page index
++ * 2. `{int} pages` The number of pages in the table
++ *
++ * Each function is expected to return an array where each element of the
++ * array can be one of:
++ *
++ * * `first` - Jump to first page when activated
++ * * `last` - Jump to last page when activated
++ * * `previous` - Show previous page when activated
++ * * `next` - Show next page when activated
++ * * `{int}` - Show page of the index given
++ * * `{array}` - A nested array containing the above elements to add a
++ * containing 'DIV' element (might be useful for styling).
++ *
++ * Note that DataTables v1.9- used this object slightly differently whereby
++ * an object with two functions would be defined for each plug-in. That
++ * ability is still supported by DataTables 1.10+ to provide backwards
++ * compatibility, but this option of use is now decremented and no longer
++ * documented in DataTables 1.10+.
++ *
++ * @type object
++ * @default {}
++ *
++ * @example
++ * // Show previous, next and current page buttons only
++ * $.fn.dataTableExt.oPagination.current = function ( page, pages ) {
++ * return [ 'previous', page, 'next' ];
++ * };
++ */
++ pager: {},
++
++
++ renderer: {
++ pageButton: {},
++ header: {}
++ },
++
++
++ /**
++ * Ordering plug-ins - custom data source
++ *
++ * The extension options for ordering of data available here is complimentary
++ * to the default type based ordering that DataTables typically uses. It
++ * allows much greater control over the the data that is being used to
++ * order a column, but is necessarily therefore more complex.
++ *
++ * This type of ordering is useful if you want to do ordering based on data
++ * live from the DOM (for example the contents of an 'input' element) rather
++ * than just the static string that DataTables knows of.
++ *
++ * The way these plug-ins work is that you create an array of the values you
++ * wish to be ordering for the column in question and then return that
++ * array. The data in the array much be in the index order of the rows in
++ * the table (not the currently ordering order!). Which order data gathering
++ * function is run here depends on the `dt-init columns.orderDataType`
++ * parameter that is used for the column (if any).
++ *
++ * The functions defined take two parameters:
++ *
++ * 1. `{object}` DataTables settings object: see
++ * {@link DataTable.models.oSettings}
++ * 2. `{int}` Target column index
++ *
++ * Each function is expected to return an array:
++ *
++ * * `{array}` Data for the column to be ordering upon
++ *
++ * @type array
++ *
++ * @example
++ * // Ordering using `input` node values
++ * $.fn.dataTable.ext.order['dom-text'] = function ( settings, col )
++ * {
++ * return this.api().column( col, {order:'index'} ).nodes().map( function ( td, i ) {
++ * return $('input', td).val();
++ * } );
++ * }
++ */
++ order: {},
++
++
++ /**
++ * Type based plug-ins.
++ *
++ * Each column in DataTables has a type assigned to it, either by automatic
++ * detection or by direct assignment using the `type` option for the column.
++ * The type of a column will effect how it is ordering and search (plug-ins
++ * can also make use of the column type if required).
++ *
++ * @namespace
++ */
++ type: {
++ /**
++ * Type detection functions.
++ *
++ * The functions defined in this object are used to automatically detect
++ * a column's type, making initialisation of DataTables super easy, even
++ * when complex data is in the table.
++ *
++ * The functions defined take two parameters:
++ *
++ * 1. `{*}` Data from the column cell to be analysed
++ * 2. `{settings}` DataTables settings object. This can be used to
++ * perform context specific type detection - for example detection
++ * based on language settings such as using a comma for a decimal
++ * place. Generally speaking the options from the settings will not
++ * be required
++ *
++ * Each function is expected to return:
++ *
++ * * `{string|null}` Data type detected, or null if unknown (and thus
++ * pass it on to the other type detection functions.
++ *
++ * @type array
++ *
++ * @example
++ * // Currency type detection plug-in:
++ * $.fn.dataTable.ext.type.detect.push(
++ * function ( data, settings ) {
++ * // Check the numeric part
++ * if ( ! $.isNumeric( data.substring(1) ) ) {
++ * return null;
++ * }
++ *
++ * // Check prefixed by currency
++ * if ( data.charAt(0) == '$' || data.charAt(0) == '£' ) {
++ * return 'currency';
++ * }
++ * return null;
++ * }
++ * );
++ */
++ detect: [],
++
++
++ /**
++ * Type based search formatting.
++ *
++ * The type based searching functions can be used to pre-format the
++ * data to be search on. For example, it can be used to strip HTML
++ * tags or to de-format telephone numbers for numeric only searching.
++ *
++ * Note that is a search is not defined for a column of a given type,
++ * no search formatting will be performed.
++ *
++ * Pre-processing of searching data plug-ins - When you assign the sType
++ * for a column (or have it automatically detected for you by DataTables
++ * or a type detection plug-in), you will typically be using this for
++ * custom sorting, but it can also be used to provide custom searching
++ * by allowing you to pre-processing the data and returning the data in
++ * the format that should be searched upon. This is done by adding
++ * functions this object with a parameter name which matches the sType
++ * for that target column. This is the corollary of <i>afnSortData</i>
++ * for searching data.
++ *
++ * The functions defined take a single parameter:
++ *
++ * 1. `{*}` Data from the column cell to be prepared for searching
++ *
++ * Each function is expected to return:
++ *
++ * * `{string|null}` Formatted string that will be used for the searching.
++ *
++ * @type object
++ * @default {}
++ *
++ * @example
++ * $.fn.dataTable.ext.type.search['title-numeric'] = function ( d ) {
++ * return d.replace(/\n/g," ").replace( /<.*?>/g, "" );
++ * }
++ */
++ search: {},
++
++
++ /**
++ * Type based ordering.
++ *
++ * The column type tells DataTables what ordering to apply to the table
++ * when a column is sorted upon. The order for each type that is defined,
++ * is defined by the functions available in this object.
++ *
++ * Each ordering option can be described by three properties added to
++ * this object:
++ *
++ * * `{type}-pre` - Pre-formatting function
++ * * `{type}-asc` - Ascending order function
++ * * `{type}-desc` - Descending order function
++ *
++ * All three can be used together, only `{type}-pre` or only
++ * `{type}-asc` and `{type}-desc` together. It is generally recommended
++ * that only `{type}-pre` is used, as this provides the optimal
++ * implementation in terms of speed, although the others are provided
++ * for compatibility with existing Javascript sort functions.
++ *
++ * `{type}-pre`: Functions defined take a single parameter:
++ *
++ * 1. `{*}` Data from the column cell to be prepared for ordering
++ *
++ * And return:
++ *
++ * * `{*}` Data to be sorted upon
++ *
++ * `{type}-asc` and `{type}-desc`: Functions are typical Javascript sort
++ * functions, taking two parameters:
++ *
++ * 1. `{*}` Data to compare to the second parameter
++ * 2. `{*}` Data to compare to the first parameter
++ *
++ * And returning:
++ *
++ * * `{*}` Ordering match: <0 if first parameter should be sorted lower
++ * than the second parameter, ===0 if the two parameters are equal and
++ * >0 if the first parameter should be sorted height than the second
++ * parameter.
++ *
++ * @type object
++ * @default {}
++ *
++ * @example
++ * // Numeric ordering of formatted numbers with a pre-formatter
++ * $.extend( $.fn.dataTable.ext.type.order, {
++ * "string-pre": function(x) {
++ * a = (a === "-" || a === "") ? 0 : a.replace( /[^\d\-\.]/g, "" );
++ * return parseFloat( a );
++ * }
++ * } );
++ *
++ * @example
++ * // Case-sensitive string ordering, with no pre-formatting method
++ * $.extend( $.fn.dataTable.ext.order, {
++ * "string-case-asc": function(x,y) {
++ * return ((x < y) ? -1 : ((x > y) ? 1 : 0));
++ * },
++ * "string-case-desc": function(x,y) {
++ * return ((x < y) ? 1 : ((x > y) ? -1 : 0));
++ * }
++ * } );
++ */
++ order: {}
++ },
++
++ /**
++ * Unique DataTables instance counter
++ *
++ * @type int
++ * @private
++ */
++ _unique: 0,
++
++
++ //
++ // Depreciated
++ // The following properties are retained for backwards compatiblity only.
++ // The should not be used in new projects and will be removed in a future
++ // version
++ //
++
++ /**
++ * Version check function.
++ * @type function
++ * @depreciated Since 1.10
++ */
++ fnVersionCheck: DataTable.fnVersionCheck,
++
++
++ /**
++ * Index for what 'this' index API functions should use
++ * @type int
++ * @deprecated Since v1.10
++ */
++ iApiIndex: 0,
++
++
++ /**
++ * jQuery UI class container
++ * @type object
++ * @deprecated Since v1.10
++ */
++ oJUIClasses: {},
++
++
++ /**
++ * Software version
++ * @type string
++ * @deprecated Since v1.10
++ */
++ sVersion: DataTable.version
++ };
++
++
++ //
++ // Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts
++ //
++ $.extend( _ext, {
++ afnFiltering: _ext.search,
++ aTypes: _ext.type.detect,
++ ofnSearch: _ext.type.search,
++ oSort: _ext.type.order,
++ afnSortData: _ext.order,
++ aoFeatures: _ext.feature,
++ oApi: _ext.internal,
++ oStdClasses: _ext.classes,
++ oPagination: _ext.pager
++ } );
++
++
++ $.extend( DataTable.ext.classes, {
++ "sTable": "dataTable",
++ "sNoFooter": "no-footer",
++
++ /* Paging buttons */
++ "sPageButton": "paginate_button",
++ "sPageButtonActive": "current",
++ "sPageButtonDisabled": "disabled",
++
++ /* Striping classes */
++ "sStripeOdd": "odd",
++ "sStripeEven": "even",
++
++ /* Empty row */
++ "sRowEmpty": "dataTables_empty",
++
++ /* Features */
++ "sWrapper": "dataTables_wrapper",
++ "sFilter": "dataTables_filter",
++ "sInfo": "dataTables_info",
++ "sPaging": "dataTables_paginate paging_", /* Note that the type is postfixed */
++ "sLength": "dataTables_length",
++ "sProcessing": "dataTables_processing",
++
++ /* Sorting */
++ "sSortAsc": "sorting_asc",
++ "sSortDesc": "sorting_desc",
++ "sSortable": "sorting", /* Sortable in both directions */
++ "sSortableAsc": "sorting_asc_disabled",
++ "sSortableDesc": "sorting_desc_disabled",
++ "sSortableNone": "sorting_disabled",
++ "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */
++
++ /* Filtering */
++ "sFilterInput": "",
++
++ /* Page length */
++ "sLengthSelect": "",
++
++ /* Scrolling */
++ "sScrollWrapper": "dataTables_scroll",
++ "sScrollHead": "dataTables_scrollHead",
++ "sScrollHeadInner": "dataTables_scrollHeadInner",
++ "sScrollBody": "dataTables_scrollBody",
++ "sScrollFoot": "dataTables_scrollFoot",
++ "sScrollFootInner": "dataTables_scrollFootInner",
++
++ /* Misc */
++ "sHeaderTH": "",
++ "sFooterTH": "",
++
++ // Deprecated
++ "sSortJUIAsc": "",
++ "sSortJUIDesc": "",
++ "sSortJUI": "",
++ "sSortJUIAscAllowed": "",
++ "sSortJUIDescAllowed": "",
++ "sSortJUIWrapper": "",
++ "sSortIcon": "",
++ "sJUIHeader": "",
++ "sJUIFooter": ""
++ } );
++
++
++ (function() {
++
++ // Reused strings for better compression. Closure compiler appears to have a
++ // weird edge case where it is trying to expand strings rather than use the
++ // variable version. This results in about 200 bytes being added, for very
++ // little preference benefit since it this run on script load only.
++ var _empty = '';
++ _empty = '';
++
++ var _stateDefault = _empty + 'ui-state-default';
++ var _sortIcon = _empty + 'css_right ui-icon ui-icon-';
++ var _headerFooter = _empty + 'fg-toolbar ui-toolbar ui-widget-header ui-helper-clearfix';
++
++ $.extend( DataTable.ext.oJUIClasses, DataTable.ext.classes, {
++ /* Full numbers paging buttons */
++ "sPageButton": "fg-button ui-button "+_stateDefault,
++ "sPageButtonActive": "ui-state-disabled",
++ "sPageButtonDisabled": "ui-state-disabled",
++
++ /* Features */
++ "sPaging": "dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi "+
++ "ui-buttonset-multi paging_", /* Note that the type is postfixed */
++
++ /* Sorting */
++ "sSortAsc": _stateDefault+" sorting_asc",
++ "sSortDesc": _stateDefault+" sorting_desc",
++ "sSortable": _stateDefault+" sorting",
++ "sSortableAsc": _stateDefault+" sorting_asc_disabled",
++ "sSortableDesc": _stateDefault+" sorting_desc_disabled",
++ "sSortableNone": _stateDefault+" sorting_disabled",
++ "sSortJUIAsc": _sortIcon+"triangle-1-n",
++ "sSortJUIDesc": _sortIcon+"triangle-1-s",
++ "sSortJUI": _sortIcon+"carat-2-n-s",
++ "sSortJUIAscAllowed": _sortIcon+"carat-1-n",
++ "sSortJUIDescAllowed": _sortIcon+"carat-1-s",
++ "sSortJUIWrapper": "DataTables_sort_wrapper",
++ "sSortIcon": "DataTables_sort_icon",
++
++ /* Scrolling */
++ "sScrollHead": "dataTables_scrollHead "+_stateDefault,
++ "sScrollFoot": "dataTables_scrollFoot "+_stateDefault,
++
++ /* Misc */
++ "sHeaderTH": _stateDefault,
++ "sFooterTH": _stateDefault,
++ "sJUIHeader": _headerFooter+" ui-corner-tl ui-corner-tr",
++ "sJUIFooter": _headerFooter+" ui-corner-bl ui-corner-br"
++ } );
++
++ }());
++
++
++
++ var extPagination = DataTable.ext.pager;
++
++ function _numbers ( page, pages ) {
++ var
++ numbers = [],
++ buttons = extPagination.numbers_length,
++ half = Math.floor( buttons / 2 ),
++ i = 1;
++
++ if ( pages <= buttons ) {
++ numbers = _range( 0, pages );
++ }
++ else if ( page <= half ) {
++ numbers = _range( 0, buttons-2 );
++ numbers.push( 'ellipsis' );
++ numbers.push( pages-1 );
++ }
++ else if ( page >= pages - 1 - half ) {
++ numbers = _range( pages-(buttons-2), pages );
++ numbers.splice( 0, 0, 'ellipsis' ); // no unshift in ie6
++ numbers.splice( 0, 0, 0 );
++ }
++ else {
++ numbers = _range( page-half+2, page+half-1 );
++ numbers.push( 'ellipsis' );
++ numbers.push( pages-1 );
++ numbers.splice( 0, 0, 'ellipsis' );
++ numbers.splice( 0, 0, 0 );
++ }
++
++ numbers.DT_el = 'span';
++ return numbers;
++ }
++
++
++ $.extend( extPagination, {
++ simple: function ( page, pages ) {
++ return [ 'previous', 'next' ];
++ },
++
++ full: function ( page, pages ) {
++ return [ 'first', 'previous', 'next', 'last' ];
++ },
++
++ simple_numbers: function ( page, pages ) {
++ return [ 'previous', _numbers(page, pages), 'next' ];
++ },
++
++ full_numbers: function ( page, pages ) {
++ return [ 'first', 'previous', _numbers(page, pages), 'next', 'last' ];
++ },
++
++ // For testing and plug-ins to use
++ _numbers: _numbers,
++
++ // Number of number buttons (including ellipsis) to show. _Must be odd!_
++ numbers_length: 7
++ } );
++
++
++ $.extend( true, DataTable.ext.renderer, {
++ pageButton: {
++ _: function ( settings, host, idx, buttons, page, pages ) {
++ var classes = settings.oClasses;
++ var lang = settings.oLanguage.oPaginate;
++ var btnDisplay, btnClass, counter=0;
++
++ var attach = function( container, buttons ) {
++ var i, ien, node, button;
++ var clickHandler = function ( e ) {
++ _fnPageChange( settings, e.data.action, true );
++ };
++
++ for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
++ button = buttons[i];
++
++ if ( $.isArray( button ) ) {
++ var inner = $( '<'+(button.DT_el || 'div')+'/>' )
++ .appendTo( container );
++ attach( inner, button );
++ }
++ else {
++ btnDisplay = '';
++ btnClass = '';
++
++ switch ( button ) {
++ case 'ellipsis':
++ container.append('<span class="ellipsis">…</span>');
++ break;
++
++ case 'first':
++ btnDisplay = lang.sFirst;
++ btnClass = button + (page > 0 ?
++ '' : ' '+classes.sPageButtonDisabled);
++ break;
++
++ case 'previous':
++ btnDisplay = lang.sPrevious;
++ btnClass = button + (page > 0 ?
++ '' : ' '+classes.sPageButtonDisabled);
++ break;
++
++ case 'next':
++ btnDisplay = lang.sNext;
++ btnClass = button + (page < pages-1 ?
++ '' : ' '+classes.sPageButtonDisabled);
++ break;
++
++ case 'last':
++ btnDisplay = lang.sLast;
++ btnClass = button + (page < pages-1 ?
++ '' : ' '+classes.sPageButtonDisabled);
++ break;
++
++ default:
++ btnDisplay = button + 1;
++ btnClass = page === button ?
++ classes.sPageButtonActive : '';
++ break;
++ }
++
++ if ( btnDisplay ) {
++ node = $('<a>', {
++ 'class': classes.sPageButton+' '+btnClass,
++ 'aria-controls': settings.sTableId,
++ 'data-dt-idx': counter,
++ 'tabindex': settings.iTabIndex,
++ 'id': idx === 0 && typeof button === 'string' ?
++ settings.sTableId +'_'+ button :
++ null
++ } )
++ .html( btnDisplay )
++ .appendTo( container );
++
++ _fnBindAction(
++ node, {action: button}, clickHandler
++ );
++
++ counter++;
++ }
++ }
++ }
++ };
++
++ // IE9 throws an 'unknown error' if document.activeElement is used
++ // inside an iframe or frame. Try / catch the error. Not good for
++ // accessibility, but neither are frames.
++ var activeEl;
++
++ try {
++ // Because this approach is destroying and recreating the paging
++ // elements, focus is lost on the select button which is bad for
++ // accessibility. So we want to restore focus once the draw has
++ // completed
++ activeEl = $(document.activeElement).data('dt-idx');
++ }
++ catch (e) {}
++
++ attach( $(host).empty(), buttons );
++
++ if ( activeEl ) {
++ $(host).find( '[data-dt-idx='+activeEl+']' ).focus();
++ }
++ }
++ }
++ } );
++
++
++
++ // Built in type detection. See model.ext.aTypes for information about
++ // what is required from this methods.
++ $.extend( DataTable.ext.type.detect, [
++ // Plain numbers - first since V8 detects some plain numbers as dates
++ // e.g. Date.parse('55') (but not all, e.g. Date.parse('22')...).
++ function ( d, settings )
++ {
++ var decimal = settings.oLanguage.sDecimal;
++ return _isNumber( d, decimal ) ? 'num'+decimal : null;
++ },
++
++ // Dates (only those recognised by the browser's Date.parse)
++ function ( d, settings )
++ {
++ // V8 will remove any unknown characters at the start and end of the
++ // expression, leading to false matches such as `$245.12` or `10%` being
++ // a valid date. See forum thread 18941 for detail.
++ if ( d && !(d instanceof Date) && ( ! _re_date_start.test(d) || ! _re_date_end.test(d) ) ) {
++ return null;
++ }
++ var parsed = Date.parse(d);
++ return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null;
++ },
++
++ // Formatted numbers
++ function ( d, settings )
++ {
++ var decimal = settings.oLanguage.sDecimal;
++ return _isNumber( d, decimal, true ) ? 'num-fmt'+decimal : null;
++ },
++
++ // HTML numeric
++ function ( d, settings )
++ {
++ var decimal = settings.oLanguage.sDecimal;
++ return _htmlNumeric( d, decimal ) ? 'html-num'+decimal : null;
++ },
++
++ // HTML numeric, formatted
++ function ( d, settings )
++ {
++ var decimal = settings.oLanguage.sDecimal;
++ return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt'+decimal : null;
++ },
++
++ // HTML (this is strict checking - there must be html)
++ function ( d, settings )
++ {
++ return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ?
++ 'html' : null;
++ }
++ ] );
++
++
++
++ // Filter formatting functions. See model.ext.ofnSearch for information about
++ // what is required from these methods.
++ //
++ // Note that additional search methods are added for the html numbers and
++ // html formatted numbers by `_addNumericSort()` when we know what the decimal
++ // place is
++
++
++ $.extend( DataTable.ext.type.search, {
++ html: function ( data ) {
++ return _empty(data) ?
++ data :
++ typeof data === 'string' ?
++ data
++ .replace( _re_new_lines, " " )
++ .replace( _re_html, "" ) :
++ '';
++ },
++
++ string: function ( data ) {
++ return _empty(data) ?
++ data :
++ typeof data === 'string' ?
++ data.replace( _re_new_lines, " " ) :
++ data;
++ }
++ } );
++
++
++
++ var __numericReplace = function ( d, decimalPlace, re1, re2 ) {
++ if ( d !== 0 && (!d || d === '-') ) {
++ return -Infinity;
++ }
++
++ // If a decimal place other than `.` is used, it needs to be given to the
++ // function so we can detect it and replace with a `.` which is the only
++ // decimal place Javascript recognises - it is not locale aware.
++ if ( decimalPlace ) {
++ d = _numToDecimal( d, decimalPlace );
++ }
++
++ if ( d.replace ) {
++ if ( re1 ) {
++ d = d.replace( re1, '' );
++ }
++
++ if ( re2 ) {
++ d = d.replace( re2, '' );
++ }
++ }
++
++ return d * 1;
++ };
++
++
++ // Add the numeric 'deformatting' functions for sorting and search. This is done
++ // in a function to provide an easy ability for the language options to add
++ // additional methods if a non-period decimal place is used.
++ function _addNumericSort ( decimalPlace ) {
++ $.each(
++ {
++ // Plain numbers
++ "num": function ( d ) {
++ return __numericReplace( d, decimalPlace );
++ },
++
++ // Formatted numbers
++ "num-fmt": function ( d ) {
++ return __numericReplace( d, decimalPlace, _re_formatted_numeric );
++ },
++
++ // HTML numeric
++ "html-num": function ( d ) {
++ return __numericReplace( d, decimalPlace, _re_html );
++ },
++
++ // HTML numeric, formatted
++ "html-num-fmt": function ( d ) {
++ return __numericReplace( d, decimalPlace, _re_html, _re_formatted_numeric );
++ }
++ },
++ function ( key, fn ) {
++ // Add the ordering method
++ _ext.type.order[ key+decimalPlace+'-pre' ] = fn;
++
++ // For HTML types add a search formatter that will strip the HTML
++ if ( key.match(/^html\-/) ) {
++ _ext.type.search[ key+decimalPlace ] = _ext.type.search.html;
++ }
++ }
++ );
++ }
++
++
++ // Default sort methods
++ $.extend( _ext.type.order, {
++ // Dates
++ "date-pre": function ( d ) {
++ return Date.parse( d ) || 0;
++ },
++
++ // html
++ "html-pre": function ( a ) {
++ return _empty(a) ?
++ '' :
++ a.replace ?
++ a.replace( /<.*?>/g, "" ).toLowerCase() :
++ a+'';
++ },
++
++ // string
++ "string-pre": function ( a ) {
++ // This is a little complex, but faster than always calling toString,
++ // http://jsperf.com/tostring-v-check
++ return _empty(a) ?
++ '' :
++ typeof a === 'string' ?
++ a.toLowerCase() :
++ ! a.toString ?
++ '' :
++ a.toString();
++ },
++
++ // string-asc and -desc are retained only for compatibility with the old
++ // sort methods
++ "string-asc": function ( x, y ) {
++ return ((x < y) ? -1 : ((x > y) ? 1 : 0));
++ },
++
++ "string-desc": function ( x, y ) {
++ return ((x < y) ? 1 : ((x > y) ? -1 : 0));
++ }
++ } );
++
++
++ // Numeric sorting types - order doesn't matter here
++ _addNumericSort( '' );
++
++
++ $.extend( true, DataTable.ext.renderer, {
++ header: {
++ _: function ( settings, cell, column, classes ) {
++ // No additional mark-up required
++ // Attach a sort listener to update on sort - note that using the
++ // `DT` namespace will allow the event to be removed automatically
++ // on destroy, while the `dt` namespaced event is the one we are
++ // listening for
++ $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
++ if ( settings !== ctx ) { // need to check this this is the host
++ return; // table, not a nested one
++ }
++
++ var colIdx = column.idx;
++
++ cell
++ .removeClass(
++ column.sSortingClass +' '+
++ classes.sSortAsc +' '+
++ classes.sSortDesc
++ )
++ .addClass( columns[ colIdx ] == 'asc' ?
++ classes.sSortAsc : columns[ colIdx ] == 'desc' ?
++ classes.sSortDesc :
++ column.sSortingClass
++ );
++ } );
++ },
++
++ jqueryui: function ( settings, cell, column, classes ) {
++ $('<div/>')
++ .addClass( classes.sSortJUIWrapper )
++ .append( cell.contents() )
++ .append( $('<span/>')
++ .addClass( classes.sSortIcon+' '+column.sSortingClassJUI )
++ )
++ .appendTo( cell );
++
++ // Attach a sort listener to update on sort
++ $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
++ if ( settings !== ctx ) {
++ return;
++ }
++
++ var colIdx = column.idx;
++
++ cell
++ .removeClass( classes.sSortAsc +" "+classes.sSortDesc )
++ .addClass( columns[ colIdx ] == 'asc' ?
++ classes.sSortAsc : columns[ colIdx ] == 'desc' ?
++ classes.sSortDesc :
++ column.sSortingClass
++ );
++
++ cell
++ .find( 'span.'+classes.sSortIcon )
++ .removeClass(
++ classes.sSortJUIAsc +" "+
++ classes.sSortJUIDesc +" "+
++ classes.sSortJUI +" "+
++ classes.sSortJUIAscAllowed +" "+
++ classes.sSortJUIDescAllowed
++ )
++ .addClass( columns[ colIdx ] == 'asc' ?
++ classes.sSortJUIAsc : columns[ colIdx ] == 'desc' ?
++ classes.sSortJUIDesc :
++ column.sSortingClassJUI
++ );
++ } );
++ }
++ }
++ } );
++
++ /*
++ * Public helper functions. These aren't used internally by DataTables, or
++ * called by any of the options passed into DataTables, but they can be used
++ * externally by developers working with DataTables. They are helper functions
++ * to make working with DataTables a little bit easier.
++ */
++
++ /**
++ * Helpers for `columns.render`.
++ *
++ * The options defined here can be used with the `columns.render` initialisation
++ * option to provide a display renderer. The following functions are defined:
++ *
++ * * `number` - Will format numeric data (defined by `columns.data`) for
++ * display, retaining the original unformatted data for sorting and filtering.
++ * It takes 4 parameters:
++ * * `string` - Thousands grouping separator
++ * * `string` - Decimal point indicator
++ * * `integer` - Number of decimal points to show
++ * * `string` (optional) - Prefix.
++ *
++ * @example
++ * // Column definition using the number renderer
++ * {
++ * data: "salary",
++ * render: $.fn.dataTable.render.number( '\'', '.', 0, '$' )
++ * }
++ *
++ * @namespace
++ */
++ DataTable.render = {
++ number: function ( thousands, decimal, precision, prefix ) {
++ return {
++ display: function ( d ) {
++ if ( typeof d !== 'number' && typeof d !== 'string' ) {
++ return d;
++ }
++
++ var negative = d < 0 ? '-' : '';
++ d = Math.abs( parseFloat( d ) );
++
++ var intPart = parseInt( d, 10 );
++ var floatPart = precision ?
++ decimal+(d - intPart).toFixed( precision ).substring( 2 ):
++ '';
++
++ return negative + (prefix||'') +
++ intPart.toString().replace(
++ /\B(?=(\d{3})+(?!\d))/g, thousands
++ ) +
++ floatPart;
++ }
++ };
++ }
++ };
++
++
++ /*
++ * This is really a good bit rubbish this method of exposing the internal methods
++ * publicly... - To be fixed in 2.0 using methods on the prototype
++ */
++
++
++ /**
++ * Create a wrapper function for exporting an internal functions to an external API.
++ * @param {string} fn API function name
++ * @returns {function} wrapped function
++ * @memberof DataTable#internal
++ */
++ function _fnExternApiFunc (fn)
++ {
++ return function() {
++ var args = [_fnSettingsFromNode( this[DataTable.ext.iApiIndex] )].concat(
++ Array.prototype.slice.call(arguments)
++ );
++ return DataTable.ext.internal[fn].apply( this, args );
++ };
++ }
++
++
++ /**
++ * Reference to internal functions for use by plug-in developers. Note that
++ * these methods are references to internal functions and are considered to be
++ * private. If you use these methods, be aware that they are liable to change
++ * between versions.
++ * @namespace
++ */
++ $.extend( DataTable.ext.internal, {
++ _fnExternApiFunc: _fnExternApiFunc,
++ _fnBuildAjax: _fnBuildAjax,
++ _fnAjaxUpdate: _fnAjaxUpdate,
++ _fnAjaxParameters: _fnAjaxParameters,
++ _fnAjaxUpdateDraw: _fnAjaxUpdateDraw,
++ _fnAjaxDataSrc: _fnAjaxDataSrc,
++ _fnAddColumn: _fnAddColumn,
++ _fnColumnOptions: _fnColumnOptions,
++ _fnAdjustColumnSizing: _fnAdjustColumnSizing,
++ _fnVisibleToColumnIndex: _fnVisibleToColumnIndex,
++ _fnColumnIndexToVisible: _fnColumnIndexToVisible,
++ _fnVisbleColumns: _fnVisbleColumns,
++ _fnGetColumns: _fnGetColumns,
++ _fnColumnTypes: _fnColumnTypes,
++ _fnApplyColumnDefs: _fnApplyColumnDefs,
++ _fnHungarianMap: _fnHungarianMap,
++ _fnCamelToHungarian: _fnCamelToHungarian,
++ _fnLanguageCompat: _fnLanguageCompat,
++ _fnBrowserDetect: _fnBrowserDetect,
++ _fnAddData: _fnAddData,
++ _fnAddTr: _fnAddTr,
++ _fnNodeToDataIndex: _fnNodeToDataIndex,
++ _fnNodeToColumnIndex: _fnNodeToColumnIndex,
++ _fnGetCellData: _fnGetCellData,
++ _fnSetCellData: _fnSetCellData,
++ _fnSplitObjNotation: _fnSplitObjNotation,
++ _fnGetObjectDataFn: _fnGetObjectDataFn,
++ _fnSetObjectDataFn: _fnSetObjectDataFn,
++ _fnGetDataMaster: _fnGetDataMaster,
++ _fnClearTable: _fnClearTable,
++ _fnDeleteIndex: _fnDeleteIndex,
++ _fnInvalidate: _fnInvalidate,
++ _fnGetRowElements: _fnGetRowElements,
++ _fnCreateTr: _fnCreateTr,
++ _fnBuildHead: _fnBuildHead,
++ _fnDrawHead: _fnDrawHead,
++ _fnDraw: _fnDraw,
++ _fnReDraw: _fnReDraw,
++ _fnAddOptionsHtml: _fnAddOptionsHtml,
++ _fnDetectHeader: _fnDetectHeader,
++ _fnGetUniqueThs: _fnGetUniqueThs,
++ _fnFeatureHtmlFilter: _fnFeatureHtmlFilter,
++ _fnFilterComplete: _fnFilterComplete,
++ _fnFilterCustom: _fnFilterCustom,
++ _fnFilterColumn: _fnFilterColumn,
++ _fnFilter: _fnFilter,
++ _fnFilterCreateSearch: _fnFilterCreateSearch,
++ _fnEscapeRegex: _fnEscapeRegex,
++ _fnFilterData: _fnFilterData,
++ _fnFeatureHtmlInfo: _fnFeatureHtmlInfo,
++ _fnUpdateInfo: _fnUpdateInfo,
++ _fnInfoMacros: _fnInfoMacros,
++ _fnInitialise: _fnInitialise,
++ _fnInitComplete: _fnInitComplete,
++ _fnLengthChange: _fnLengthChange,
++ _fnFeatureHtmlLength: _fnFeatureHtmlLength,
++ _fnFeatureHtmlPaginate: _fnFeatureHtmlPaginate,
++ _fnPageChange: _fnPageChange,
++ _fnFeatureHtmlProcessing: _fnFeatureHtmlProcessing,
++ _fnProcessingDisplay: _fnProcessingDisplay,
++ _fnFeatureHtmlTable: _fnFeatureHtmlTable,
++ _fnScrollDraw: _fnScrollDraw,
++ _fnApplyToChildren: _fnApplyToChildren,
++ _fnCalculateColumnWidths: _fnCalculateColumnWidths,
++ _fnThrottle: _fnThrottle,
++ _fnConvertToWidth: _fnConvertToWidth,
++ _fnScrollingWidthAdjust: _fnScrollingWidthAdjust,
++ _fnGetWidestNode: _fnGetWidestNode,
++ _fnGetMaxLenString: _fnGetMaxLenString,
++ _fnStringToCss: _fnStringToCss,
++ _fnScrollBarWidth: _fnScrollBarWidth,
++ _fnSortFlatten: _fnSortFlatten,
++ _fnSort: _fnSort,
++ _fnSortAria: _fnSortAria,
++ _fnSortListener: _fnSortListener,
++ _fnSortAttachListener: _fnSortAttachListener,
++ _fnSortingClasses: _fnSortingClasses,
++ _fnSortData: _fnSortData,
++ _fnSaveState: _fnSaveState,
++ _fnLoadState: _fnLoadState,
++ _fnSettingsFromNode: _fnSettingsFromNode,
++ _fnLog: _fnLog,
++ _fnMap: _fnMap,
++ _fnBindAction: _fnBindAction,
++ _fnCallbackReg: _fnCallbackReg,
++ _fnCallbackFire: _fnCallbackFire,
++ _fnLengthOverflow: _fnLengthOverflow,
++ _fnRenderer: _fnRenderer,
++ _fnDataSource: _fnDataSource,
++ _fnRowAttributes: _fnRowAttributes,
++ _fnCalculateEnd: function () {} // Used by a lot of plug-ins, but redundant
++ // in 1.10, so this dead-end function is
++ // added to prevent errors
++ } );
++
++
++ // jQuery access
++ $.fn.dataTable = DataTable;
++
++ // Legacy aliases
++ $.fn.dataTableSettings = DataTable.settings;
++ $.fn.dataTableExt = DataTable.ext;
++
++ // With a capital `D` we return a DataTables API instance rather than a
++ // jQuery object
++ $.fn.DataTable = function ( opts ) {
++ return $(this).dataTable( opts ).api();
++ };
++
++ // All properties that are available to $.fn.dataTable should also be
++ // available on $.fn.DataTable
++ $.each( DataTable, function ( prop, val ) {
++ $.fn.DataTable[ prop ] = val;
++ } );
++
++
++ // Information about events fired by DataTables - for documentation.
++ /**
++ * Draw event, fired whenever the table is redrawn on the page, at the same
++ * point as fnDrawCallback. This may be useful for binding events or
++ * performing calculations when the table is altered at all.
++ * @name DataTable#draw.dt
++ * @event
++ * @param {event} e jQuery event object
++ * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
++ */
++
++ /**
++ * Search event, fired when the searching applied to the table (using the
++ * built-in global search, or column filters) is altered.
++ * @name DataTable#search.dt
++ * @event
++ * @param {event} e jQuery event object
++ * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
++ */
++
++ /**
++ * Page change event, fired when the paging of the table is altered.
++ * @name DataTable#page.dt
++ * @event
++ * @param {event} e jQuery event object
++ * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
++ */
++
++ /**
++ * Order event, fired when the ordering applied to the table is altered.
++ * @name DataTable#order.dt
++ * @event
++ * @param {event} e jQuery event object
++ * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
++ */
++
++ /**
++ * DataTables initialisation complete event, fired when the table is fully
++ * drawn, including Ajax data loaded, if Ajax data is required.
++ * @name DataTable#init.dt
++ * @event
++ * @param {event} e jQuery event object
++ * @param {object} oSettings DataTables settings object
++ * @param {object} json The JSON object request from the server - only
++ * present if client-side Ajax sourced data is used</li></ol>
++ */
++
++ /**
++ * State save event, fired when the table has changed state a new state save
++ * is required. This event allows modification of the state saving object
++ * prior to actually doing the save, including addition or other state
++ * properties (for plug-ins) or modification of a DataTables core property.
++ * @name DataTable#stateSaveParams.dt
++ * @event
++ * @param {event} e jQuery event object
++ * @param {object} oSettings DataTables settings object
++ * @param {object} json The state information to be saved
++ */
++
++ /**
++ * State load event, fired when the table is loading state from the stored
++ * data, but prior to the settings object being modified by the saved state
++ * - allowing modification of the saved state is required or loading of
++ * state for a plug-in.
++ * @name DataTable#stateLoadParams.dt
++ * @event
++ * @param {event} e jQuery event object
++ * @param {object} oSettings DataTables settings object
++ * @param {object} json The saved state information
++ */
++
++ /**
++ * State loaded event, fired when state has been loaded from stored data and
++ * the settings object has been modified by the loaded data.
++ * @name DataTable#stateLoaded.dt
++ * @event
++ * @param {event} e jQuery event object
++ * @param {object} oSettings DataTables settings object
++ * @param {object} json The saved state information
++ */
++
++ /**
++ * Processing event, fired when DataTables is doing some kind of processing
++ * (be it, order, searcg or anything else). It can be used to indicate to
++ * the end user that there is something happening, or that something has
++ * finished.
++ * @name DataTable#processing.dt
++ * @event
++ * @param {event} e jQuery event object
++ * @param {object} oSettings DataTables settings object
++ * @param {boolean} bShow Flag for if DataTables is doing processing or not
++ */
++
++ /**
++ * Ajax (XHR) event, fired whenever an Ajax request is completed from a
++ * request to made to the server for new data. This event is called before
++ * DataTables processed the returned data, so it can also be used to pre-
++ * process the data returned from the server, if needed.
++ *
++ * Note that this trigger is called in `fnServerData`, if you override
++ * `fnServerData` and which to use this event, you need to trigger it in you
++ * success function.
++ * @name DataTable#xhr.dt
++ * @event
++ * @param {event} e jQuery event object
++ * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
++ * @param {object} json JSON returned from the server
++ *
++ * @example
++ * // Use a custom property returned from the server in another DOM element
++ * $('#table').dataTable().on('xhr.dt', function (e, settings, json) {
++ * $('#status').html( json.status );
++ * } );
++ *
++ * @example
++ * // Pre-process the data returned from the server
++ * $('#table').dataTable().on('xhr.dt', function (e, settings, json) {
++ * for ( var i=0, ien=json.aaData.length ; i<ien ; i++ ) {
++ * json.aaData[i].sum = json.aaData[i].one + json.aaData[i].two;
++ * }
++ * // Note no return - manipulate the data directly in the JSON object.
++ * } );
++ */
++
++ /**
++ * Destroy event, fired when the DataTable is destroyed by calling fnDestroy
++ * or passing the bDestroy:true parameter in the initialisation object. This
++ * can be used to remove bound events, added DOM nodes, etc.
++ * @name DataTable#destroy.dt
++ * @event
++ * @param {event} e jQuery event object
++ * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
++ */
++
++ /**
++ * Page length change event, fired when number of records to show on each
++ * page (the length) is changed.
++ * @name DataTable#length.dt
++ * @event
++ * @param {event} e jQuery event object
++ * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
++ * @param {integer} len New length
++ */
++
++ /**
++ * Column sizing has changed.
++ * @name DataTable#column-sizing.dt
++ * @event
++ * @param {event} e jQuery event object
++ * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
++ */
++
++ /**
++ * Column visibility has changed.
++ * @name DataTable#column-visibility.dt
++ * @event
++ * @param {event} e jQuery event object
++ * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
++ * @param {int} column Column index
++ * @param {bool} vis `false` if column now hidden, or `true` if visible
++ */
++
++ return $.fn.dataTable;
++}));
++
++}(window, document));
++
--- /dev/null
--- /dev/null
++// Ion.RangeSlider
++// version 2.1.2 Build: 350
++// © Denis Ineshin, 2015
++// https://github.com/IonDen
++//
++// Project page: http://ionden.com/a/plugins/ion.rangeSlider/en.html
++// GitHub page: https://github.com/IonDen/ion.rangeSlider
++//
++// Released under MIT licence:
++// http://ionden.com/a/plugins/licence-en.html
++// =====================================================================================================================
++
++;(function ($, document, window, navigator, undefined) {
++ "use strict";
++
++ // =================================================================================================================
++ // Service
++
++ var plugin_count = 0;
++
++ // IE8 fix
++ var is_old_ie = (function () {
++ var n = navigator.userAgent,
++ r = /msie\s\d+/i,
++ v;
++ if (n.search(r) > 0) {
++ v = r.exec(n).toString();
++ v = v.split(" ")[1];
++ if (v < 9) {
++ $("html").addClass("lt-ie9");
++ return true;
++ }
++ }
++ return false;
++ } ());
++ if (!Function.prototype.bind) {
++ Function.prototype.bind = function bind(that) {
++
++ var target = this;
++ var slice = [].slice;
++
++ if (typeof target != "function") {
++ throw new TypeError();
++ }
++
++ var args = slice.call(arguments, 1),
++ bound = function () {
++
++ if (this instanceof bound) {
++
++ var F = function(){};
++ F.prototype = target.prototype;
++ var self = new F();
++
++ var result = target.apply(
++ self,
++ args.concat(slice.call(arguments))
++ );
++ if (Object(result) === result) {
++ return result;
++ }
++ return self;
++
++ } else {
++
++ return target.apply(
++ that,
++ args.concat(slice.call(arguments))
++ );
++
++ }
++
++ };
++
++ return bound;
++ };
++ }
++ if (!Array.prototype.indexOf) {
++ Array.prototype.indexOf = function(searchElement, fromIndex) {
++ var k;
++ if (this == null) {
++ throw new TypeError('"this" is null or not defined');
++ }
++ var O = Object(this);
++ var len = O.length >>> 0;
++ if (len === 0) {
++ return -1;
++ }
++ var n = +fromIndex || 0;
++ if (Math.abs(n) === Infinity) {
++ n = 0;
++ }
++ if (n >= len) {
++ return -1;
++ }
++ k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
++ while (k < len) {
++ if (k in O && O[k] === searchElement) {
++ return k;
++ }
++ k++;
++ }
++ return -1;
++ };
++ }
++
++
++
++ // =================================================================================================================
++ // Template
++
++ var base_html =
++ '<span class="irs">' +
++ '<span class="irs-line" tabindex="-1"><span class="irs-line-left"></span><span class="irs-line-mid"></span><span class="irs-line-right"></span></span>' +
++ '<span class="irs-min">0</span><span class="irs-max">1</span>' +
++ '<span class="irs-from">0</span><span class="irs-to">0</span><span class="irs-single">0</span>' +
++ '</span>' +
++ '<span class="irs-grid"></span>' +
++ '<span class="irs-bar"></span>';
++
++ var single_html =
++ '<span class="irs-bar-edge"></span>' +
++ '<span class="irs-shadow shadow-single"></span>' +
++ '<span class="irs-slider single"></span>';
++
++ var double_html =
++ '<span class="irs-shadow shadow-from"></span>' +
++ '<span class="irs-shadow shadow-to"></span>' +
++ '<span class="irs-slider from"></span>' +
++ '<span class="irs-slider to"></span>';
++
++ var disable_html =
++ '<span class="irs-disable-mask"></span>';
++
++
++
++ // =================================================================================================================
++ // Core
++
++ /**
++ * Main plugin constructor
++ *
++ * @param input {Object} link to base input element
++ * @param options {Object} slider config
++ * @param plugin_count {Number}
++ * @constructor
++ */
++ var IonRangeSlider = function (input, options, plugin_count) {
++ this.VERSION = "2.1.2";
++ this.input = input;
++ this.plugin_count = plugin_count;
++ this.current_plugin = 0;
++ this.calc_count = 0;
++ this.update_tm = 0;
++ this.old_from = 0;
++ this.old_to = 0;
++ this.old_min_interval = null;
++ this.raf_id = null;
++ this.dragging = false;
++ this.force_redraw = false;
++ this.no_diapason = false;
++ this.is_key = false;
++ this.is_update = false;
++ this.is_start = true;
++ this.is_finish = false;
++ this.is_active = false;
++ this.is_resize = false;
++ this.is_click = false;
++
++ // cache for links to all DOM elements
++ this.$cache = {
++ win: $(window),
++ body: $(document.body),
++ input: $(input),
++ cont: null,
++ rs: null,
++ min: null,
++ max: null,
++ from: null,
++ to: null,
++ single: null,
++ bar: null,
++ line: null,
++ s_single: null,
++ s_from: null,
++ s_to: null,
++ shad_single: null,
++ shad_from: null,
++ shad_to: null,
++ edge: null,
++ grid: null,
++ grid_labels: []
++ };
++
++ // storage for measure variables
++ this.coords = {
++ // left
++ x_gap: 0,
++ x_pointer: 0,
++
++ // width
++ w_rs: 0,
++ w_rs_old: 0,
++ w_handle: 0,
++
++ // percents
++ p_gap: 0,
++ p_gap_left: 0,
++ p_gap_right: 0,
++ p_step: 0,
++ p_pointer: 0,
++ p_handle: 0,
++ p_single_fake: 0,
++ p_single_real: 0,
++ p_from_fake: 0,
++ p_from_real: 0,
++ p_to_fake: 0,
++ p_to_real: 0,
++ p_bar_x: 0,
++ p_bar_w: 0,
++
++ // grid
++ grid_gap: 0,
++ big_num: 0,
++ big: [],
++ big_w: [],
++ big_p: [],
++ big_x: []
++ };
++
++ // storage for labels measure variables
++ this.labels = {
++ // width
++ w_min: 0,
++ w_max: 0,
++ w_from: 0,
++ w_to: 0,
++ w_single: 0,
++
++ // percents
++ p_min: 0,
++ p_max: 0,
++ p_from_fake: 0,
++ p_from_left: 0,
++ p_to_fake: 0,
++ p_to_left: 0,
++ p_single_fake: 0,
++ p_single_left: 0
++ };
++
++
++
++ /**
++ * get and validate config
++ */
++ var $inp = this.$cache.input,
++ val = $inp.prop("value"),
++ config, config_from_data, prop;
++
++ // default config
++ config = {
++ type: "single",
++
++ min: 10,
++ max: 100,
++ from: null,
++ to: null,
++ step: 1,
++
++ min_interval: 0,
++ max_interval: 0,
++ drag_interval: false,
++
++ values: [],
++ p_values: [],
++
++ from_fixed: false,
++ from_min: null,
++ from_max: null,
++ from_shadow: false,
++
++ to_fixed: false,
++ to_min: null,
++ to_max: null,
++ to_shadow: false,
++
++ prettify_enabled: true,
++ prettify_separator: " ",
++ prettify: null,
++
++ force_edges: false,
++
++ keyboard: false,
++ keyboard_step: 5,
++
++ grid: false,
++ grid_margin: true,
++ grid_num: 4,
++ grid_snap: false,
++
++ hide_min_max: false,
++ hide_from_to: false,
++
++ prefix: "",
++ postfix: "",
++ max_postfix: "",
++ decorate_both: true,
++ values_separator: " — ",
++
++ input_values_separator: ";",
++
++ disable: false,
++
++ onStart: null,
++ onChange: null,
++ onFinish: null,
++ onUpdate: null
++ };
++
++
++
++ // config from data-attributes extends js config
++ config_from_data = {
++ type: $inp.data("type"),
++
++ min: $inp.data("min"),
++ max: $inp.data("max"),
++ from: $inp.data("from"),
++ to: $inp.data("to"),
++ step: $inp.data("step"),
++
++ min_interval: $inp.data("minInterval"),
++ max_interval: $inp.data("maxInterval"),
++ drag_interval: $inp.data("dragInterval"),
++
++ values: $inp.data("values"),
++
++ from_fixed: $inp.data("fromFixed"),
++ from_min: $inp.data("fromMin"),
++ from_max: $inp.data("fromMax"),
++ from_shadow: $inp.data("fromShadow"),
++
++ to_fixed: $inp.data("toFixed"),
++ to_min: $inp.data("toMin"),
++ to_max: $inp.data("toMax"),
++ to_shadow: $inp.data("toShadow"),
++
++ prettify_enabled: $inp.data("prettifyEnabled"),
++ prettify_separator: $inp.data("prettifySeparator"),
++
++ force_edges: $inp.data("forceEdges"),
++
++ keyboard: $inp.data("keyboard"),
++ keyboard_step: $inp.data("keyboardStep"),
++
++ grid: $inp.data("grid"),
++ grid_margin: $inp.data("gridMargin"),
++ grid_num: $inp.data("gridNum"),
++ grid_snap: $inp.data("gridSnap"),
++
++ hide_min_max: $inp.data("hideMinMax"),
++ hide_from_to: $inp.data("hideFromTo"),
++
++ prefix: $inp.data("prefix"),
++ postfix: $inp.data("postfix"),
++ max_postfix: $inp.data("maxPostfix"),
++ decorate_both: $inp.data("decorateBoth"),
++ values_separator: $inp.data("valuesSeparator"),
++
++ input_values_separator: $inp.data("inputValuesSeparator"),
++
++ disable: $inp.data("disable")
++ };
++ config_from_data.values = config_from_data.values && config_from_data.values.split(",");
++
++ for (prop in config_from_data) {
++ if (config_from_data.hasOwnProperty(prop)) {
++ if (!config_from_data[prop] && config_from_data[prop] !== 0) {
++ delete config_from_data[prop];
++ }
++ }
++ }
++
++
++
++ // input value extends default config
++ if (val) {
++ val = val.split(config_from_data.input_values_separator || options.input_values_separator || ";");
++
++ if (val[0] && val[0] == +val[0]) {
++ val[0] = +val[0];
++ }
++ if (val[1] && val[1] == +val[1]) {
++ val[1] = +val[1];
++ }
++
++ if (options && options.values && options.values.length) {
++ config.from = val[0] && options.values.indexOf(val[0]);
++ config.to = val[1] && options.values.indexOf(val[1]);
++ } else {
++ config.from = val[0] && +val[0];
++ config.to = val[1] && +val[1];
++ }
++ }
++
++
++
++ // js config extends default config
++ $.extend(config, options);
++
++
++ // data config extends config
++ $.extend(config, config_from_data);
++ this.options = config;
++
++
++
++ // validate config, to be sure that all data types are correct
++ this.validate();
++
++
++
++ // default result object, returned to callbacks
++ this.result = {
++ input: this.$cache.input,
++ slider: null,
++
++ min: this.options.min,
++ max: this.options.max,
++
++ from: this.options.from,
++ from_percent: 0,
++ from_value: null,
++
++ to: this.options.to,
++ to_percent: 0,
++ to_value: null
++ };
++
++
++
++ this.init();
++ };
++
++ IonRangeSlider.prototype = {
++
++ /**
++ * Starts or updates the plugin instance
++ *
++ * @param is_update {boolean}
++ */
++ init: function (is_update) {
++ this.no_diapason = false;
++ this.coords.p_step = this.convertToPercent(this.options.step, true);
++
++ this.target = "base";
++
++ this.toggleInput();
++ this.append();
++ this.setMinMax();
++
++ if (is_update) {
++ this.force_redraw = true;
++ this.calc(true);
++
++ // callbacks called
++ this.callOnUpdate();
++ } else {
++ this.force_redraw = true;
++ this.calc(true);
++
++ // callbacks called
++ this.callOnStart();
++ }
++
++ this.updateScene();
++ },
++
++ /**
++ * Appends slider template to a DOM
++ */
++ append: function () {
++ var container_html = '<span class="irs js-irs-' + this.plugin_count + '"></span>';
++ this.$cache.input.before(container_html);
++ this.$cache.input.prop("readonly", true);
++ this.$cache.cont = this.$cache.input.prev();
++ this.result.slider = this.$cache.cont;
++
++ this.$cache.cont.html(base_html);
++ this.$cache.rs = this.$cache.cont.find(".irs");
++ this.$cache.min = this.$cache.cont.find(".irs-min");
++ this.$cache.max = this.$cache.cont.find(".irs-max");
++ this.$cache.from = this.$cache.cont.find(".irs-from");
++ this.$cache.to = this.$cache.cont.find(".irs-to");
++ this.$cache.single = this.$cache.cont.find(".irs-single");
++ this.$cache.bar = this.$cache.cont.find(".irs-bar");
++ this.$cache.line = this.$cache.cont.find(".irs-line");
++ this.$cache.grid = this.$cache.cont.find(".irs-grid");
++
++ if (this.options.type === "single") {
++ this.$cache.cont.append(single_html);
++ this.$cache.edge = this.$cache.cont.find(".irs-bar-edge");
++ this.$cache.s_single = this.$cache.cont.find(".single");
++ this.$cache.from[0].style.visibility = "hidden";
++ this.$cache.to[0].style.visibility = "hidden";
++ this.$cache.shad_single = this.$cache.cont.find(".shadow-single");
++ } else {
++ this.$cache.cont.append(double_html);
++ this.$cache.s_from = this.$cache.cont.find(".from");
++ this.$cache.s_to = this.$cache.cont.find(".to");
++ this.$cache.shad_from = this.$cache.cont.find(".shadow-from");
++ this.$cache.shad_to = this.$cache.cont.find(".shadow-to");
++
++ this.setTopHandler();
++ }
++
++ if (this.options.hide_from_to) {
++ this.$cache.from[0].style.display = "none";
++ this.$cache.to[0].style.display = "none";
++ this.$cache.single[0].style.display = "none";
++ }
++
++ this.appendGrid();
++
++ if (this.options.disable) {
++ this.appendDisableMask();
++ this.$cache.input[0].disabled = true;
++ } else {
++ this.$cache.cont.removeClass("irs-disabled");
++ this.$cache.input[0].disabled = false;
++ this.bindEvents();
++ }
++
++ if (this.options.drag_interval) {
++ this.$cache.bar[0].style.cursor = "ew-resize";
++ }
++ },
++
++ /**
++ * Determine which handler has a priority
++ * works only for double slider type
++ */
++ setTopHandler: function () {
++ var min = this.options.min,
++ max = this.options.max,
++ from = this.options.from,
++ to = this.options.to;
++
++ if (from > min && to === max) {
++ this.$cache.s_from.addClass("type_last");
++ } else if (to < max) {
++ this.$cache.s_to.addClass("type_last");
++ }
++ },
++
++ /**
++ * Determine which handles was clicked last
++ * and which handler should have hover effect
++ *
++ * @param target {String}
++ */
++ changeLevel: function (target) {
++ switch (target) {
++ case "single":
++ this.coords.p_gap = this.toFixed(this.coords.p_pointer - this.coords.p_single_fake);
++ break;
++ case "from":
++ this.coords.p_gap = this.toFixed(this.coords.p_pointer - this.coords.p_from_fake);
++ this.$cache.s_from.addClass("state_hover");
++ this.$cache.s_from.addClass("type_last");
++ this.$cache.s_to.removeClass("type_last");
++ break;
++ case "to":
++ this.coords.p_gap = this.toFixed(this.coords.p_pointer - this.coords.p_to_fake);
++ this.$cache.s_to.addClass("state_hover");
++ this.$cache.s_to.addClass("type_last");
++ this.$cache.s_from.removeClass("type_last");
++ break;
++ case "both":
++ this.coords.p_gap_left = this.toFixed(this.coords.p_pointer - this.coords.p_from_fake);
++ this.coords.p_gap_right = this.toFixed(this.coords.p_to_fake - this.coords.p_pointer);
++ this.$cache.s_to.removeClass("type_last");
++ this.$cache.s_from.removeClass("type_last");
++ break;
++ }
++ },
++
++ /**
++ * Then slider is disabled
++ * appends extra layer with opacity
++ */
++ appendDisableMask: function () {
++ this.$cache.cont.append(disable_html);
++ this.$cache.cont.addClass("irs-disabled");
++ },
++
++ /**
++ * Remove slider instance
++ * and ubind all events
++ */
++ remove: function () {
++ this.$cache.cont.remove();
++ this.$cache.cont = null;
++
++ this.$cache.line.off("keydown.irs_" + this.plugin_count);
++
++ this.$cache.body.off("touchmove.irs_" + this.plugin_count);
++ this.$cache.body.off("mousemove.irs_" + this.plugin_count);
++
++ this.$cache.win.off("touchend.irs_" + this.plugin_count);
++ this.$cache.win.off("mouseup.irs_" + this.plugin_count);
++
++ if (is_old_ie) {
++ this.$cache.body.off("mouseup.irs_" + this.plugin_count);
++ this.$cache.body.off("mouseleave.irs_" + this.plugin_count);
++ }
++
++ this.$cache.grid_labels = [];
++ this.coords.big = [];
++ this.coords.big_w = [];
++ this.coords.big_p = [];
++ this.coords.big_x = [];
++
++ cancelAnimationFrame(this.raf_id);
++ },
++
++ /**
++ * bind all slider events
++ */
++ bindEvents: function () {
++ if (this.no_diapason) {
++ return;
++ }
++
++ this.$cache.body.on("touchmove.irs_" + this.plugin_count, this.pointerMove.bind(this));
++ this.$cache.body.on("mousemove.irs_" + this.plugin_count, this.pointerMove.bind(this));
++
++ this.$cache.win.on("touchend.irs_" + this.plugin_count, this.pointerUp.bind(this));
++ this.$cache.win.on("mouseup.irs_" + this.plugin_count, this.pointerUp.bind(this));
++
++ this.$cache.line.on("touchstart.irs_" + this.plugin_count, this.pointerClick.bind(this, "click"));
++ this.$cache.line.on("mousedown.irs_" + this.plugin_count, this.pointerClick.bind(this, "click"));
++
++ if (this.options.drag_interval && this.options.type === "double") {
++ this.$cache.bar.on("touchstart.irs_" + this.plugin_count, this.pointerDown.bind(this, "both"));
++ this.$cache.bar.on("mousedown.irs_" + this.plugin_count, this.pointerDown.bind(this, "both"));
++ } else {
++ this.$cache.bar.on("touchstart.irs_" + this.plugin_count, this.pointerClick.bind(this, "click"));
++ this.$cache.bar.on("mousedown.irs_" + this.plugin_count, this.pointerClick.bind(this, "click"));
++ }
++
++ if (this.options.type === "single") {
++ this.$cache.single.on("touchstart.irs_" + this.plugin_count, this.pointerDown.bind(this, "single"));
++ this.$cache.s_single.on("touchstart.irs_" + this.plugin_count, this.pointerDown.bind(this, "single"));
++ this.$cache.shad_single.on("touchstart.irs_" + this.plugin_count, this.pointerClick.bind(this, "click"));
++
++ this.$cache.single.on("mousedown.irs_" + this.plugin_count, this.pointerDown.bind(this, "single"));
++ this.$cache.s_single.on("mousedown.irs_" + this.plugin_count, this.pointerDown.bind(this, "single"));
++ this.$cache.edge.on("mousedown.irs_" + this.plugin_count, this.pointerClick.bind(this, "click"));
++ this.$cache.shad_single.on("mousedown.irs_" + this.plugin_count, this.pointerClick.bind(this, "click"));
++ } else {
++ this.$cache.single.on("touchstart.irs_" + this.plugin_count, this.pointerDown.bind(this, null));
++ this.$cache.single.on("mousedown.irs_" + this.plugin_count, this.pointerDown.bind(this, null));
++
++ this.$cache.from.on("touchstart.irs_" + this.plugin_count, this.pointerDown.bind(this, "from"));
++ this.$cache.s_from.on("touchstart.irs_" + this.plugin_count, this.pointerDown.bind(this, "from"));
++ this.$cache.to.on("touchstart.irs_" + this.plugin_count, this.pointerDown.bind(this, "to"));
++ this.$cache.s_to.on("touchstart.irs_" + this.plugin_count, this.pointerDown.bind(this, "to"));
++ this.$cache.shad_from.on("touchstart.irs_" + this.plugin_count, this.pointerClick.bind(this, "click"));
++ this.$cache.shad_to.on("touchstart.irs_" + this.plugin_count, this.pointerClick.bind(this, "click"));
++
++ this.$cache.from.on("mousedown.irs_" + this.plugin_count, this.pointerDown.bind(this, "from"));
++ this.$cache.s_from.on("mousedown.irs_" + this.plugin_count, this.pointerDown.bind(this, "from"));
++ this.$cache.to.on("mousedown.irs_" + this.plugin_count, this.pointerDown.bind(this, "to"));
++ this.$cache.s_to.on("mousedown.irs_" + this.plugin_count, this.pointerDown.bind(this, "to"));
++ this.$cache.shad_from.on("mousedown.irs_" + this.plugin_count, this.pointerClick.bind(this, "click"));
++ this.$cache.shad_to.on("mousedown.irs_" + this.plugin_count, this.pointerClick.bind(this, "click"));
++ }
++
++ if (this.options.keyboard) {
++ this.$cache.line.on("keydown.irs_" + this.plugin_count, this.key.bind(this, "keyboard"));
++ }
++
++ if (is_old_ie) {
++ this.$cache.body.on("mouseup.irs_" + this.plugin_count, this.pointerUp.bind(this));
++ this.$cache.body.on("mouseleave.irs_" + this.plugin_count, this.pointerUp.bind(this));
++ }
++ },
++
++ /**
++ * Mousemove or touchmove
++ * only for handlers
++ *
++ * @param e {Object} event object
++ */
++ pointerMove: function (e) {
++ if (!this.dragging) {
++ return;
++ }
++
++ var x = e.pageX || e.originalEvent.touches && e.originalEvent.touches[0].pageX;
++ this.coords.x_pointer = x - this.coords.x_gap;
++
++ this.calc();
++ },
++
++ /**
++ * Mouseup or touchend
++ * only for handlers
++ *
++ * @param e {Object} event object
++ */
++ pointerUp: function (e) {
++ if (this.current_plugin !== this.plugin_count) {
++ return;
++ }
++
++ if (this.is_active) {
++ this.is_active = false;
++ } else {
++ return;
++ }
++
++ this.$cache.cont.find(".state_hover").removeClass("state_hover");
++
++ this.force_redraw = true;
++
++ if (is_old_ie) {
++ $("*").prop("unselectable", false);
++ }
++
++ this.updateScene();
++ this.restoreOriginalMinInterval();
++
++ // callbacks call
++ if ($.contains(this.$cache.cont[0], e.target) || this.dragging) {
++ this.is_finish = true;
++ this.callOnFinish();
++ }
++
++ this.dragging = false;
++ },
++
++ /**
++ * Mousedown or touchstart
++ * only for handlers
++ *
++ * @param target {String|null}
++ * @param e {Object} event object
++ */
++ pointerDown: function (target, e) {
++ e.preventDefault();
++ var x = e.pageX || e.originalEvent.touches && e.originalEvent.touches[0].pageX;
++ if (e.button === 2) {
++ return;
++ }
++
++ if (target === "both") {
++ this.setTempMinInterval();
++ }
++
++ if (!target) {
++ target = this.target;
++ }
++
++ this.current_plugin = this.plugin_count;
++ this.target = target;
++
++ this.is_active = true;
++ this.dragging = true;
++
++ this.coords.x_gap = this.$cache.rs.offset().left;
++ this.coords.x_pointer = x - this.coords.x_gap;
++
++ this.calcPointerPercent();
++ this.changeLevel(target);
++
++ if (is_old_ie) {
++ $("*").prop("unselectable", true);
++ }
++
++ this.$cache.line.trigger("focus");
++
++ this.updateScene();
++ },
++
++ /**
++ * Mousedown or touchstart
++ * for other slider elements, like diapason line
++ *
++ * @param target {String}
++ * @param e {Object} event object
++ */
++ pointerClick: function (target, e) {
++ e.preventDefault();
++ var x = e.pageX || e.originalEvent.touches && e.originalEvent.touches[0].pageX;
++ if (e.button === 2) {
++ return;
++ }
++
++ this.current_plugin = this.plugin_count;
++ this.target = target;
++
++ this.is_click = true;
++ this.coords.x_gap = this.$cache.rs.offset().left;
++ this.coords.x_pointer = +(x - this.coords.x_gap).toFixed();
++
++ this.force_redraw = true;
++ this.calc();
++
++ this.$cache.line.trigger("focus");
++ },
++
++ /**
++ * Keyborard controls for focused slider
++ *
++ * @param target {String}
++ * @param e {Object} event object
++ * @returns {boolean|undefined}
++ */
++ key: function (target, e) {
++ if (this.current_plugin !== this.plugin_count || e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) {
++ return;
++ }
++
++ switch (e.which) {
++ case 83: // W
++ case 65: // A
++ case 40: // DOWN
++ case 37: // LEFT
++ e.preventDefault();
++ this.moveByKey(false);
++ break;
++
++ case 87: // S
++ case 68: // D
++ case 38: // UP
++ case 39: // RIGHT
++ e.preventDefault();
++ this.moveByKey(true);
++ break;
++ }
++
++ return true;
++ },
++
++ /**
++ * Move by key. Beta
++ * @todo refactor than have plenty of time
++ *
++ * @param right {boolean} direction to move
++ */
++ moveByKey: function (right) {
++ var p = this.coords.p_pointer;
++
++ if (right) {
++ p += this.options.keyboard_step;
++ } else {
++ p -= this.options.keyboard_step;
++ }
++
++ this.coords.x_pointer = this.toFixed(this.coords.w_rs / 100 * p);
++ this.is_key = true;
++ this.calc();
++ },
++
++ /**
++ * Set visibility and content
++ * of Min and Max labels
++ */
++ setMinMax: function () {
++ if (!this.options) {
++ return;
++ }
++
++ if (this.options.hide_min_max) {
++ this.$cache.min[0].style.display = "none";
++ this.$cache.max[0].style.display = "none";
++ return;
++ }
++
++ if (this.options.values.length) {
++ this.$cache.min.html(this.decorate(this.options.p_values[this.options.min]));
++ this.$cache.max.html(this.decorate(this.options.p_values[this.options.max]));
++ } else {
++ this.$cache.min.html(this.decorate(this._prettify(this.options.min), this.options.min));
++ this.$cache.max.html(this.decorate(this._prettify(this.options.max), this.options.max));
++ }
++
++ this.labels.w_min = this.$cache.min.outerWidth(false);
++ this.labels.w_max = this.$cache.max.outerWidth(false);
++ },
++
++ /**
++ * Then dragging interval, prevent interval collapsing
++ * using min_interval option
++ */
++ setTempMinInterval: function () {
++ var interval = this.result.to - this.result.from;
++
++ if (this.old_min_interval === null) {
++ this.old_min_interval = this.options.min_interval;
++ }
++
++ this.options.min_interval = interval;
++ },
++
++ /**
++ * Restore min_interval option to original
++ */
++ restoreOriginalMinInterval: function () {
++ if (this.old_min_interval !== null) {
++ this.options.min_interval = this.old_min_interval;
++ this.old_min_interval = null;
++ }
++ },
++
++
++
++ // =============================================================================================================
++ // Calculations
++
++ /**
++ * All calculations and measures start here
++ *
++ * @param update {boolean=}
++ */
++ calc: function (update) {
++ if (!this.options) {
++ return;
++ }
++
++ this.calc_count++;
++
++ if (this.calc_count === 10 || update) {
++ this.calc_count = 0;
++ this.coords.w_rs = this.$cache.rs.outerWidth(false);
++
++ this.calcHandlePercent();
++ }
++
++ if (!this.coords.w_rs) {
++ return;
++ }
++
++ this.calcPointerPercent();
++ var handle_x = this.getHandleX();
++
++ if (this.target === "click") {
++ this.coords.p_gap = this.coords.p_handle / 2;
++ handle_x = this.getHandleX();
++
++ if (this.options.drag_interval) {
++ this.target = "both_one";
++ } else {
++ this.target = this.chooseHandle(handle_x);
++ }
++ }
++
++ switch (this.target) {
++ case "base":
++ var w = (this.options.max - this.options.min) / 100,
++ f = (this.result.from - this.options.min) / w,
++ t = (this.result.to - this.options.min) / w;
++
++ this.coords.p_single_real = this.toFixed(f);
++ this.coords.p_from_real = this.toFixed(f);
++ this.coords.p_to_real = this.toFixed(t);
++
++ this.coords.p_single_real = this.checkDiapason(this.coords.p_single_real, this.options.from_min, this.options.from_max);
++ this.coords.p_from_real = this.checkDiapason(this.coords.p_from_real, this.options.from_min, this.options.from_max);
++ this.coords.p_to_real = this.checkDiapason(this.coords.p_to_real, this.options.to_min, this.options.to_max);
++
++ this.coords.p_single_fake = this.convertToFakePercent(this.coords.p_single_real);
++ this.coords.p_from_fake = this.convertToFakePercent(this.coords.p_from_real);
++ this.coords.p_to_fake = this.convertToFakePercent(this.coords.p_to_real);
++
++ this.target = null;
++
++ break;
++
++ case "single":
++ if (this.options.from_fixed) {
++ break;
++ }
++
++ this.coords.p_single_real = this.convertToRealPercent(handle_x);
++ this.coords.p_single_real = this.calcWithStep(this.coords.p_single_real);
++ this.coords.p_single_real = this.checkDiapason(this.coords.p_single_real, this.options.from_min, this.options.from_max);
++
++ this.coords.p_single_fake = this.convertToFakePercent(this.coords.p_single_real);
++
++ break;
++
++ case "from":
++ if (this.options.from_fixed) {
++ break;
++ }
++
++ this.coords.p_from_real = this.convertToRealPercent(handle_x);
++ this.coords.p_from_real = this.calcWithStep(this.coords.p_from_real);
++ if (this.coords.p_from_real > this.coords.p_to_real) {
++ this.coords.p_from_real = this.coords.p_to_real;
++ }
++ this.coords.p_from_real = this.checkDiapason(this.coords.p_from_real, this.options.from_min, this.options.from_max);
++ this.coords.p_from_real = this.checkMinInterval(this.coords.p_from_real, this.coords.p_to_real, "from");
++ this.coords.p_from_real = this.checkMaxInterval(this.coords.p_from_real, this.coords.p_to_real, "from");
++
++ this.coords.p_from_fake = this.convertToFakePercent(this.coords.p_from_real);
++
++ break;
++
++ case "to":
++ if (this.options.to_fixed) {
++ break;
++ }
++
++ this.coords.p_to_real = this.convertToRealPercent(handle_x);
++ this.coords.p_to_real = this.calcWithStep(this.coords.p_to_real);
++ if (this.coords.p_to_real < this.coords.p_from_real) {
++ this.coords.p_to_real = this.coords.p_from_real;
++ }
++ this.coords.p_to_real = this.checkDiapason(this.coords.p_to_real, this.options.to_min, this.options.to_max);
++ this.coords.p_to_real = this.checkMinInterval(this.coords.p_to_real, this.coords.p_from_real, "to");
++ this.coords.p_to_real = this.checkMaxInterval(this.coords.p_to_real, this.coords.p_from_real, "to");
++
++ this.coords.p_to_fake = this.convertToFakePercent(this.coords.p_to_real);
++
++ break;
++
++ case "both":
++ if (this.options.from_fixed || this.options.to_fixed) {
++ break;
++ }
++
++ handle_x = this.toFixed(handle_x + (this.coords.p_handle * 0.1));
++
++ this.coords.p_from_real = this.convertToRealPercent(handle_x) - this.coords.p_gap_left;
++ this.coords.p_from_real = this.calcWithStep(this.coords.p_from_real);
++ this.coords.p_from_real = this.checkDiapason(this.coords.p_from_real, this.options.from_min, this.options.from_max);
++ this.coords.p_from_real = this.checkMinInterval(this.coords.p_from_real, this.coords.p_to_real, "from");
++ this.coords.p_from_fake = this.convertToFakePercent(this.coords.p_from_real);
++
++ this.coords.p_to_real = this.convertToRealPercent(handle_x) + this.coords.p_gap_right;
++ this.coords.p_to_real = this.calcWithStep(this.coords.p_to_real);
++ this.coords.p_to_real = this.checkDiapason(this.coords.p_to_real, this.options.to_min, this.options.to_max);
++ this.coords.p_to_real = this.checkMinInterval(this.coords.p_to_real, this.coords.p_from_real, "to");
++ this.coords.p_to_fake = this.convertToFakePercent(this.coords.p_to_real);
++
++ break;
++
++ case "both_one":
++ if (this.options.from_fixed || this.options.to_fixed) {
++ break;
++ }
++
++ var real_x = this.convertToRealPercent(handle_x),
++ from = this.result.from_percent,
++ to = this.result.to_percent,
++ full = to - from,
++ half = full / 2,
++ new_from = real_x - half,
++ new_to = real_x + half;
++
++ if (new_from < 0) {
++ new_from = 0;
++ new_to = new_from + full;
++ }
++
++ if (new_to > 100) {
++ new_to = 100;
++ new_from = new_to - full;
++ }
++
++ this.coords.p_from_real = this.calcWithStep(new_from);
++ this.coords.p_from_real = this.checkDiapason(this.coords.p_from_real, this.options.from_min, this.options.from_max);
++ this.coords.p_from_fake = this.convertToFakePercent(this.coords.p_from_real);
++
++ this.coords.p_to_real = this.calcWithStep(new_to);
++ this.coords.p_to_real = this.checkDiapason(this.coords.p_to_real, this.options.to_min, this.options.to_max);
++ this.coords.p_to_fake = this.convertToFakePercent(this.coords.p_to_real);
++
++ break;
++ }
++
++ if (this.options.type === "single") {
++ this.coords.p_bar_x = (this.coords.p_handle / 2);
++ this.coords.p_bar_w = this.coords.p_single_fake;
++
++ this.result.from_percent = this.coords.p_single_real;
++ this.result.from = this.convertToValue(this.coords.p_single_real);
++
++ if (this.options.values.length) {
++ this.result.from_value = this.options.values[this.result.from];
++ }
++ } else {
++ this.coords.p_bar_x = this.toFixed(this.coords.p_from_fake + (this.coords.p_handle / 2));
++ this.coords.p_bar_w = this.toFixed(this.coords.p_to_fake - this.coords.p_from_fake);
++
++ this.result.from_percent = this.coords.p_from_real;
++ this.result.from = this.convertToValue(this.coords.p_from_real);
++ this.result.to_percent = this.coords.p_to_real;
++ this.result.to = this.convertToValue(this.coords.p_to_real);
++
++ if (this.options.values.length) {
++ this.result.from_value = this.options.values[this.result.from];
++ this.result.to_value = this.options.values[this.result.to];
++ }
++ }
++
++ this.calcMinMax();
++ this.calcLabels();
++ },
++
++
++ /**
++ * calculates pointer X in percent
++ */
++ calcPointerPercent: function () {
++ if (!this.coords.w_rs) {
++ this.coords.p_pointer = 0;
++ return;
++ }
++
++ if (this.coords.x_pointer < 0 || isNaN(this.coords.x_pointer) ) {
++ this.coords.x_pointer = 0;
++ } else if (this.coords.x_pointer > this.coords.w_rs) {
++ this.coords.x_pointer = this.coords.w_rs;
++ }
++
++ this.coords.p_pointer = this.toFixed(this.coords.x_pointer / this.coords.w_rs * 100);
++ },
++
++ convertToRealPercent: function (fake) {
++ var full = 100 - this.coords.p_handle;
++ return fake / full * 100;
++ },
++
++ convertToFakePercent: function (real) {
++ var full = 100 - this.coords.p_handle;
++ return real / 100 * full;
++ },
++
++ getHandleX: function () {
++ var max = 100 - this.coords.p_handle,
++ x = this.toFixed(this.coords.p_pointer - this.coords.p_gap);
++
++ if (x < 0) {
++ x = 0;
++ } else if (x > max) {
++ x = max;
++ }
++
++ return x;
++ },
++
++ calcHandlePercent: function () {
++ if (this.options.type === "single") {
++ this.coords.w_handle = this.$cache.s_single.outerWidth(false);
++ } else {
++ this.coords.w_handle = this.$cache.s_from.outerWidth(false);
++ }
++
++ this.coords.p_handle = this.toFixed(this.coords.w_handle / this.coords.w_rs * 100);
++ },
++
++ /**
++ * Find closest handle to pointer click
++ *
++ * @param real_x {Number}
++ * @returns {String}
++ */
++ chooseHandle: function (real_x) {
++ if (this.options.type === "single") {
++ return "single";
++ } else {
++ var m_point = this.coords.p_from_real + ((this.coords.p_to_real - this.coords.p_from_real) / 2);
++ if (real_x >= m_point) {
++ return this.options.to_fixed ? "from" : "to";
++ } else {
++ return this.options.from_fixed ? "to" : "from";
++ }
++ }
++ },
++
++ /**
++ * Measure Min and Max labels width in percent
++ */
++ calcMinMax: function () {
++ if (!this.coords.w_rs) {
++ return;
++ }
++
++ this.labels.p_min = this.labels.w_min / this.coords.w_rs * 100;
++ this.labels.p_max = this.labels.w_max / this.coords.w_rs * 100;
++ },
++
++ /**
++ * Measure labels width and X in percent
++ */
++ calcLabels: function () {
++ if (!this.coords.w_rs || this.options.hide_from_to) {
++ return;
++ }
++
++ if (this.options.type === "single") {
++
++ this.labels.w_single = this.$cache.single.outerWidth(false);
++ this.labels.p_single_fake = this.labels.w_single / this.coords.w_rs * 100;
++ this.labels.p_single_left = this.coords.p_single_fake + (this.coords.p_handle / 2) - (this.labels.p_single_fake / 2);
++ this.labels.p_single_left = this.checkEdges(this.labels.p_single_left, this.labels.p_single_fake);
++
++ } else {
++
++ this.labels.w_from = this.$cache.from.outerWidth(false);
++ this.labels.p_from_fake = this.labels.w_from / this.coords.w_rs * 100;
++ this.labels.p_from_left = this.coords.p_from_fake + (this.coords.p_handle / 2) - (this.labels.p_from_fake / 2);
++ this.labels.p_from_left = this.toFixed(this.labels.p_from_left);
++ this.labels.p_from_left = this.checkEdges(this.labels.p_from_left, this.labels.p_from_fake);
++
++ this.labels.w_to = this.$cache.to.outerWidth(false);
++ this.labels.p_to_fake = this.labels.w_to / this.coords.w_rs * 100;
++ this.labels.p_to_left = this.coords.p_to_fake + (this.coords.p_handle / 2) - (this.labels.p_to_fake / 2);
++ this.labels.p_to_left = this.toFixed(this.labels.p_to_left);
++ this.labels.p_to_left = this.checkEdges(this.labels.p_to_left, this.labels.p_to_fake);
++
++ this.labels.w_single = this.$cache.single.outerWidth(false);
++ this.labels.p_single_fake = this.labels.w_single / this.coords.w_rs * 100;
++ this.labels.p_single_left = ((this.labels.p_from_left + this.labels.p_to_left + this.labels.p_to_fake) / 2) - (this.labels.p_single_fake / 2);
++ this.labels.p_single_left = this.toFixed(this.labels.p_single_left);
++ this.labels.p_single_left = this.checkEdges(this.labels.p_single_left, this.labels.p_single_fake);
++
++ }
++ },
++
++
++
++ // =============================================================================================================
++ // Drawings
++
++ /**
++ * Main function called in request animation frame
++ * to update everything
++ */
++ updateScene: function () {
++ if (this.raf_id) {
++ cancelAnimationFrame(this.raf_id);
++ this.raf_id = null;
++ }
++
++ clearTimeout(this.update_tm);
++ this.update_tm = null;
++
++ if (!this.options) {
++ return;
++ }
++
++ this.drawHandles();
++
++ if (this.is_active) {
++ this.raf_id = requestAnimationFrame(this.updateScene.bind(this));
++ } else {
++ this.update_tm = setTimeout(this.updateScene.bind(this), 300);
++ }
++ },
++
++ /**
++ * Draw handles
++ */
++ drawHandles: function () {
++ this.coords.w_rs = this.$cache.rs.outerWidth(false);
++
++ if (!this.coords.w_rs) {
++ return;
++ }
++
++ if (this.coords.w_rs !== this.coords.w_rs_old) {
++ this.target = "base";
++ this.is_resize = true;
++ }
++
++ if (this.coords.w_rs !== this.coords.w_rs_old || this.force_redraw) {
++ this.setMinMax();
++ this.calc(true);
++ this.drawLabels();
++ if (this.options.grid) {
++ this.calcGridMargin();
++ this.calcGridLabels();
++ }
++ this.force_redraw = true;
++ this.coords.w_rs_old = this.coords.w_rs;
++ this.drawShadow();
++ }
++
++ if (!this.coords.w_rs) {
++ return;
++ }
++
++ if (!this.dragging && !this.force_redraw && !this.is_key) {
++ return;
++ }
++
++ if (this.old_from !== this.result.from || this.old_to !== this.result.to || this.force_redraw || this.is_key) {
++
++ this.drawLabels();
++
++ this.$cache.bar[0].style.left = this.coords.p_bar_x + "%";
++ this.$cache.bar[0].style.width = this.coords.p_bar_w + "%";
++
++ if (this.options.type === "single") {
++ this.$cache.s_single[0].style.left = this.coords.p_single_fake + "%";
++
++ this.$cache.single[0].style.left = this.labels.p_single_left + "%";
++
++ if (this.options.values.length) {
++ this.$cache.input.prop("value", this.result.from_value);
++ } else {
++ this.$cache.input.prop("value", this.result.from);
++ }
++ this.$cache.input.data("from", this.result.from);
++ } else {
++ this.$cache.s_from[0].style.left = this.coords.p_from_fake + "%";
++ this.$cache.s_to[0].style.left = this.coords.p_to_fake + "%";
++
++ if (this.old_from !== this.result.from || this.force_redraw) {
++ this.$cache.from[0].style.left = this.labels.p_from_left + "%";
++ }
++ if (this.old_to !== this.result.to || this.force_redraw) {
++ this.$cache.to[0].style.left = this.labels.p_to_left + "%";
++ }
++
++ this.$cache.single[0].style.left = this.labels.p_single_left + "%";
++
++ if (this.options.values.length) {
++ this.$cache.input.prop("value", this.result.from_value + this.options.input_values_separator + this.result.to_value);
++ } else {
++ this.$cache.input.prop("value", this.result.from + this.options.input_values_separator + this.result.to);
++ }
++ this.$cache.input.data("from", this.result.from);
++ this.$cache.input.data("to", this.result.to);
++ }
++
++ if ((this.old_from !== this.result.from || this.old_to !== this.result.to) && !this.is_start) {
++ this.$cache.input.trigger("change");
++ }
++
++ this.old_from = this.result.from;
++ this.old_to = this.result.to;
++
++ // callbacks call
++ if (!this.is_resize && !this.is_update && !this.is_start && !this.is_finish) {
++ this.callOnChange();
++ }
++ if (this.is_key || this.is_click) {
++ this.is_key = false;
++ this.is_click = false;
++ this.callOnFinish();
++ }
++
++ this.is_update = false;
++ this.is_resize = false;
++ this.is_finish = false;
++ }
++
++ this.is_start = false;
++ this.is_key = false;
++ this.is_click = false;
++ this.force_redraw = false;
++ },
++
++ /**
++ * Draw labels
++ * measure labels collisions
++ * collapse close labels
++ */
++ drawLabels: function () {
++ if (!this.options) {
++ return;
++ }
++
++ var values_num = this.options.values.length,
++ p_values = this.options.p_values,
++ text_single,
++ text_from,
++ text_to;
++
++ if (this.options.hide_from_to) {
++ return;
++ }
++
++ if (this.options.type === "single") {
++
++ if (values_num) {
++ text_single = this.decorate(p_values[this.result.from]);
++ this.$cache.single.html(text_single);
++ } else {
++ text_single = this.decorate(this._prettify(this.result.from), this.result.from);
++ this.$cache.single.html(text_single);
++ }
++
++ this.calcLabels();
++
++ if (this.labels.p_single_left < this.labels.p_min + 1) {
++ this.$cache.min[0].style.visibility = "hidden";
++ } else {
++ this.$cache.min[0].style.visibility = "visible";
++ }
++
++ if (this.labels.p_single_left + this.labels.p_single_fake > 100 - this.labels.p_max - 1) {
++ this.$cache.max[0].style.visibility = "hidden";
++ } else {
++ this.$cache.max[0].style.visibility = "visible";
++ }
++
++ } else {
++
++ if (values_num) {
++
++ if (this.options.decorate_both) {
++ text_single = this.decorate(p_values[this.result.from]);
++ text_single += this.options.values_separator;
++ text_single += this.decorate(p_values[this.result.to]);
++ } else {
++ text_single = this.decorate(p_values[this.result.from] + this.options.values_separator + p_values[this.result.to]);
++ }
++ text_from = this.decorate(p_values[this.result.from]);
++ text_to = this.decorate(p_values[this.result.to]);
++
++ this.$cache.single.html(text_single);
++ this.$cache.from.html(text_from);
++ this.$cache.to.html(text_to);
++
++ } else {
++
++ if (this.options.decorate_both) {
++ text_single = this.decorate(this._prettify(this.result.from), this.result.from);
++ text_single += this.options.values_separator;
++ text_single += this.decorate(this._prettify(this.result.to), this.result.to);
++ } else {
++ text_single = this.decorate(this._prettify(this.result.from) + this.options.values_separator + this._prettify(this.result.to), this.result.to);
++ }
++ text_from = this.decorate(this._prettify(this.result.from), this.result.from);
++ text_to = this.decorate(this._prettify(this.result.to), this.result.to);
++
++ this.$cache.single.html(text_single);
++ this.$cache.from.html(text_from);
++ this.$cache.to.html(text_to);
++
++ }
++
++ this.calcLabels();
++
++ var min = Math.min(this.labels.p_single_left, this.labels.p_from_left),
++ single_left = this.labels.p_single_left + this.labels.p_single_fake,
++ to_left = this.labels.p_to_left + this.labels.p_to_fake,
++ max = Math.max(single_left, to_left);
++
++ if (this.labels.p_from_left + this.labels.p_from_fake >= this.labels.p_to_left) {
++ this.$cache.from[0].style.visibility = "hidden";
++ this.$cache.to[0].style.visibility = "hidden";
++ this.$cache.single[0].style.visibility = "visible";
++
++ if (this.result.from === this.result.to) {
++ if (this.target === "from") {
++ this.$cache.from[0].style.visibility = "visible";
++ } else if (this.target === "to") {
++ this.$cache.to[0].style.visibility = "visible";
++ }
++ this.$cache.single[0].style.visibility = "hidden";
++ max = to_left;
++ } else {
++ this.$cache.from[0].style.visibility = "hidden";
++ this.$cache.to[0].style.visibility = "hidden";
++ this.$cache.single[0].style.visibility = "visible";
++ max = Math.max(single_left, to_left);
++ }
++ } else {
++ this.$cache.from[0].style.visibility = "visible";
++ this.$cache.to[0].style.visibility = "visible";
++ this.$cache.single[0].style.visibility = "hidden";
++ }
++
++ if (min < this.labels.p_min + 1) {
++ this.$cache.min[0].style.visibility = "hidden";
++ } else {
++ this.$cache.min[0].style.visibility = "visible";
++ }
++
++ if (max > 100 - this.labels.p_max - 1) {
++ this.$cache.max[0].style.visibility = "hidden";
++ } else {
++ this.$cache.max[0].style.visibility = "visible";
++ }
++
++ }
++ },
++
++ /**
++ * Draw shadow intervals
++ */
++ drawShadow: function () {
++ var o = this.options,
++ c = this.$cache,
++
++ is_from_min = typeof o.from_min === "number" && !isNaN(o.from_min),
++ is_from_max = typeof o.from_max === "number" && !isNaN(o.from_max),
++ is_to_min = typeof o.to_min === "number" && !isNaN(o.to_min),
++ is_to_max = typeof o.to_max === "number" && !isNaN(o.to_max),
++
++ from_min,
++ from_max,
++ to_min,
++ to_max;
++
++ if (o.type === "single") {
++ if (o.from_shadow && (is_from_min || is_from_max)) {
++ from_min = this.convertToPercent(is_from_min ? o.from_min : o.min);
++ from_max = this.convertToPercent(is_from_max ? o.from_max : o.max) - from_min;
++ from_min = this.toFixed(from_min - (this.coords.p_handle / 100 * from_min));
++ from_max = this.toFixed(from_max - (this.coords.p_handle / 100 * from_max));
++ from_min = from_min + (this.coords.p_handle / 2);
++
++ c.shad_single[0].style.display = "block";
++ c.shad_single[0].style.left = from_min + "%";
++ c.shad_single[0].style.width = from_max + "%";
++ } else {
++ c.shad_single[0].style.display = "none";
++ }
++ } else {
++ if (o.from_shadow && (is_from_min || is_from_max)) {
++ from_min = this.convertToPercent(is_from_min ? o.from_min : o.min);
++ from_max = this.convertToPercent(is_from_max ? o.from_max : o.max) - from_min;
++ from_min = this.toFixed(from_min - (this.coords.p_handle / 100 * from_min));
++ from_max = this.toFixed(from_max - (this.coords.p_handle / 100 * from_max));
++ from_min = from_min + (this.coords.p_handle / 2);
++
++ c.shad_from[0].style.display = "block";
++ c.shad_from[0].style.left = from_min + "%";
++ c.shad_from[0].style.width = from_max + "%";
++ } else {
++ c.shad_from[0].style.display = "none";
++ }
++
++ if (o.to_shadow && (is_to_min || is_to_max)) {
++ to_min = this.convertToPercent(is_to_min ? o.to_min : o.min);
++ to_max = this.convertToPercent(is_to_max ? o.to_max : o.max) - to_min;
++ to_min = this.toFixed(to_min - (this.coords.p_handle / 100 * to_min));
++ to_max = this.toFixed(to_max - (this.coords.p_handle / 100 * to_max));
++ to_min = to_min + (this.coords.p_handle / 2);
++
++ c.shad_to[0].style.display = "block";
++ c.shad_to[0].style.left = to_min + "%";
++ c.shad_to[0].style.width = to_max + "%";
++ } else {
++ c.shad_to[0].style.display = "none";
++ }
++ }
++ },
++
++
++
++ // =============================================================================================================
++ // Callbacks
++
++ callOnStart: function () {
++ if (this.options.onStart && typeof this.options.onStart === "function") {
++ this.options.onStart(this.result);
++ }
++ },
++ callOnChange: function () {
++ if (this.options.onChange && typeof this.options.onChange === "function") {
++ this.options.onChange(this.result);
++ }
++ },
++ callOnFinish: function () {
++ if (this.options.onFinish && typeof this.options.onFinish === "function") {
++ this.options.onFinish(this.result);
++ }
++ },
++ callOnUpdate: function () {
++ if (this.options.onUpdate && typeof this.options.onUpdate === "function") {
++ this.options.onUpdate(this.result);
++ }
++ },
++
++
++
++ // =============================================================================================================
++ // Service methods
++
++ toggleInput: function () {
++ this.$cache.input.toggleClass("irs-hidden-input");
++ },
++
++ /**
++ * Convert real value to percent
++ *
++ * @param value {Number} X in real
++ * @param no_min {boolean=} don't use min value
++ * @returns {Number} X in percent
++ */
++ convertToPercent: function (value, no_min) {
++ var diapason = this.options.max - this.options.min,
++ one_percent = diapason / 100,
++ val, percent;
++
++ if (!diapason) {
++ this.no_diapason = true;
++ return 0;
++ }
++
++ if (no_min) {
++ val = value;
++ } else {
++ val = value - this.options.min;
++ }
++
++ percent = val / one_percent;
++
++ return this.toFixed(percent);
++ },
++
++ /**
++ * Convert percent to real values
++ *
++ * @param percent {Number} X in percent
++ * @returns {Number} X in real
++ */
++ convertToValue: function (percent) {
++ var min = this.options.min,
++ max = this.options.max,
++ min_decimals = min.toString().split(".")[1],
++ max_decimals = max.toString().split(".")[1],
++ min_length, max_length,
++ avg_decimals = 0,
++ abs = 0;
++
++ if (percent === 0) {
++ return this.options.min;
++ }
++ if (percent === 100) {
++ return this.options.max;
++ }
++
++
++ if (min_decimals) {
++ min_length = min_decimals.length;
++ avg_decimals = min_length;
++ }
++ if (max_decimals) {
++ max_length = max_decimals.length;
++ avg_decimals = max_length;
++ }
++ if (min_length && max_length) {
++ avg_decimals = (min_length >= max_length) ? min_length : max_length;
++ }
++
++ if (min < 0) {
++ abs = Math.abs(min);
++ min = +(min + abs).toFixed(avg_decimals);
++ max = +(max + abs).toFixed(avg_decimals);
++ }
++
++ var number = ((max - min) / 100 * percent) + min,
++ string = this.options.step.toString().split(".")[1],
++ result;
++
++ if (string) {
++ number = +number.toFixed(string.length);
++ } else {
++ number = number / this.options.step;
++ number = number * this.options.step;
++
++ number = +number.toFixed(0);
++ }
++
++ if (abs) {
++ number -= abs;
++ }
++
++ if (string) {
++ result = +number.toFixed(string.length);
++ } else {
++ result = this.toFixed(number);
++ }
++
++ if (result < this.options.min) {
++ result = this.options.min;
++ } else if (result > this.options.max) {
++ result = this.options.max;
++ }
++
++ return result;
++ },
++
++ /**
++ * Round percent value with step
++ *
++ * @param percent {Number}
++ * @returns percent {Number} rounded
++ */
++ calcWithStep: function (percent) {
++ var rounded = Math.round(percent / this.coords.p_step) * this.coords.p_step;
++
++ if (rounded > 100) {
++ rounded = 100;
++ }
++ if (percent === 100) {
++ rounded = 100;
++ }
++
++ return this.toFixed(rounded);
++ },
++
++ checkMinInterval: function (p_current, p_next, type) {
++ var o = this.options,
++ current,
++ next;
++
++ if (!o.min_interval) {
++ return p_current;
++ }
++
++ current = this.convertToValue(p_current);
++ next = this.convertToValue(p_next);
++
++ if (type === "from") {
++
++ if (next - current < o.min_interval) {
++ current = next - o.min_interval;
++ }
++
++ } else {
++
++ if (current - next < o.min_interval) {
++ current = next + o.min_interval;
++ }
++
++ }
++
++ return this.convertToPercent(current);
++ },
++
++ checkMaxInterval: function (p_current, p_next, type) {
++ var o = this.options,
++ current,
++ next;
++
++ if (!o.max_interval) {
++ return p_current;
++ }
++
++ current = this.convertToValue(p_current);
++ next = this.convertToValue(p_next);
++
++ if (type === "from") {
++
++ if (next - current > o.max_interval) {
++ current = next - o.max_interval;
++ }
++
++ } else {
++
++ if (current - next > o.max_interval) {
++ current = next + o.max_interval;
++ }
++
++ }
++
++ return this.convertToPercent(current);
++ },
++
++ checkDiapason: function (p_num, min, max) {
++ var num = this.convertToValue(p_num),
++ o = this.options;
++
++ if (typeof min !== "number") {
++ min = o.min;
++ }
++
++ if (typeof max !== "number") {
++ max = o.max;
++ }
++
++ if (num < min) {
++ num = min;
++ }
++
++ if (num > max) {
++ num = max;
++ }
++
++ return this.convertToPercent(num);
++ },
++
++ toFixed: function (num) {
++ num = num.toFixed(9);
++ return +num;
++ },
++
++ _prettify: function (num) {
++ if (!this.options.prettify_enabled) {
++ return num;
++ }
++
++ if (this.options.prettify && typeof this.options.prettify === "function") {
++ return this.options.prettify(num);
++ } else {
++ return this.prettify(num);
++ }
++ },
++
++ prettify: function (num) {
++ var n = num.toString();
++ return n.replace(/(\d{1,3}(?=(?:\d\d\d)+(?!\d)))/g, "$1" + this.options.prettify_separator);
++ },
++
++ checkEdges: function (left, width) {
++ if (!this.options.force_edges) {
++ return this.toFixed(left);
++ }
++
++ if (left < 0) {
++ left = 0;
++ } else if (left > 100 - width) {
++ left = 100 - width;
++ }
++
++ return this.toFixed(left);
++ },
++
++ validate: function () {
++ var o = this.options,
++ r = this.result,
++ v = o.values,
++ vl = v.length,
++ value,
++ i;
++
++ if (typeof o.min === "string") o.min = +o.min;
++ if (typeof o.max === "string") o.max = +o.max;
++ if (typeof o.from === "string") o.from = +o.from;
++ if (typeof o.to === "string") o.to = +o.to;
++ if (typeof o.step === "string") o.step = +o.step;
++
++ if (typeof o.from_min === "string") o.from_min = +o.from_min;
++ if (typeof o.from_max === "string") o.from_max = +o.from_max;
++ if (typeof o.to_min === "string") o.to_min = +o.to_min;
++ if (typeof o.to_max === "string") o.to_max = +o.to_max;
++
++ if (typeof o.keyboard_step === "string") o.keyboard_step = +o.keyboard_step;
++ if (typeof o.grid_num === "string") o.grid_num = +o.grid_num;
++
++ if (o.max < o.min) {
++ o.max = o.min;
++ }
++
++ if (vl) {
++ o.p_values = [];
++ o.min = 0;
++ o.max = vl - 1;
++ o.step = 1;
++ o.grid_num = o.max;
++ o.grid_snap = true;
++
++
++ for (i = 0; i < vl; i++) {
++ value = +v[i];
++
++ if (!isNaN(value)) {
++ v[i] = value;
++ value = this._prettify(value);
++ } else {
++ value = v[i];
++ }
++
++ o.p_values.push(value);
++ }
++ }
++
++ if (typeof o.from !== "number" || isNaN(o.from)) {
++ o.from = o.min;
++ }
++
++ if (typeof o.to !== "number" || isNaN(o.from)) {
++ o.to = o.max;
++ }
++
++ if (o.type === "single") {
++
++ if (o.from < o.min) {
++ o.from = o.min;
++ }
++
++ if (o.from > o.max) {
++ o.from = o.max;
++ }
++
++ } else {
++
++ if (o.from < o.min || o.from > o.max) {
++ o.from = o.min;
++ }
++ if (o.to > o.max || o.to < o.min) {
++ o.to = o.max;
++ }
++ if (o.from > o.to) {
++ o.from = o.to;
++ }
++
++ }
++
++ if (typeof o.step !== "number" || isNaN(o.step) || !o.step || o.step < 0) {
++ o.step = 1;
++ }
++
++ if (typeof o.keyboard_step !== "number" || isNaN(o.keyboard_step) || !o.keyboard_step || o.keyboard_step < 0) {
++ o.keyboard_step = 5;
++ }
++
++ if (typeof o.from_min === "number" && o.from < o.from_min) {
++ o.from = o.from_min;
++ }
++
++ if (typeof o.from_max === "number" && o.from > o.from_max) {
++ o.from = o.from_max;
++ }
++
++ if (typeof o.to_min === "number" && o.to < o.to_min) {
++ o.to = o.to_min;
++ }
++
++ if (typeof o.to_max === "number" && o.from > o.to_max) {
++ o.to = o.to_max;
++ }
++
++ if (r) {
++ if (r.min !== o.min) {
++ r.min = o.min;
++ }
++
++ if (r.max !== o.max) {
++ r.max = o.max;
++ }
++
++ if (r.from < r.min || r.from > r.max) {
++ r.from = o.from;
++ }
++
++ if (r.to < r.min || r.to > r.max) {
++ r.to = o.to;
++ }
++ }
++
++ if (typeof o.min_interval !== "number" || isNaN(o.min_interval) || !o.min_interval || o.min_interval < 0) {
++ o.min_interval = 0;
++ }
++
++ if (typeof o.max_interval !== "number" || isNaN(o.max_interval) || !o.max_interval || o.max_interval < 0) {
++ o.max_interval = 0;
++ }
++
++ if (o.min_interval && o.min_interval > o.max - o.min) {
++ o.min_interval = o.max - o.min;
++ }
++
++ if (o.max_interval && o.max_interval > o.max - o.min) {
++ o.max_interval = o.max - o.min;
++ }
++ },
++
++ decorate: function (num, original) {
++ var decorated = "",
++ o = this.options;
++
++ if (o.prefix) {
++ decorated += o.prefix;
++ }
++
++ decorated += num;
++
++ if (o.max_postfix) {
++ if (o.values.length && num === o.p_values[o.max]) {
++ decorated += o.max_postfix;
++ if (o.postfix) {
++ decorated += " ";
++ }
++ } else if (original === o.max) {
++ decorated += o.max_postfix;
++ if (o.postfix) {
++ decorated += " ";
++ }
++ }
++ }
++
++ if (o.postfix) {
++ decorated += o.postfix;
++ }
++
++ return decorated;
++ },
++
++ updateFrom: function () {
++ this.result.from = this.options.from;
++ this.result.from_percent = this.convertToPercent(this.result.from);
++ if (this.options.values) {
++ this.result.from_value = this.options.values[this.result.from];
++ }
++ },
++
++ updateTo: function () {
++ this.result.to = this.options.to;
++ this.result.to_percent = this.convertToPercent(this.result.to);
++ if (this.options.values) {
++ this.result.to_value = this.options.values[this.result.to];
++ }
++ },
++
++ updateResult: function () {
++ this.result.min = this.options.min;
++ this.result.max = this.options.max;
++ this.updateFrom();
++ this.updateTo();
++ },
++
++
++ // =============================================================================================================
++ // Grid
++
++ appendGrid: function () {
++ if (!this.options.grid) {
++ return;
++ }
++
++ var o = this.options,
++ i, z,
++
++ total = o.max - o.min,
++ big_num = o.grid_num,
++ big_p = 0,
++ big_w = 0,
++
++ small_max = 4,
++ local_small_max,
++ small_p,
++ small_w = 0,
++
++ result,
++ html = '';
++
++
++
++ this.calcGridMargin();
++
++ if (o.grid_snap) {
++ big_num = total / o.step;
++ big_p = this.toFixed(o.step / (total / 100));
++ } else {
++ big_p = this.toFixed(100 / big_num);
++ }
++
++ if (big_num > 4) {
++ small_max = 3;
++ }
++ if (big_num > 7) {
++ small_max = 2;
++ }
++ if (big_num > 14) {
++ small_max = 1;
++ }
++ if (big_num > 28) {
++ small_max = 0;
++ }
++
++ for (i = 0; i < big_num + 1; i++) {
++ local_small_max = small_max;
++
++ big_w = this.toFixed(big_p * i);
++
++ if (big_w > 100) {
++ big_w = 100;
++
++ local_small_max -= 2;
++ if (local_small_max < 0) {
++ local_small_max = 0;
++ }
++ }
++ this.coords.big[i] = big_w;
++
++ small_p = (big_w - (big_p * (i - 1))) / (local_small_max + 1);
++
++ for (z = 1; z <= local_small_max; z++) {
++ if (big_w === 0) {
++ break;
++ }
++
++ small_w = this.toFixed(big_w - (small_p * z));
++
++ html += '<span class="irs-grid-pol small" style="left: ' + small_w + '%"></span>';
++ }
++
++ html += '<span class="irs-grid-pol" style="left: ' + big_w + '%"></span>';
++
++ result = this.convertToValue(big_w);
++ if (o.values.length) {
++ result = o.p_values[result];
++ } else {
++ result = this._prettify(result);
++ }
++
++ html += '<span class="irs-grid-text js-grid-text-' + i + '" style="left: ' + big_w + '%">' + result + '</span>';
++ }
++ this.coords.big_num = Math.ceil(big_num + 1);
++
++
++
++ this.$cache.cont.addClass("irs-with-grid");
++ this.$cache.grid.html(html);
++ this.cacheGridLabels();
++ },
++
++ cacheGridLabels: function () {
++ var $label, i,
++ num = this.coords.big_num;
++
++ for (i = 0; i < num; i++) {
++ $label = this.$cache.grid.find(".js-grid-text-" + i);
++ this.$cache.grid_labels.push($label);
++ }
++
++ this.calcGridLabels();
++ },
++
++ calcGridLabels: function () {
++ var i, label, start = [], finish = [],
++ num = this.coords.big_num;
++
++ for (i = 0; i < num; i++) {
++ this.coords.big_w[i] = this.$cache.grid_labels[i].outerWidth(false);
++ this.coords.big_p[i] = this.toFixed(this.coords.big_w[i] / this.coords.w_rs * 100);
++ this.coords.big_x[i] = this.toFixed(this.coords.big_p[i] / 2);
++
++ start[i] = this.toFixed(this.coords.big[i] - this.coords.big_x[i]);
++ finish[i] = this.toFixed(start[i] + this.coords.big_p[i]);
++ }
++
++ if (this.options.force_edges) {
++ if (start[0] < -this.coords.grid_gap) {
++ start[0] = -this.coords.grid_gap;
++ finish[0] = this.toFixed(start[0] + this.coords.big_p[0]);
++
++ this.coords.big_x[0] = this.coords.grid_gap;
++ }
++
++ if (finish[num - 1] > 100 + this.coords.grid_gap) {
++ finish[num - 1] = 100 + this.coords.grid_gap;
++ start[num - 1] = this.toFixed(finish[num - 1] - this.coords.big_p[num - 1]);
++
++ this.coords.big_x[num - 1] = this.toFixed(this.coords.big_p[num - 1] - this.coords.grid_gap);
++ }
++ }
++
++ this.calcGridCollision(2, start, finish);
++ this.calcGridCollision(4, start, finish);
++
++ for (i = 0; i < num; i++) {
++ label = this.$cache.grid_labels[i][0];
++ label.style.marginLeft = -this.coords.big_x[i] + "%";
++ }
++ },
++
++ // Collisions Calc Beta
++ // TODO: Refactor then have plenty of time
++ calcGridCollision: function (step, start, finish) {
++ var i, next_i, label,
++ num = this.coords.big_num;
++
++ for (i = 0; i < num; i += step) {
++ next_i = i + (step / 2);
++ if (next_i >= num) {
++ break;
++ }
++
++ label = this.$cache.grid_labels[next_i][0];
++
++ if (finish[i] <= start[next_i]) {
++ label.style.visibility = "visible";
++ } else {
++ label.style.visibility = "hidden";
++ }
++ }
++ },
++
++ calcGridMargin: function () {
++ if (!this.options.grid_margin) {
++ return;
++ }
++
++ this.coords.w_rs = this.$cache.rs.outerWidth(false);
++ if (!this.coords.w_rs) {
++ return;
++ }
++
++ if (this.options.type === "single") {
++ this.coords.w_handle = this.$cache.s_single.outerWidth(false);
++ } else {
++ this.coords.w_handle = this.$cache.s_from.outerWidth(false);
++ }
++ this.coords.p_handle = this.toFixed(this.coords.w_handle / this.coords.w_rs * 100);
++ this.coords.grid_gap = this.toFixed((this.coords.p_handle / 2) - 0.1);
++
++ this.$cache.grid[0].style.width = this.toFixed(100 - this.coords.p_handle) + "%";
++ this.$cache.grid[0].style.left = this.coords.grid_gap + "%";
++ },
++
++
++
++ // =============================================================================================================
++ // Public methods
++
++ update: function (options) {
++ if (!this.input) {
++ return;
++ }
++
++ this.is_update = true;
++
++ this.options.from = this.result.from;
++ this.options.to = this.result.to;
++
++ this.options = $.extend(this.options, options);
++ this.validate();
++ this.updateResult(options);
++
++ this.toggleInput();
++ this.remove();
++ this.init(true);
++ },
++
++ reset: function () {
++ if (!this.input) {
++ return;
++ }
++
++ this.updateResult();
++ this.update();
++ },
++
++ destroy: function () {
++ if (!this.input) {
++ return;
++ }
++
++ this.toggleInput();
++ this.$cache.input.prop("readonly", false);
++ $.data(this.input, "ionRangeSlider", null);
++
++ this.remove();
++ this.input = null;
++ this.options = null;
++ }
++ };
++
++ $.fn.ionRangeSlider = function (options) {
++ return this.each(function() {
++ if (!$.data(this, "ionRangeSlider")) {
++ $.data(this, "ionRangeSlider", new IonRangeSlider(this, options, plugin_count++));
++ }
++ });
++ };
++
++
++
++ // =================================================================================================================
++ // http://paulirish.com/2011/requestanimationframe-for-smart-animating/
++ // http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
++
++ // requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel
++
++ // MIT license
++
++ (function() {
++ var lastTime = 0;
++ var vendors = ['ms', 'moz', 'webkit', 'o'];
++ for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
++ window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
++ window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame']
++ || window[vendors[x]+'CancelRequestAnimationFrame'];
++ }
++
++ if (!window.requestAnimationFrame)
++ window.requestAnimationFrame = function(callback, element) {
++ var currTime = new Date().getTime();
++ var timeToCall = Math.max(0, 16 - (currTime - lastTime));
++ var id = window.setTimeout(function() { callback(currTime + timeToCall); },
++ timeToCall);
++ lastTime = currTime + timeToCall;
++ return id;
++ };
++
++ if (!window.cancelAnimationFrame)
++ window.cancelAnimationFrame = function(id) {
++ clearTimeout(id);
++ };
++ }());
++
++} (jQuery, document, window, navigator));
--- /dev/null
--- /dev/null
++/*!
++ * jQuery JavaScript Library v2.2.3
++ * http://jquery.com/
++ *
++ * Includes Sizzle.js
++ * http://sizzlejs.com/
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license
++ * http://jquery.org/license
++ *
++ * Date: 2016-04-05T19:26Z
++ */
++
++(function( global, factory ) {
++
++ if ( typeof module === "object" && typeof module.exports === "object" ) {
++ // For CommonJS and CommonJS-like environments where a proper `window`
++ // is present, execute the factory and get jQuery.
++ // For environments that do not have a `window` with a `document`
++ // (such as Node.js), expose a factory as module.exports.
++ // This accentuates the need for the creation of a real `window`.
++ // e.g. var jQuery = require("jquery")(window);
++ // See ticket #14549 for more info.
++ module.exports = global.document ?
++ factory( global, true ) :
++ function( w ) {
++ if ( !w.document ) {
++ throw new Error( "jQuery requires a window with a document" );
++ }
++ return factory( w );
++ };
++ } else {
++ factory( global );
++ }
++
++// Pass this if window is not defined yet
++}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
++
++// Support: Firefox 18+
++// Can't be in strict mode, several libs including ASP.NET trace
++// the stack via arguments.caller.callee and Firefox dies if
++// you try to trace through "use strict" call chains. (#13335)
++//"use strict";
++var arr = [];
++
++var document = window.document;
++
++var slice = arr.slice;
++
++var concat = arr.concat;
++
++var push = arr.push;
++
++var indexOf = arr.indexOf;
++
++var class2type = {};
++
++var toString = class2type.toString;
++
++var hasOwn = class2type.hasOwnProperty;
++
++var support = {};
++
++
++
++var
++ version = "2.2.3",
++
++ // Define a local copy of jQuery
++ jQuery = function( selector, context ) {
++
++ // The jQuery object is actually just the init constructor 'enhanced'
++ // Need init if jQuery is called (just allow error to be thrown if not included)
++ return new jQuery.fn.init( selector, context );
++ },
++
++ // Support: Android<4.1
++ // Make sure we trim BOM and NBSP
++ rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
++
++ // Matches dashed string for camelizing
++ rmsPrefix = /^-ms-/,
++ rdashAlpha = /-([\da-z])/gi,
++
++ // Used by jQuery.camelCase as callback to replace()
++ fcamelCase = function( all, letter ) {
++ return letter.toUpperCase();
++ };
++
++jQuery.fn = jQuery.prototype = {
++
++ // The current version of jQuery being used
++ jquery: version,
++
++ constructor: jQuery,
++
++ // Start with an empty selector
++ selector: "",
++
++ // The default length of a jQuery object is 0
++ length: 0,
++
++ toArray: function() {
++ return slice.call( this );
++ },
++
++ // Get the Nth element in the matched element set OR
++ // Get the whole matched element set as a clean array
++ get: function( num ) {
++ return num != null ?
++
++ // Return just the one element from the set
++ ( num < 0 ? this[ num + this.length ] : this[ num ] ) :
++
++ // Return all the elements in a clean array
++ slice.call( this );
++ },
++
++ // Take an array of elements and push it onto the stack
++ // (returning the new matched element set)
++ pushStack: function( elems ) {
++
++ // Build a new jQuery matched element set
++ var ret = jQuery.merge( this.constructor(), elems );
++
++ // Add the old object onto the stack (as a reference)
++ ret.prevObject = this;
++ ret.context = this.context;
++
++ // Return the newly-formed element set
++ return ret;
++ },
++
++ // Execute a callback for every element in the matched set.
++ each: function( callback ) {
++ return jQuery.each( this, callback );
++ },
++
++ map: function( callback ) {
++ return this.pushStack( jQuery.map( this, function( elem, i ) {
++ return callback.call( elem, i, elem );
++ } ) );
++ },
++
++ slice: function() {
++ return this.pushStack( slice.apply( this, arguments ) );
++ },
++
++ first: function() {
++ return this.eq( 0 );
++ },
++
++ last: function() {
++ return this.eq( -1 );
++ },
++
++ eq: function( i ) {
++ var len = this.length,
++ j = +i + ( i < 0 ? len : 0 );
++ return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] );
++ },
++
++ end: function() {
++ return this.prevObject || this.constructor();
++ },
++
++ // For internal use only.
++ // Behaves like an Array's method, not like a jQuery method.
++ push: push,
++ sort: arr.sort,
++ splice: arr.splice
++};
++
++jQuery.extend = jQuery.fn.extend = function() {
++ var options, name, src, copy, copyIsArray, clone,
++ target = arguments[ 0 ] || {},
++ i = 1,
++ length = arguments.length,
++ deep = false;
++
++ // Handle a deep copy situation
++ if ( typeof target === "boolean" ) {
++ deep = target;
++
++ // Skip the boolean and the target
++ target = arguments[ i ] || {};
++ i++;
++ }
++
++ // Handle case when target is a string or something (possible in deep copy)
++ if ( typeof target !== "object" && !jQuery.isFunction( target ) ) {
++ target = {};
++ }
++
++ // Extend jQuery itself if only one argument is passed
++ if ( i === length ) {
++ target = this;
++ i--;
++ }
++
++ for ( ; i < length; i++ ) {
++
++ // Only deal with non-null/undefined values
++ if ( ( options = arguments[ i ] ) != null ) {
++
++ // Extend the base object
++ for ( name in options ) {
++ src = target[ name ];
++ copy = options[ name ];
++
++ // Prevent never-ending loop
++ if ( target === copy ) {
++ continue;
++ }
++
++ // Recurse if we're merging plain objects or arrays
++ if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
++ ( copyIsArray = jQuery.isArray( copy ) ) ) ) {
++
++ if ( copyIsArray ) {
++ copyIsArray = false;
++ clone = src && jQuery.isArray( src ) ? src : [];
++
++ } else {
++ clone = src && jQuery.isPlainObject( src ) ? src : {};
++ }
++
++ // Never move original objects, clone them
++ target[ name ] = jQuery.extend( deep, clone, copy );
++
++ // Don't bring in undefined values
++ } else if ( copy !== undefined ) {
++ target[ name ] = copy;
++ }
++ }
++ }
++ }
++
++ // Return the modified object
++ return target;
++};
++
++jQuery.extend( {
++
++ // Unique for each copy of jQuery on the page
++ expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),
++
++ // Assume jQuery is ready without the ready module
++ isReady: true,
++
++ error: function( msg ) {
++ throw new Error( msg );
++ },
++
++ noop: function() {},
++
++ isFunction: function( obj ) {
++ return jQuery.type( obj ) === "function";
++ },
++
++ isArray: Array.isArray,
++
++ isWindow: function( obj ) {
++ return obj != null && obj === obj.window;
++ },
++
++ isNumeric: function( obj ) {
++
++ // parseFloat NaNs numeric-cast false positives (null|true|false|"")
++ // ...but misinterprets leading-number strings, particularly hex literals ("0x...")
++ // subtraction forces infinities to NaN
++ // adding 1 corrects loss of precision from parseFloat (#15100)
++ var realStringObj = obj && obj.toString();
++ return !jQuery.isArray( obj ) && ( realStringObj - parseFloat( realStringObj ) + 1 ) >= 0;
++ },
++
++ isPlainObject: function( obj ) {
++ var key;
++
++ // Not plain objects:
++ // - Any object or value whose internal [[Class]] property is not "[object Object]"
++ // - DOM nodes
++ // - window
++ if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
++ return false;
++ }
++
++ // Not own constructor property must be Object
++ if ( obj.constructor &&
++ !hasOwn.call( obj, "constructor" ) &&
++ !hasOwn.call( obj.constructor.prototype || {}, "isPrototypeOf" ) ) {
++ return false;
++ }
++
++ // Own properties are enumerated firstly, so to speed up,
++ // if last one is own, then all properties are own
++ for ( key in obj ) {}
++
++ return key === undefined || hasOwn.call( obj, key );
++ },
++
++ isEmptyObject: function( obj ) {
++ var name;
++ for ( name in obj ) {
++ return false;
++ }
++ return true;
++ },
++
++ type: function( obj ) {
++ if ( obj == null ) {
++ return obj + "";
++ }
++
++ // Support: Android<4.0, iOS<6 (functionish RegExp)
++ return typeof obj === "object" || typeof obj === "function" ?
++ class2type[ toString.call( obj ) ] || "object" :
++ typeof obj;
++ },
++
++ // Evaluates a script in a global context
++ globalEval: function( code ) {
++ var script,
++ indirect = eval;
++
++ code = jQuery.trim( code );
++
++ if ( code ) {
++
++ // If the code includes a valid, prologue position
++ // strict mode pragma, execute code by injecting a
++ // script tag into the document.
++ if ( code.indexOf( "use strict" ) === 1 ) {
++ script = document.createElement( "script" );
++ script.text = code;
++ document.head.appendChild( script ).parentNode.removeChild( script );
++ } else {
++
++ // Otherwise, avoid the DOM node creation, insertion
++ // and removal by using an indirect global eval
++
++ indirect( code );
++ }
++ }
++ },
++
++ // Convert dashed to camelCase; used by the css and data modules
++ // Support: IE9-11+
++ // Microsoft forgot to hump their vendor prefix (#9572)
++ camelCase: function( string ) {
++ return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
++ },
++
++ nodeName: function( elem, name ) {
++ return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
++ },
++
++ each: function( obj, callback ) {
++ var length, i = 0;
++
++ if ( isArrayLike( obj ) ) {
++ length = obj.length;
++ for ( ; i < length; i++ ) {
++ if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
++ break;
++ }
++ }
++ } else {
++ for ( i in obj ) {
++ if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
++ break;
++ }
++ }
++ }
++
++ return obj;
++ },
++
++ // Support: Android<4.1
++ trim: function( text ) {
++ return text == null ?
++ "" :
++ ( text + "" ).replace( rtrim, "" );
++ },
++
++ // results is for internal usage only
++ makeArray: function( arr, results ) {
++ var ret = results || [];
++
++ if ( arr != null ) {
++ if ( isArrayLike( Object( arr ) ) ) {
++ jQuery.merge( ret,
++ typeof arr === "string" ?
++ [ arr ] : arr
++ );
++ } else {
++ push.call( ret, arr );
++ }
++ }
++
++ return ret;
++ },
++
++ inArray: function( elem, arr, i ) {
++ return arr == null ? -1 : indexOf.call( arr, elem, i );
++ },
++
++ merge: function( first, second ) {
++ var len = +second.length,
++ j = 0,
++ i = first.length;
++
++ for ( ; j < len; j++ ) {
++ first[ i++ ] = second[ j ];
++ }
++
++ first.length = i;
++
++ return first;
++ },
++
++ grep: function( elems, callback, invert ) {
++ var callbackInverse,
++ matches = [],
++ i = 0,
++ length = elems.length,
++ callbackExpect = !invert;
++
++ // Go through the array, only saving the items
++ // that pass the validator function
++ for ( ; i < length; i++ ) {
++ callbackInverse = !callback( elems[ i ], i );
++ if ( callbackInverse !== callbackExpect ) {
++ matches.push( elems[ i ] );
++ }
++ }
++
++ return matches;
++ },
++
++ // arg is for internal usage only
++ map: function( elems, callback, arg ) {
++ var length, value,
++ i = 0,
++ ret = [];
++
++ // Go through the array, translating each of the items to their new values
++ if ( isArrayLike( elems ) ) {
++ length = elems.length;
++ for ( ; i < length; i++ ) {
++ value = callback( elems[ i ], i, arg );
++
++ if ( value != null ) {
++ ret.push( value );
++ }
++ }
++
++ // Go through every key on the object,
++ } else {
++ for ( i in elems ) {
++ value = callback( elems[ i ], i, arg );
++
++ if ( value != null ) {
++ ret.push( value );
++ }
++ }
++ }
++
++ // Flatten any nested arrays
++ return concat.apply( [], ret );
++ },
++
++ // A global GUID counter for objects
++ guid: 1,
++
++ // Bind a function to a context, optionally partially applying any
++ // arguments.
++ proxy: function( fn, context ) {
++ var tmp, args, proxy;
++
++ if ( typeof context === "string" ) {
++ tmp = fn[ context ];
++ context = fn;
++ fn = tmp;
++ }
++
++ // Quick check to determine if target is callable, in the spec
++ // this throws a TypeError, but we will just return undefined.
++ if ( !jQuery.isFunction( fn ) ) {
++ return undefined;
++ }
++
++ // Simulated bind
++ args = slice.call( arguments, 2 );
++ proxy = function() {
++ return fn.apply( context || this, args.concat( slice.call( arguments ) ) );
++ };
++
++ // Set the guid of unique handler to the same of original handler, so it can be removed
++ proxy.guid = fn.guid = fn.guid || jQuery.guid++;
++
++ return proxy;
++ },
++
++ now: Date.now,
++
++ // jQuery.support is not used in Core but other projects attach their
++ // properties to it so it needs to exist.
++ support: support
++} );
++
++// JSHint would error on this code due to the Symbol not being defined in ES5.
++// Defining this global in .jshintrc would create a danger of using the global
++// unguarded in another place, it seems safer to just disable JSHint for these
++// three lines.
++/* jshint ignore: start */
++if ( typeof Symbol === "function" ) {
++ jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ];
++}
++/* jshint ignore: end */
++
++// Populate the class2type map
++jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ),
++function( i, name ) {
++ class2type[ "[object " + name + "]" ] = name.toLowerCase();
++} );
++
++function isArrayLike( obj ) {
++
++ // Support: iOS 8.2 (not reproducible in simulator)
++ // `in` check used to prevent JIT error (gh-2145)
++ // hasOwn isn't used here due to false negatives
++ // regarding Nodelist length in IE
++ var length = !!obj && "length" in obj && obj.length,
++ type = jQuery.type( obj );
++
++ if ( type === "function" || jQuery.isWindow( obj ) ) {
++ return false;
++ }
++
++ return type === "array" || length === 0 ||
++ typeof length === "number" && length > 0 && ( length - 1 ) in obj;
++}
++var Sizzle =
++/*!
++ * Sizzle CSS Selector Engine v2.2.1
++ * http://sizzlejs.com/
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license
++ * http://jquery.org/license
++ *
++ * Date: 2015-10-17
++ */
++(function( window ) {
++
++var i,
++ support,
++ Expr,
++ getText,
++ isXML,
++ tokenize,
++ compile,
++ select,
++ outermostContext,
++ sortInput,
++ hasDuplicate,
++
++ // Local document vars
++ setDocument,
++ document,
++ docElem,
++ documentIsHTML,
++ rbuggyQSA,
++ rbuggyMatches,
++ matches,
++ contains,
++
++ // Instance-specific data
++ expando = "sizzle" + 1 * new Date(),
++ preferredDoc = window.document,
++ dirruns = 0,
++ done = 0,
++ classCache = createCache(),
++ tokenCache = createCache(),
++ compilerCache = createCache(),
++ sortOrder = function( a, b ) {
++ if ( a === b ) {
++ hasDuplicate = true;
++ }
++ return 0;
++ },
++
++ // General-purpose constants
++ 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 as it's faster than native
++ // http://jsperf.com/thor-indexof-vs-for/5
++ indexOf = function( list, elem ) {
++ var i = 0,
++ len = list.length;
++ for ( ; i < len; i++ ) {
++ if ( list[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
++
++ // http://www.w3.org/TR/css3-selectors/#whitespace
++ whitespace = "[\\x20\\t\\r\\n\\f]",
++
++ // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
++ identifier = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
++
++ // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors
++ attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace +
++ // Operator (capture 2)
++ "*([*^$|!~]?=)" + whitespace +
++ // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
++ "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace +
++ "*\\]",
++
++ pseudos = ":(" + identifier + ")(?:\\((" +
++ // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
++ // 1. quoted (capture 3; capture 4 or capture 5)
++ "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
++ // 2. simple (capture 6)
++ "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
++ // 3. anything else (capture 2)
++ ".*" +
++ ")\\)|)",
++
++ // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
++ rwhitespace = new RegExp( whitespace + "+", "g" ),
++ rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
++
++ rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
++ rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),
++
++ rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ),
++
++ rpseudo = new RegExp( pseudos ),
++ ridentifier = new RegExp( "^" + identifier + "$" ),
++
++ matchExpr = {
++ "ID": new RegExp( "^#(" + identifier + ")" ),
++ "CLASS": new RegExp( "^\\.(" + identifier + ")" ),
++ "TAG": new RegExp( "^(" + identifier + "|[*])" ),
++ "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" )
++ },
++
++ rinputs = /^(?:input|select|textarea|button)$/i,
++ rheader = /^h\d$/i,
++
++ rnative = /^[^{]+\{\s*\[native \w/,
++
++ // Easily-parseable/retrievable ID or TAG or CLASS selectors
++ rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
++
++ rsibling = /[+~]/,
++ 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<24
++ // Workaround erroneous numeric interpretation of +"0x"
++ return high !== high || escapedWhitespace ?
++ escaped :
++ high < 0 ?
++ // BMP codepoint
++ String.fromCharCode( high + 0x10000 ) :
++ // Supplemental Plane codepoint (surrogate pair)
++ String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
++ },
++
++ // Used for iframes
++ // See setDocument()
++ // Removing the function wrapper causes a "Permission Denied"
++ // error in IE
++ unloadHandler = function() {
++ setDocument();
++ };
++
++// 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;
++ }
++ };
++}
++
++function Sizzle( selector, context, results, seed ) {
++ var m, i, elem, nid, nidselect, match, groups, newSelector,
++ newContext = context && context.ownerDocument,
++
++ // nodeType defaults to 9, since context defaults to document
++ nodeType = context ? context.nodeType : 9;
++
++ results = results || [];
++
++ // Return early from calls with invalid selector or context
++ if ( typeof selector !== "string" || !selector ||
++ nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) {
++
++ return results;
++ }
++
++ // Try to shortcut find operations (as opposed to filters) in HTML documents
++ if ( !seed ) {
++
++ if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
++ setDocument( context );
++ }
++ context = context || document;
++
++ if ( documentIsHTML ) {
++
++ // If the selector is sufficiently simple, try using a "get*By*" DOM method
++ // (excepting DocumentFragment context, where the methods don't exist)
++ if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) {
++
++ // ID selector
++ if ( (m = match[1]) ) {
++
++ // Document context
++ if ( nodeType === 9 ) {
++ if ( (elem = context.getElementById( m )) ) {
++
++ // Support: IE, Opera, Webkit
++ // TODO: identify versions
++ // getElementById can match elements by name instead of ID
++ if ( elem.id === m ) {
++ results.push( elem );
++ return results;
++ }
++ } else {
++ return results;
++ }
++
++ // Element context
++ } else {
++
++ // Support: IE, Opera, Webkit
++ // TODO: identify versions
++ // getElementById can match elements by name instead of ID
++ if ( newContext && (elem = newContext.getElementById( m )) &&
++ contains( context, elem ) &&
++ elem.id === m ) {
++
++ results.push( elem );
++ return results;
++ }
++ }
++
++ // Type selector
++ } else if ( match[2] ) {
++ push.apply( results, context.getElementsByTagName( selector ) );
++ return results;
++
++ // Class selector
++ } else if ( (m = match[3]) && support.getElementsByClassName &&
++ context.getElementsByClassName ) {
++
++ push.apply( results, context.getElementsByClassName( m ) );
++ return results;
++ }
++ }
++
++ // Take advantage of querySelectorAll
++ if ( support.qsa &&
++ !compilerCache[ selector + " " ] &&
++ (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
++
++ if ( nodeType !== 1 ) {
++ newContext = context;
++ newSelector = selector;
++
++ // qSA looks outside Element context, which is not what we want
++ // Thanks to Andrew Dupont for this workaround technique
++ // Support: IE <=8
++ // Exclude object elements
++ } else if ( context.nodeName.toLowerCase() !== "object" ) {
++
++ // Capture the context ID, setting it first if necessary
++ if ( (nid = context.getAttribute( "id" )) ) {
++ nid = nid.replace( rescape, "\\$&" );
++ } else {
++ context.setAttribute( "id", (nid = expando) );
++ }
++
++ // Prefix every selector in the list
++ groups = tokenize( selector );
++ i = groups.length;
++ nidselect = ridentifier.test( nid ) ? "#" + nid : "[id='" + nid + "']";
++ while ( i-- ) {
++ groups[i] = nidselect + " " + toSelector( groups[i] );
++ }
++ newSelector = groups.join( "," );
++
++ // Expand context for sibling selectors
++ newContext = rsibling.test( selector ) && testContext( context.parentNode ) ||
++ context;
++ }
++
++ if ( newSelector ) {
++ try {
++ push.apply( results,
++ newContext.querySelectorAll( newSelector )
++ );
++ return results;
++ } catch ( qsaError ) {
++ } finally {
++ if ( nid === expando ) {
++ 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 {
++ 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 = arr.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]);
++ }
++ }
++ });
++ });
++}
++
++/**
++ * Checks a node for validity as a Sizzle context
++ * @param {Element|Object=} context
++ * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
++ */
++function testContext( context ) {
++ return context && typeof context.getElementsByTagName !== "undefined" && context;
++}
++
++// Expose support vars for convenience
++support = Sizzle.support = {};
++
++/**
++ * Detects XML nodes
++ * @param {Element|Object} elem An element or a document
++ * @returns {Boolean} True iff elem is a non-HTML XML node
++ */
++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;
++};
++
++/**
++ * 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 hasCompare, parent,
++ doc = node ? node.ownerDocument || node : preferredDoc;
++
++ // Return early if doc is invalid or already selected
++ if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
++ return document;
++ }
++
++ // Update global variables
++ document = doc;
++ docElem = document.documentElement;
++ documentIsHTML = !isXML( document );
++
++ // Support: IE 9-11, Edge
++ // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936)
++ if ( (parent = document.defaultView) && parent.top !== parent ) {
++ // Support: IE 11
++ if ( parent.addEventListener ) {
++ parent.addEventListener( "unload", unloadHandler, false );
++
++ // Support: IE 9 - 10 only
++ } else if ( parent.attachEvent ) {
++ parent.attachEvent( "onunload", unloadHandler );
++ }
++ }
++
++ /* 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( document.createComment("") );
++ return !div.getElementsByTagName("*").length;
++ });
++
++ // Support: IE<9
++ support.getElementsByClassName = rnative.test( document.getElementsByClassName );
++
++ // 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 !document.getElementsByName || !document.getElementsByName( expando ).length;
++ });
++
++ // ID find and filter
++ if ( support.getById ) {
++ Expr.find["ID"] = function( id, context ) {
++ if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
++ var m = context.getElementById( id );
++ return m ? [ 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 !== "undefined" &&
++ elem.getAttributeNode("id");
++ return node && node.value === attrId;
++ };
++ };
++ }
++
++ // Tag
++ Expr.find["TAG"] = support.getElementsByTagName ?
++ function( tag, context ) {
++ if ( typeof context.getElementsByTagName !== "undefined" ) {
++ return context.getElementsByTagName( tag );
++
++ // DocumentFragment nodes don't have gEBTN
++ } else if ( support.qsa ) {
++ return context.querySelectorAll( tag );
++ }
++ } :
++
++ function( tag, context ) {
++ var elem,
++ tmp = [],
++ i = 0,
++ // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too
++ 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 !== "undefined" && 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( document.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
++ docElem.appendChild( div ).innerHTML = "<a id='" + expando + "'></a>" +
++ "<select id='" + expando + "-\r\\' msallowcapture=''>" +
++ "<option selected=''></option></select>";
++
++ // Support: IE8, Opera 11-12.16
++ // Nothing should be selected when empty strings follow ^= or $= or *=
++ // The test attribute must be unknown in Opera but "safe" for WinRT
++ // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section
++ if ( div.querySelectorAll("[msallowcapture^='']").length ) {
++ rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
++ }
++
++ // Support: IE8
++ // Boolean attributes and "value" are not treated correctly
++ if ( !div.querySelectorAll("[selected]").length ) {
++ rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
++ }
++
++ // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+
++ if ( !div.querySelectorAll( "[id~=" + expando + "-]" ).length ) {
++ rbuggyQSA.push("~=");
++ }
++
++ // 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");
++ }
++
++ // Support: Safari 8+, iOS 8+
++ // https://bugs.webkit.org/show_bug.cgi?id=136851
++ // In-page `selector#id sibing-combinator selector` fails
++ if ( !div.querySelectorAll( "a#" + expando + "+*" ).length ) {
++ rbuggyQSA.push(".#.+[+~]");
++ }
++ });
++
++ assert(function( div ) {
++ // Support: Windows 8 Native Apps
++ // The type and name attributes are restricted during .innerHTML assignment
++ var input = document.createElement("input");
++ input.setAttribute( "type", "hidden" );
++ div.appendChild( input ).setAttribute( "name", "D" );
++
++ // Support: IE8
++ // Enforce case-sensitivity of name attribute
++ if ( div.querySelectorAll("[name=d]").length ) {
++ rbuggyQSA.push( "name" + 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.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
++ ---------------------------------------------------------------------- */
++ hasCompare = rnative.test( docElem.compareDocumentPosition );
++
++ // Element contains another
++ // Purposefully self-exclusive
++ // As in, an element does not contain itself
++ contains = hasCompare || rnative.test( docElem.contains ) ?
++ 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 = hasCompare ?
++ function( a, b ) {
++
++ // Flag for duplicate removal
++ if ( a === b ) {
++ hasDuplicate = true;
++ return 0;
++ }
++
++ // Sort on method existence if only one input has compareDocumentPosition
++ var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
++ if ( compare ) {
++ return compare;
++ }
++
++ // Calculate position if both inputs belong to the same document
++ compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ?
++ a.compareDocumentPosition( b ) :
++
++ // Otherwise we know they are disconnected
++ 1;
++
++ // Disconnected nodes
++ if ( compare & 1 ||
++ (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {
++
++ // Choose the first element that is related to our preferred document
++ if ( a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) {
++ return -1;
++ }
++ if ( b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) {
++ return 1;
++ }
++
++ // Maintain original order
++ return sortInput ?
++ ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
++ 0;
++ }
++
++ return compare & 4 ? -1 : 1;
++ } :
++ function( a, b ) {
++ // Exit early if the nodes are identical
++ if ( a === b ) {
++ hasDuplicate = true;
++ return 0;
++ }
++
++ var cur,
++ i = 0,
++ aup = a.parentNode,
++ bup = b.parentNode,
++ ap = [ a ],
++ bp = [ b ];
++
++ // Parentless nodes are either documents or disconnected
++ if ( !aup || !bup ) {
++ return a === document ? -1 :
++ b === document ? 1 :
++ aup ? -1 :
++ bup ? 1 :
++ sortInput ?
++ ( indexOf( sortInput, a ) - indexOf( 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 document;
++};
++
++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 &&
++ !compilerCache[ expr + " " ] &&
++ ( !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 ?
++ val :
++ support.attributes || !documentIsHTML ?
++ elem.getAttribute( name ) :
++ (val = elem.getAttributeNode(name)) && val.specified ?
++ val.value :
++ null;
++};
++
++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 );
++ }
++ }
++
++ // Clear input after sorting to release objects
++ // See https://github.com/jquery/sizzle/pull/225
++ sortInput = null;
++
++ 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
++ while ( (node = elem[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 (jQuery #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[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[6] && match[2];
++
++ if ( matchExpr["CHILD"].test( match[0] ) ) {
++ return null;
++ }
++
++ // Accept quoted arguments as-is
++ if ( match[3] ) {
++ match[2] = match[4] || match[5] || "";
++
++ // 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 !== "undefined" && 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.replace( rwhitespace, " " ) + " " ).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, uniqueCache, outerCache, node, nodeIndex, start,
++ dir = simple !== forward ? "nextSibling" : "previousSibling",
++ parent = elem.parentNode,
++ name = ofType && elem.nodeName.toLowerCase(),
++ useCache = !xml && !ofType,
++ diff = false;
++
++ 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;
++ }
++
++ 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
++
++ // ...in a gzip-friendly way
++ node = parent;
++ outerCache = node[ expando ] || (node[ expando ] = {});
++
++ // Support: IE <9 only
++ // Defend against cloned attroperties (jQuery gh-1709)
++ uniqueCache = outerCache[ node.uniqueID ] ||
++ (outerCache[ node.uniqueID ] = {});
++
++ cache = uniqueCache[ type ] || [];
++ nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
++ diff = nodeIndex && 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 ) {
++ uniqueCache[ type ] = [ dirruns, nodeIndex, diff ];
++ break;
++ }
++ }
++
++ } else {
++ // Use previously-cached element index if available
++ if ( useCache ) {
++ // ...in a gzip-friendly way
++ node = elem;
++ outerCache = node[ expando ] || (node[ expando ] = {});
++
++ // Support: IE <9 only
++ // Defend against cloned attroperties (jQuery gh-1709)
++ uniqueCache = outerCache[ node.uniqueID ] ||
++ (outerCache[ node.uniqueID ] = {});
++
++ cache = uniqueCache[ type ] || [];
++ nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
++ diff = nodeIndex;
++ }
++
++ // xml :nth-child(...)
++ // or :nth-last-child(...) or :nth(-last)?-of-type(...)
++ if ( diff === false ) {
++ // 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 ) {
++ outerCache = node[ expando ] || (node[ expando ] = {});
++
++ // Support: IE <9 only
++ // Defend against cloned attroperties (jQuery gh-1709)
++ uniqueCache = outerCache[ node.uniqueID ] ||
++ (outerCache[ node.uniqueID ] = {});
++
++ uniqueCache[ type ] = [ dirruns, diff ];
++ }
++
++ if ( node === elem ) {
++ break;
++ }
++ }
++ }
++ }
++ }
++
++ // Incorporate the offset, then check against cycle size
++ diff -= last;
++ return diff === first || ( diff % first === 0 && diff / first >= 0 );
++ }
++ };
++ },
++
++ "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( seed, matched[i] );
++ seed[ idx ] = !( matches[ idx ] = matched[i] );
++ }
++ }) :
++ 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);
++ }
++ }
++ }) :
++ function( elem, context, xml ) {
++ input[0] = elem;
++ matcher( input, null, xml, results );
++ // Don't keep the element (issue #299)
++ input[0] = null;
++ return !results.pop();
++ };
++ }),
++
++ "has": markFunction(function( selector ) {
++ return function( elem ) {
++ return Sizzle( selector, elem ).length > 0;
++ };
++ }),
++
++ "contains": markFunction(function( text ) {
++ text = text.replace( runescape, funescape );
++ 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;
++ };
++ }),
++
++ // 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 negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
++ // but not by others (comment: 8; processing instruction: 7; etc.)
++ // nodeType < 6 works because attributes (2) do not appear as children
++ for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
++ if ( elem.nodeType < 6 ) {
++ 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;
++ return elem.nodeName.toLowerCase() === "input" &&
++ elem.type === "text" &&
++
++ // Support: IE<8
++ // New HTML5 attribute values (e.g., "search") appear with elem.type === "text"
++ ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" );
++ },
++
++ // 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();
++
++tokenize = Sizzle.tokenize = function( 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 oldCache, uniqueCache, outerCache,
++ newCache = [ dirruns, doneName ];
++
++ // We can't set arbitrary data on XML nodes, so they don't benefit from combinator 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 ] = {});
++
++ // Support: IE <9 only
++ // Defend against cloned attroperties (jQuery gh-1709)
++ uniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {});
++
++ if ( (oldCache = uniqueCache[ dir ]) &&
++ oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {
++
++ // Assign to newCache so results back-propagate to previous elements
++ return (newCache[ 2 ] = oldCache[ 2 ]);
++ } else {
++ // Reuse newcache so results back-propagate to previous elements
++ uniqueCache[ dir ] = newCache;
++
++ // A match means we're done; a fail means we have to keep checking
++ if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) {
++ 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 multipleContexts( selector, contexts, results ) {
++ var i = 0,
++ len = contexts.length;
++ for ( ; i < len; i++ ) {
++ Sizzle( selector, contexts[i], results );
++ }
++ return results;
++}
++
++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( 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( checkContext, elem ) > -1;
++ }, implicitRelative, true ),
++ matchers = [ function( elem, context, xml ) {
++ var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
++ (checkContext = context).nodeType ?
++ matchContext( elem, context, xml ) :
++ matchAnyContext( elem, context, xml ) );
++ // Avoid hanging onto element (issue #299)
++ checkContext = null;
++ return ret;
++ } ];
++
++ 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 ) {
++ var bySet = setMatchers.length > 0,
++ byElement = elementMatchers.length > 0,
++ superMatcher = function( seed, context, xml, results, outermost ) {
++ var elem, j, matcher,
++ matchedCount = 0,
++ i = "0",
++ unmatched = seed && [],
++ setMatched = [],
++ contextBackup = outermostContext,
++ // We must always have either seed elements or outermost context
++ elems = seed || byElement && Expr.find["TAG"]( "*", outermost ),
++ // Use integer dirruns iff this is the outermost matcher
++ dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1),
++ len = elems.length;
++
++ if ( outermost ) {
++ outermostContext = context === document || context || outermost;
++ }
++
++ // Add elements passing elementMatchers directly to results
++ // Support: IE<9, Safari
++ // Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id
++ for ( ; i !== len && (elem = elems[i]) != null; i++ ) {
++ if ( byElement && elem ) {
++ j = 0;
++ if ( !context && elem.ownerDocument !== document ) {
++ setDocument( elem );
++ xml = !documentIsHTML;
++ }
++ while ( (matcher = elementMatchers[j++]) ) {
++ if ( matcher( elem, context || document, xml) ) {
++ results.push( elem );
++ break;
++ }
++ }
++ if ( outermost ) {
++ dirruns = dirrunsUnique;
++ }
++ }
++
++ // 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 );
++ }
++ }
++ }
++
++ // `i` is now the count of elements visited above, and adding it to `matchedCount`
++ // makes the latter nonnegative.
++ matchedCount += i;
++
++ // Apply set filters to unmatched elements
++ // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount`
++ // equals `i`), unless we didn't visit _any_ elements in the above loop because we have
++ // no element matchers and no seed.
++ // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that
++ // case, which will result in a "00" `matchedCount` that differs from `i` but is also
++ // numerically zero.
++ 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, match /* 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 ( !match ) {
++ match = tokenize( selector );
++ }
++ i = match.length;
++ while ( i-- ) {
++ cached = matcherFromTokens( match[i] );
++ if ( cached[ expando ] ) {
++ setMatchers.push( cached );
++ } else {
++ elementMatchers.push( cached );
++ }
++ }
++
++ // Cache the compiled function
++ cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
++
++ // Save selector and tokenization
++ cached.selector = selector;
++ }
++ return cached;
++};
++
++/**
++ * A low-level selection function that works with Sizzle's compiled
++ * selector functions
++ * @param {String|Function} selector A selector or a pre-compiled
++ * selector function built with Sizzle.compile
++ * @param {Element} context
++ * @param {Array} [results]
++ * @param {Array} [seed] A set of elements to match against
++ */
++select = Sizzle.select = function( selector, context, results, seed ) {
++ var i, tokens, token, type, find,
++ compiled = typeof selector === "function" && selector,
++ match = !seed && tokenize( (selector = compiled.selector || selector) );
++
++ results = results || [];
++
++ // Try to minimize operations if there is only one selector in the list and no seed
++ // (the latter of which guarantees us context)
++ if ( match.length === 1 ) {
++
++ // Reduce context if the leading compound 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;
++
++ // Precompiled matchers will still verify ancestry, so step up a level
++ } else if ( compiled ) {
++ context = context.parentNode;
++ }
++
++ 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 ) && testContext( 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 if one is not provided
++ // Provide `match` to avoid retokenization if we modified the selector above
++ ( compiled || compile( selector, match ) )(
++ seed,
++ context,
++ !documentIsHTML,
++ results,
++ !context || rsibling.test( selector ) && testContext( context.parentNode ) || context
++ );
++ return results;
++};
++
++// One-time assignments
++
++// Sort stability
++support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;
++
++// Support: Chrome 14-35+
++// 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 = "<a href='#'></a>";
++ 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 = "<input/>";
++ 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 elem[ name ] === true ? name.toLowerCase() :
++ (val = elem.getAttributeNode( name )) && val.specified ?
++ val.value :
++ null;
++ }
++ });
++}
++
++return Sizzle;
++
++})( window );
++
++
++
++jQuery.find = Sizzle;
++jQuery.expr = Sizzle.selectors;
++jQuery.expr[ ":" ] = jQuery.expr.pseudos;
++jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort;
++jQuery.text = Sizzle.getText;
++jQuery.isXMLDoc = Sizzle.isXML;
++jQuery.contains = Sizzle.contains;
++
++
++
++var dir = function( elem, dir, until ) {
++ var matched = [],
++ truncate = until !== undefined;
++
++ while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) {
++ if ( elem.nodeType === 1 ) {
++ if ( truncate && jQuery( elem ).is( until ) ) {
++ break;
++ }
++ matched.push( elem );
++ }
++ }
++ return matched;
++};
++
++
++var siblings = function( n, elem ) {
++ var matched = [];
++
++ for ( ; n; n = n.nextSibling ) {
++ if ( n.nodeType === 1 && n !== elem ) {
++ matched.push( n );
++ }
++ }
++
++ return matched;
++};
++
++
++var rneedsContext = jQuery.expr.match.needsContext;
++
++var rsingleTag = ( /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/ );
++
++
++
++var risSimple = /^.[^:#\[\.,]*$/;
++
++// Implement the identical functionality for filter and not
++function winnow( elements, qualifier, not ) {
++ if ( jQuery.isFunction( qualifier ) ) {
++ return jQuery.grep( elements, function( elem, i ) {
++ /* jshint -W018 */
++ return !!qualifier.call( elem, i, elem ) !== not;
++ } );
++
++ }
++
++ if ( qualifier.nodeType ) {
++ return jQuery.grep( elements, function( elem ) {
++ return ( elem === qualifier ) !== not;
++ } );
++
++ }
++
++ if ( typeof qualifier === "string" ) {
++ if ( risSimple.test( qualifier ) ) {
++ return jQuery.filter( qualifier, elements, not );
++ }
++
++ qualifier = jQuery.filter( qualifier, elements );
++ }
++
++ return jQuery.grep( elements, function( elem ) {
++ return ( indexOf.call( qualifier, elem ) > -1 ) !== not;
++ } );
++}
++
++jQuery.filter = function( expr, elems, not ) {
++ var elem = elems[ 0 ];
++
++ if ( not ) {
++ expr = ":not(" + expr + ")";
++ }
++
++ 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;
++ } ) );
++};
++
++jQuery.fn.extend( {
++ find: function( selector ) {
++ var i,
++ len = this.length,
++ ret = [],
++ self = this;
++
++ if ( typeof selector !== "string" ) {
++ return this.pushStack( jQuery( selector ).filter( function() {
++ for ( i = 0; i < len; i++ ) {
++ if ( jQuery.contains( self[ i ], this ) ) {
++ return true;
++ }
++ }
++ } ) );
++ }
++
++ 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;
++ },
++ filter: function( selector ) {
++ return this.pushStack( winnow( this, selector || [], false ) );
++ },
++ not: function( selector ) {
++ return this.pushStack( winnow( this, selector || [], true ) );
++ },
++ is: function( selector ) {
++ 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;
++ }
++} );
++
++
++// Initialize a jQuery object
++
++
++// A central reference to the root jQuery(document)
++var rootjQuery,
++
++ // A simple way to check for HTML strings
++ // Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
++ // Strict HTML recognition (#11290: must start with <)
++ rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
++
++ init = jQuery.fn.init = function( selector, context, root ) {
++ var match, elem;
++
++ // HANDLE: $(""), $(null), $(undefined), $(false)
++ if ( !selector ) {
++ return this;
++ }
++
++ // Method init() accepts an alternate rootjQuery
++ // so migrate can support jQuery.sub (gh-2101)
++ root = root || rootjQuery;
++
++ // Handle HTML strings
++ if ( typeof selector === "string" ) {
++ if ( selector[ 0 ] === "<" &&
++ selector[ 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 = 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;
++
++ // Option to run scripts is true for back-compat
++ // Intentionally let the error be thrown if parseHTML is not present
++ 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 ] );
++ }
++ }
++ }
++
++ return this;
++
++ // HANDLE: $(#id)
++ } else {
++ elem = document.getElementById( match[ 2 ] );
++
++ // Support: Blackberry 4.6
++ // gEBID returns nodes no longer in the document (#6963)
++ if ( elem && elem.parentNode ) {
++
++ // Inject the element directly into the jQuery object
++ this.length = 1;
++ this[ 0 ] = elem;
++ }
++
++ this.context = document;
++ this.selector = selector;
++ return this;
++ }
++
++ // HANDLE: $(expr, $(...))
++ } else if ( !context || context.jquery ) {
++ return ( context || root ).find( selector );
++
++ // HANDLE: $(expr, context)
++ // (which is just equivalent to: $(context).find(expr)
++ } else {
++ 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 root.ready !== undefined ?
++ root.ready( selector ) :
++
++ // Execute immediately if ready is not present
++ selector( jQuery );
++ }
++
++ if ( selector.selector !== undefined ) {
++ this.selector = selector.selector;
++ this.context = selector.context;
++ }
++
++ return jQuery.makeArray( selector, this );
++ };
++
++// Give the init function the jQuery prototype for later instantiation
++init.prototype = jQuery.fn;
++
++// Initialize central reference
++rootjQuery = jQuery( document );
++
++
++var rparentsprev = /^(?:parents|prev(?:Until|All))/,
++
++ // Methods guaranteed to produce a unique set when starting from a unique set
++ guaranteedUnique = {
++ children: true,
++ contents: true,
++ next: true,
++ prev: true
++ };
++
++jQuery.fn.extend( {
++ has: function( target ) {
++ var targets = jQuery( target, this ),
++ l = targets.length;
++
++ return this.filter( function() {
++ var i = 0;
++ for ( ; i < l; i++ ) {
++ if ( jQuery.contains( this, targets[ i ] ) ) {
++ return true;
++ }
++ }
++ } );
++ },
++
++ closest: function( selectors, context ) {
++ var cur,
++ i = 0,
++ l = this.length,
++ matched = [],
++ pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
++ jQuery( selectors, context || this.context ) :
++ 0;
++
++ 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 ) ) ) {
++
++ matched.push( cur );
++ break;
++ }
++ }
++ }
++
++ return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched );
++ },
++
++ // Determine the position of an element within the set
++ index: function( elem ) {
++
++ // No argument, return index in parent
++ if ( !elem ) {
++ return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
++ }
++
++ // Index in selector
++ if ( typeof elem === "string" ) {
++ return indexOf.call( jQuery( elem ), this[ 0 ] );
++ }
++
++ // Locate the position of the desired element
++ return indexOf.call( this,
++
++ // If it receives a jQuery object, the first element is used
++ elem.jquery ? elem[ 0 ] : elem
++ );
++ },
++
++ add: function( selector, context ) {
++ return this.pushStack(
++ jQuery.uniqueSort(
++ jQuery.merge( this.get(), jQuery( selector, context ) )
++ )
++ );
++ },
++
++ addBack: function( selector ) {
++ return this.add( selector == null ?
++ this.prevObject : this.prevObject.filter( selector )
++ );
++ }
++} );
++
++function sibling( cur, dir ) {
++ while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {}
++ return cur;
++}
++
++jQuery.each( {
++ parent: function( elem ) {
++ var parent = elem.parentNode;
++ return parent && parent.nodeType !== 11 ? parent : null;
++ },
++ parents: function( elem ) {
++ return dir( elem, "parentNode" );
++ },
++ parentsUntil: function( elem, i, until ) {
++ return dir( elem, "parentNode", until );
++ },
++ next: function( elem ) {
++ return sibling( elem, "nextSibling" );
++ },
++ prev: function( elem ) {
++ return sibling( elem, "previousSibling" );
++ },
++ nextAll: function( elem ) {
++ return dir( elem, "nextSibling" );
++ },
++ prevAll: function( elem ) {
++ return dir( elem, "previousSibling" );
++ },
++ nextUntil: function( elem, i, until ) {
++ return dir( elem, "nextSibling", until );
++ },
++ prevUntil: function( elem, i, until ) {
++ return dir( elem, "previousSibling", until );
++ },
++ siblings: function( elem ) {
++ return siblings( ( elem.parentNode || {} ).firstChild, elem );
++ },
++ children: function( elem ) {
++ return siblings( elem.firstChild );
++ },
++ contents: function( elem ) {
++ return elem.contentDocument || jQuery.merge( [], elem.childNodes );
++ }
++}, function( name, fn ) {
++ jQuery.fn[ name ] = function( until, selector ) {
++ var matched = jQuery.map( this, fn, until );
++
++ if ( name.slice( -5 ) !== "Until" ) {
++ selector = until;
++ }
++
++ if ( selector && typeof selector === "string" ) {
++ matched = jQuery.filter( selector, matched );
++ }
++
++ if ( this.length > 1 ) {
++
++ // Remove duplicates
++ if ( !guaranteedUnique[ name ] ) {
++ jQuery.uniqueSort( matched );
++ }
++
++ // Reverse order for parents* and prev-derivatives
++ if ( rparentsprev.test( name ) ) {
++ matched.reverse();
++ }
++ }
++
++ return this.pushStack( matched );
++ };
++} );
++var rnotwhite = ( /\S+/g );
++
++
++
++// Convert String-formatted options into Object-formatted ones
++function createOptions( options ) {
++ var object = {};
++ jQuery.each( options.match( 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" ?
++ 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,
++
++ // Flag to prevent firing
++ locked,
++
++ // Actual callback list
++ list = [],
++
++ // Queue of execution data for repeatable lists
++ queue = [],
++
++ // Index of currently firing callback (modified by add/remove as needed)
++ firingIndex = -1,
++
++ // Fire callbacks
++ fire = function() {
++
++ // Enforce single-firing
++ locked = options.once;
++
++ // Execute callbacks for all pending executions,
++ // respecting firingIndex overrides and runtime changes
++ fired = firing = true;
++ for ( ; queue.length; firingIndex = -1 ) {
++ memory = queue.shift();
++ while ( ++firingIndex < list.length ) {
++
++ // Run callback and check for early termination
++ if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&
++ options.stopOnFalse ) {
++
++ // Jump to end and forget the data so .add doesn't re-fire
++ firingIndex = list.length;
++ memory = false;
++ }
++ }
++ }
++
++ // Forget the data if we're done with it
++ if ( !options.memory ) {
++ memory = false;
++ }
++
++ firing = false;
++
++ // Clean up if we're done firing for good
++ if ( locked ) {
++
++ // Keep an empty list if we have data for future add calls
++ if ( memory ) {
++ list = [];
++
++ // Otherwise, this object is spent
++ } else {
++ list = "";
++ }
++ }
++ },
++
++ // Actual Callbacks object
++ self = {
++
++ // Add a callback or a collection of callbacks to the list
++ add: function() {
++ if ( list ) {
++
++ // If we have memory from a past run, we should fire after adding
++ if ( memory && !firing ) {
++ firingIndex = list.length - 1;
++ queue.push( memory );
++ }
++
++ ( function add( args ) {
++ jQuery.each( args, function( _, arg ) {
++ if ( jQuery.isFunction( arg ) ) {
++ if ( !options.unique || !self.has( arg ) ) {
++ list.push( arg );
++ }
++ } else if ( arg && arg.length && jQuery.type( arg ) !== "string" ) {
++
++ // Inspect recursively
++ add( arg );
++ }
++ } );
++ } )( arguments );
++
++ if ( memory && !firing ) {
++ fire();
++ }
++ }
++ return this;
++ },
++
++ // Remove a callback from the list
++ remove: function() {
++ jQuery.each( arguments, function( _, arg ) {
++ var index;
++ while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
++ list.splice( index, 1 );
++
++ // Handle firing indexes
++ 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.length > 0;
++ },
++
++ // Remove all callbacks from the list
++ empty: function() {
++ if ( list ) {
++ list = [];
++ }
++ return this;
++ },
++
++ // Disable .fire and .add
++ // Abort any current/pending executions
++ // Clear all callbacks and values
++ disable: function() {
++ locked = queue = [];
++ list = memory = "";
++ return this;
++ },
++ disabled: function() {
++ return !list;
++ },
++
++ // Disable .fire
++ // Also disable .add unless we have memory (since it would have no effect)
++ // Abort any pending executions
++ lock: function() {
++ locked = queue = [];
++ if ( !memory ) {
++ list = memory = "";
++ }
++ return this;
++ },
++ locked: function() {
++ return !!locked;
++ },
++
++ // Call all callbacks with the given context and arguments
++ fireWith: function( context, args ) {
++ if ( !locked ) {
++ args = args || [];
++ args = [ context, args.slice ? args.slice() : args ];
++ queue.push( args );
++ if ( !firing ) {
++ fire();
++ }
++ }
++ 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 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()
++ .progress( newDefer.notify )
++ .done( newDefer.resolve )
++ .fail( newDefer.reject );
++ } else {
++ newDefer[ tuple[ 0 ] + "With" ](
++ this === promise ? newDefer.promise() : this,
++ fn ? [ returned ] : arguments
++ );
++ }
++ } );
++ } );
++ 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;
++ }
++ },
++ 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 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( subordinate /* , ..., subordinateN */ ) {
++ var i = 0,
++ resolveValues = 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 ? 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 ) {
++ 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()
++ .progress( updateFunc( i, progressContexts, progressValues ) )
++ .done( updateFunc( i, resolveContexts, resolveValues ) )
++ .fail( deferred.reject );
++ } else {
++ --remaining;
++ }
++ }
++ }
++
++ // If we're not waiting on anything, resolve the master
++ if ( !remaining ) {
++ deferred.resolveWith( resolveContexts, resolveValues );
++ }
++
++ return deferred.promise();
++ }
++} );
++
++
++// The deferred used on DOM ready
++var readyList;
++
++jQuery.fn.ready = function( fn ) {
++
++ // Add the callback
++ jQuery.ready.promise().done( fn );
++
++ return this;
++};
++
++jQuery.extend( {
++
++ // Is the DOM ready to be used? Set to true once it occurs.
++ isReady: false,
++
++ // A counter to track how many items to wait for before
++ // the ready event fires. See #6781
++ readyWait: 1,
++
++ // Hold (or release) the ready event
++ holdReady: function( hold ) {
++ if ( hold ) {
++ jQuery.readyWait++;
++ } else {
++ jQuery.ready( true );
++ }
++ },
++
++ // Handle when the DOM is ready
++ ready: function( wait ) {
++
++ // Abort if there are pending holds or we're already ready
++ if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
++ return;
++ }
++
++ // 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.triggerHandler ) {
++ jQuery( document ).triggerHandler( "ready" );
++ jQuery( document ).off( "ready" );
++ }
++ }
++} );
++
++/**
++ * The ready event handler and self cleanup method
++ */
++function completed() {
++ document.removeEventListener( "DOMContentLoaded", completed );
++ window.removeEventListener( "load", completed );
++ jQuery.ready();
++}
++
++jQuery.ready.promise = function( obj ) {
++ if ( !readyList ) {
++
++ readyList = jQuery.Deferred();
++
++ // Catch cases where $(document).ready() is called
++ // after the browser event has already occurred.
++ // Support: IE9-10 only
++ // Older IE sometimes signals "interactive" too soon
++ if ( document.readyState === "complete" ||
++ ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) {
++
++ // Handle it asynchronously to allow scripts the opportunity to delay ready
++ window.setTimeout( jQuery.ready );
++
++ } else {
++
++ // Use the handy event callback
++ document.addEventListener( "DOMContentLoaded", completed );
++
++ // A fallback to window.onload, that will always work
++ window.addEventListener( "load", completed );
++ }
++ }
++ return readyList.promise( obj );
++};
++
++// Kick off the DOM ready check even if the user does not
++jQuery.ready.promise();
++
++
++
++
++// Multifunctional method to get and set values of a collection
++// The value/s can optionally be executed if it's a function
++var access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
++ var i = 0,
++ len = elems.length,
++ bulk = key == null;
++
++ // Sets many values
++ if ( jQuery.type( key ) === "object" ) {
++ chainable = true;
++ for ( i in key ) {
++ 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 < len; i++ ) {
++ fn(
++ elems[ i ], key, raw ?
++ value :
++ value.call( elems[ i ], i, fn( elems[ i ], key ) )
++ );
++ }
++ }
++ }
++
++ return chainable ?
++ elems :
++
++ // Gets
++ bulk ?
++ fn.call( elems ) :
++ len ? fn( elems[ 0 ], key ) : emptyGet;
++};
++var acceptData = function( owner ) {
++
++ // Accepts only:
++ // - Node
++ // - Node.ELEMENT_NODE
++ // - Node.DOCUMENT_NODE
++ // - Object
++ // - Any
++ /* jshint -W018 */
++ return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType );
++};
++
++
++
++
++function Data() {
++ this.expando = jQuery.expando + Data.uid++;
++}
++
++Data.uid = 1;
++
++Data.prototype = {
++
++ register: function( owner, initial ) {
++ var value = initial || {};
++
++ // If it is a node unlikely to be stringify-ed or looped over
++ // use plain assignment
++ if ( owner.nodeType ) {
++ owner[ this.expando ] = value;
++
++ // Otherwise secure it in a non-enumerable, non-writable property
++ // configurability must be true to allow the property to be
++ // deleted with the delete operator
++ } else {
++ Object.defineProperty( owner, this.expando, {
++ value: value,
++ writable: true,
++ configurable: true
++ } );
++ }
++ return owner[ this.expando ];
++ },
++ cache: function( owner ) {
++
++ // We can accept data for non-element nodes in modern browsers,
++ // but we should not, see #8335.
++ // Always return an empty object.
++ if ( !acceptData( owner ) ) {
++ return {};
++ }
++
++ // Check if the owner object already has a cache
++ var value = owner[ this.expando ];
++
++ // If not, create one
++ if ( !value ) {
++ value = {};
++
++ // We can accept data for non-element nodes in modern browsers,
++ // but we should not, see #8335.
++ // Always return an empty object.
++ if ( acceptData( owner ) ) {
++
++ // If it is a node unlikely to be stringify-ed or looped over
++ // use plain assignment
++ if ( owner.nodeType ) {
++ owner[ this.expando ] = value;
++
++ // Otherwise secure it in a non-enumerable property
++ // configurable must be true to allow the property to be
++ // deleted when data is removed
++ } else {
++ Object.defineProperty( owner, this.expando, {
++ value: value,
++ configurable: true
++ } );
++ }
++ }
++ }
++
++ return value;
++ },
++ set: function( owner, data, value ) {
++ var prop,
++ cache = this.cache( owner );
++
++ // Handle: [ owner, key, value ] args
++ if ( typeof data === "string" ) {
++ cache[ data ] = value;
++
++ // Handle: [ owner, { properties } ] args
++ } else {
++
++ // Copy the properties one-by-one to the cache object
++ for ( prop in data ) {
++ cache[ prop ] = data[ prop ];
++ }
++ }
++ return cache;
++ },
++ get: function( owner, key ) {
++ return key === undefined ?
++ this.cache( owner ) :
++ owner[ this.expando ] && owner[ this.expando ][ key ];
++ },
++ access: function( owner, key, value ) {
++ var stored;
++
++ // In cases where either:
++ //
++ // 1. No key was specified
++ // 2. A string key was specified, but no value provided
++ //
++ // Take the "read" path and allow the get method to determine
++ // which value to return, respectively either:
++ //
++ // 1. The entire cache object
++ // 2. The data stored at the key
++ //
++ if ( key === undefined ||
++ ( ( key && typeof key === "string" ) && value === undefined ) ) {
++
++ stored = this.get( owner, key );
++
++ return stored !== undefined ?
++ stored : this.get( owner, jQuery.camelCase( key ) );
++ }
++
++ // When the key is not a string, or both a key and value
++ // are specified, set or extend (existing objects) with either:
++ //
++ // 1. An object of properties
++ // 2. A key and value
++ //
++ this.set( owner, key, value );
++
++ // Since the "set" path can have two possible entry points
++ // return the expected data based on which path was taken[*]
++ return value !== undefined ? value : key;
++ },
++ remove: function( owner, key ) {
++ var i, name, camel,
++ cache = owner[ this.expando ];
++
++ if ( cache === undefined ) {
++ return;
++ }
++
++ if ( key === undefined ) {
++ this.register( owner );
++
++ } else {
++
++ // Support array or space separated string of keys
++ if ( jQuery.isArray( key ) ) {
++
++ // 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 = key.concat( key.map( jQuery.camelCase ) );
++ } else {
++ camel = jQuery.camelCase( key );
++
++ // Try the string as a key before any manipulation
++ if ( key in cache ) {
++ name = [ key, camel ];
++ } else {
++
++ // If a key with the spaces exists, use it.
++ // Otherwise, create an array by matching non-whitespace
++ name = camel;
++ name = name in cache ?
++ [ name ] : ( name.match( rnotwhite ) || [] );
++ }
++ }
++
++ i = name.length;
++
++ while ( i-- ) {
++ delete cache[ name[ i ] ];
++ }
++ }
++
++ // Remove the expando if there's no more data
++ if ( key === undefined || jQuery.isEmptyObject( cache ) ) {
++
++ // Support: Chrome <= 35-45+
++ // Webkit & Blink performance suffers when deleting properties
++ // from DOM nodes, so set to undefined instead
++ // https://code.google.com/p/chromium/issues/detail?id=378607
++ if ( owner.nodeType ) {
++ owner[ this.expando ] = undefined;
++ } else {
++ delete owner[ this.expando ];
++ }
++ }
++ },
++ hasData: function( owner ) {
++ var cache = owner[ this.expando ];
++ return cache !== undefined && !jQuery.isEmptyObject( cache );
++ }
++};
++var dataPriv = new Data();
++
++var dataUser = new Data();
++
++
++
++// Implementation Summary
++//
++// 1. Enforce API surface and semantic compatibility with 1.9.x branch
++// 2. Improve the module's maintainability by reducing the storage
++// paths to a single mechanism.
++// 3. Use the same single mechanism to support "private" and "user" data.
++// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData)
++// 5. Avoid exposing implementation details on user objects (eg. expando properties)
++// 6. Provide a clear path for implementation upgrade to WeakMap in 2014
++
++var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
++ rmultiDash = /[A-Z]/g;
++
++function dataAttr( elem, key, data ) {
++ var name;
++
++ // If nothing was found internally, try to fetch any
++ // data from the HTML5 data-* attribute
++ if ( data === undefined && elem.nodeType === 1 ) {
++ name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase();
++ data = elem.getAttribute( name );
++
++ if ( typeof data === "string" ) {
++ try {
++ data = data === "true" ? true :
++ 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;
++ } catch ( e ) {}
++
++ // Make sure we set the data so it isn't changed later
++ dataUser.set( elem, key, data );
++ } else {
++ data = undefined;
++ }
++ }
++ return data;
++}
++
++jQuery.extend( {
++ hasData: function( elem ) {
++ return dataUser.hasData( elem ) || dataPriv.hasData( elem );
++ },
++
++ data: function( elem, name, data ) {
++ return dataUser.access( elem, name, data );
++ },
++
++ removeData: function( elem, name ) {
++ dataUser.remove( elem, name );
++ },
++
++ // TODO: Now that all calls to _data and _removeData have been replaced
++ // with direct calls to dataPriv methods, these can be deprecated.
++ _data: function( elem, name, data ) {
++ return dataPriv.access( elem, name, data );
++ },
++
++ _removeData: function( elem, name ) {
++ dataPriv.remove( elem, name );
++ }
++} );
++
++jQuery.fn.extend( {
++ data: function( key, value ) {
++ var i, name, data,
++ elem = this[ 0 ],
++ attrs = elem && elem.attributes;
++
++ // Gets all values
++ if ( key === undefined ) {
++ if ( this.length ) {
++ data = dataUser.get( elem );
++
++ if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) {
++ i = attrs.length;
++ while ( i-- ) {
++
++ // Support: IE11+
++ // The attrs elements can be null (#14894)
++ if ( attrs[ i ] ) {
++ name = attrs[ i ].name;
++ if ( name.indexOf( "data-" ) === 0 ) {
++ name = jQuery.camelCase( name.slice( 5 ) );
++ dataAttr( elem, name, data[ name ] );
++ }
++ }
++ }
++ dataPriv.set( elem, "hasDataAttrs", true );
++ }
++ }
++
++ return data;
++ }
++
++ // Sets multiple values
++ if ( typeof key === "object" ) {
++ return this.each( function() {
++ dataUser.set( this, key );
++ } );
++ }
++
++ return access( this, function( value ) {
++ var data, camelKey;
++
++ // The calling jQuery object (element matches) is not empty
++ // (and therefore has an element appears at this[ 0 ]) and the
++ // `value` parameter was not undefined. An empty jQuery object
++ // will result in `undefined` for elem = this[ 0 ] which will
++ // throw an exception if an attempt to read a data cache is made.
++ if ( elem && value === undefined ) {
++
++ // Attempt to get data from the cache
++ // with the key as-is
++ data = dataUser.get( elem, key ) ||
++
++ // Try to find dashed key if it exists (gh-2779)
++ // This is for 2.2.x only
++ dataUser.get( elem, key.replace( rmultiDash, "-$&" ).toLowerCase() );
++
++ if ( data !== undefined ) {
++ return data;
++ }
++
++ camelKey = jQuery.camelCase( key );
++
++ // Attempt to get data from the cache
++ // with the key camelized
++ data = dataUser.get( elem, camelKey );
++ if ( data !== undefined ) {
++ return data;
++ }
++
++ // Attempt to "discover" the data in
++ // HTML5 custom data-* attrs
++ data = dataAttr( elem, camelKey, undefined );
++ if ( data !== undefined ) {
++ return data;
++ }
++
++ // We tried really hard, but the data doesn't exist.
++ return;
++ }
++
++ // Set the data...
++ camelKey = jQuery.camelCase( key );
++ this.each( function() {
++
++ // First, attempt to store a copy or reference of any
++ // data that might've been store with a camelCased key.
++ var data = dataUser.get( this, camelKey );
++
++ // For HTML5 data-* attribute interop, we have to
++ // store property names with dashes in a camelCase form.
++ // This might not apply to all properties...*
++ dataUser.set( this, camelKey, value );
++
++ // *... In the case of properties that might _actually_
++ // have dashes, we need to also store a copy of that
++ // unchanged property.
++ if ( key.indexOf( "-" ) > -1 && data !== undefined ) {
++ dataUser.set( this, key, value );
++ }
++ } );
++ }, null, value, arguments.length > 1, null, true );
++ },
++
++ removeData: function( key ) {
++ return this.each( function() {
++ dataUser.remove( this, key );
++ } );
++ }
++} );
++
++
++jQuery.extend( {
++ queue: function( elem, type, data ) {
++ var queue;
++
++ if ( elem ) {
++ type = ( type || "fx" ) + "queue";
++ queue = dataPriv.get( elem, type );
++
++ // Speed up dequeue by getting out quickly if this is just a lookup
++ if ( data ) {
++ if ( !queue || jQuery.isArray( data ) ) {
++ queue = dataPriv.access( elem, type, jQuery.makeArray( data ) );
++ } else {
++ queue.push( data );
++ }
++ }
++ return queue || [];
++ }
++ },
++
++ dequeue: function( elem, type ) {
++ type = type || "fx";
++
++ var queue = jQuery.queue( elem, type ),
++ startLength = queue.length,
++ fn = queue.shift(),
++ 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" );
++ }
++
++ // Clear up the last queue stop function
++ delete hooks.stop;
++ fn.call( elem, next, hooks );
++ }
++
++ if ( !startLength && hooks ) {
++ hooks.empty.fire();
++ }
++ },
++
++ // Not public - generate a queueHooks object, or return the current one
++ _queueHooks: function( elem, type ) {
++ var key = type + "queueHooks";
++ return dataPriv.get( elem, key ) || dataPriv.access( elem, key, {
++ empty: jQuery.Callbacks( "once memory" ).add( function() {
++ dataPriv.remove( elem, [ type + "queue", key ] );
++ } )
++ } );
++ }
++} );
++
++jQuery.fn.extend( {
++ queue: function( type, data ) {
++ var setter = 2;
++
++ if ( typeof type !== "string" ) {
++ data = type;
++ type = "fx";
++ setter--;
++ }
++
++ if ( arguments.length < setter ) {
++ return jQuery.queue( this[ 0 ], 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() {
++ jQuery.dequeue( this, type );
++ } );
++ },
++ clearQueue: function( type ) {
++ return this.queue( type || "fx", [] );
++ },
++
++ // Get a promise resolved when queues of a certain type
++ // are emptied (fx is the type by default)
++ promise: function( type, 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" ) {
++ obj = type;
++ type = undefined;
++ }
++ type = type || "fx";
++
++ while ( i-- ) {
++ tmp = dataPriv.get( elements[ i ], type + "queueHooks" );
++ if ( tmp && tmp.empty ) {
++ count++;
++ tmp.empty.add( resolve );
++ }
++ }
++ resolve();
++ return defer.promise( obj );
++ }
++} );
++var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source;
++
++var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" );
++
++
++var cssExpand = [ "Top", "Right", "Bottom", "Left" ];
++
++var isHidden = function( elem, el ) {
++
++ // isHidden might be called from jQuery#filter function;
++ // in that case, element will be second argument
++ elem = el || elem;
++ return jQuery.css( elem, "display" ) === "none" ||
++ !jQuery.contains( elem.ownerDocument, elem );
++ };
++
++
++
++function adjustCSS( elem, prop, valueParts, tween ) {
++ var adjusted,
++ scale = 1,
++ maxIterations = 20,
++ currentValue = tween ?
++ function() { return tween.cur(); } :
++ function() { return jQuery.css( elem, prop, "" ); },
++ initial = currentValue(),
++ unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
++
++ // Starting value computation is required for potential unit mismatches
++ initialInUnit = ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) &&
++ rcssNum.exec( jQuery.css( elem, prop ) );
++
++ if ( initialInUnit && initialInUnit[ 3 ] !== unit ) {
++
++ // Trust units reported by jQuery.css
++ unit = unit || initialInUnit[ 3 ];
++
++ // Make sure we update the tween properties later on
++ valueParts = valueParts || [];
++
++ // Iteratively approximate from a nonzero starting point
++ initialInUnit = +initial || 1;
++
++ do {
++
++ // If previous iteration zeroed out, double until we get *something*.
++ // Use string for doubling so we don't accidentally see scale as unchanged below
++ scale = scale || ".5";
++
++ // Adjust and apply
++ initialInUnit = initialInUnit / scale;
++ jQuery.style( elem, prop, initialInUnit + unit );
++
++ // Update scale, tolerating zero or NaN from tween.cur()
++ // Break the loop if scale is unchanged or perfect, or if we've just had enough.
++ } while (
++ scale !== ( scale = currentValue() / initial ) && scale !== 1 && --maxIterations
++ );
++ }
++
++ if ( valueParts ) {
++ initialInUnit = +initialInUnit || +initial || 0;
++
++ // Apply relative offset (+=/-=) if specified
++ adjusted = valueParts[ 1 ] ?
++ initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] :
++ +valueParts[ 2 ];
++ if ( tween ) {
++ tween.unit = unit;
++ tween.start = initialInUnit;
++ tween.end = adjusted;
++ }
++ }
++ return adjusted;
++}
++var rcheckableType = ( /^(?:checkbox|radio)$/i );
++
++var rtagName = ( /<([\w:-]+)/ );
++
++var rscriptType = ( /^$|\/(?:java|ecma)script/i );
++
++
++
++// We have to close these tags to support XHTML (#13200)
++var wrapMap = {
++
++ // Support: IE9
++ option: [ 1, "<select multiple='multiple'>", "</select>" ],
++
++ // XHTML parsers do not magically insert elements in the
++ // same way that tag soup parsers do. So we cannot shorten
++ // this by omitting <tbody> or other required elements.
++ thead: [ 1, "<table>", "</table>" ],
++ col: [ 2, "<table><colgroup>", "</colgroup></table>" ],
++ tr: [ 2, "<table><tbody>", "</tbody></table>" ],
++ td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
++
++ _default: [ 0, "", "" ]
++};
++
++// Support: IE9
++wrapMap.optgroup = wrapMap.option;
++
++wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
++wrapMap.th = wrapMap.td;
++
++
++function getAll( context, tag ) {
++
++ // Support: IE9-11+
++ // Use typeof to avoid zero-argument method invocation on host objects (#15151)
++ var ret = typeof context.getElementsByTagName !== "undefined" ?
++ context.getElementsByTagName( tag || "*" ) :
++ typeof context.querySelectorAll !== "undefined" ?
++ context.querySelectorAll( tag || "*" ) :
++ [];
++
++ return tag === undefined || tag && jQuery.nodeName( context, tag ) ?
++ jQuery.merge( [ context ], ret ) :
++ ret;
++}
++
++
++// Mark scripts as having already been evaluated
++function setGlobalEval( elems, refElements ) {
++ var i = 0,
++ l = elems.length;
++
++ for ( ; i < l; i++ ) {
++ dataPriv.set(
++ elems[ i ],
++ "globalEval",
++ !refElements || dataPriv.get( refElements[ i ], "globalEval" )
++ );
++ }
++}
++
++
++var rhtml = /<|&#?\w+;/;
++
++function buildFragment( elems, context, scripts, selection, ignored ) {
++ var elem, tmp, tag, wrap, contains, j,
++ fragment = context.createDocumentFragment(),
++ nodes = [],
++ i = 0,
++ l = elems.length;
++
++ for ( ; i < l; i++ ) {
++ elem = elems[ i ];
++
++ if ( elem || elem === 0 ) {
++
++ // Add nodes directly
++ if ( jQuery.type( elem ) === "object" ) {
++
++ // Support: Android<4.1, PhantomJS<2
++ // push.apply(_, arraylike) throws on ancient WebKit
++ jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
++
++ // Convert non-html into a text node
++ } else if ( !rhtml.test( elem ) ) {
++ nodes.push( context.createTextNode( elem ) );
++
++ // Convert html into DOM nodes
++ } else {
++ tmp = tmp || fragment.appendChild( context.createElement( "div" ) );
++
++ // Deserialize a standard representation
++ tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase();
++ wrap = wrapMap[ tag ] || wrapMap._default;
++ tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ];
++
++ // Descend through wrappers to the right content
++ j = wrap[ 0 ];
++ while ( j-- ) {
++ tmp = tmp.lastChild;
++ }
++
++ // Support: Android<4.1, PhantomJS<2
++ // push.apply(_, arraylike) throws on ancient WebKit
++ jQuery.merge( nodes, tmp.childNodes );
++
++ // Remember the top-level container
++ tmp = fragment.firstChild;
++
++ // Ensure the created nodes are orphaned (#12392)
++ tmp.textContent = "";
++ }
++ }
++ }
++
++ // Remove wrapper from fragment
++ fragment.textContent = "";
++
++ i = 0;
++ while ( ( elem = nodes[ i++ ] ) ) {
++
++ // Skip elements already in the context collection (trac-4087)
++ if ( selection && jQuery.inArray( elem, selection ) > -1 ) {
++ if ( ignored ) {
++ ignored.push( elem );
++ }
++ continue;
++ }
++
++ contains = jQuery.contains( elem.ownerDocument, elem );
++
++ // Append to fragment
++ tmp = getAll( fragment.appendChild( elem ), "script" );
++
++ // Preserve script evaluation history
++ if ( contains ) {
++ setGlobalEval( tmp );
++ }
++
++ // Capture executables
++ if ( scripts ) {
++ j = 0;
++ while ( ( elem = tmp[ j++ ] ) ) {
++ if ( rscriptType.test( elem.type || "" ) ) {
++ scripts.push( elem );
++ }
++ }
++ }
++ }
++
++ return fragment;
++}
++
++
++( function() {
++ var fragment = document.createDocumentFragment(),
++ div = fragment.appendChild( document.createElement( "div" ) ),
++ input = document.createElement( "input" );
++
++ // Support: Android 4.0-4.3, Safari<=5.1
++ // Check state lost if the name is set (#11217)
++ // Support: Windows Web Apps (WWA)
++ // `name` and `type` must use .setAttribute for WWA (#14901)
++ input.setAttribute( "type", "radio" );
++ input.setAttribute( "checked", "checked" );
++ input.setAttribute( "name", "t" );
++
++ div.appendChild( input );
++
++ // Support: Safari<=5.1, Android<4.2
++ // Older WebKit doesn't clone checked state correctly in fragments
++ support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;
++
++ // Support: IE<=11+
++ // Make sure textarea (and checkbox) defaultValue is properly cloned
++ div.innerHTML = "<textarea>x</textarea>";
++ support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;
++} )();
++
++
++var
++ rkeyEvent = /^key/,
++ rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/,
++ rtypenamespace = /^([^.]*)(?:\.(.+)|)/;
++
++function returnTrue() {
++ return true;
++}
++
++function returnFalse() {
++ return false;
++}
++
++// Support: IE9
++// See #13393 for more info
++function safeActiveElement() {
++ try {
++ return document.activeElement;
++ } catch ( err ) { }
++}
++
++function on( elem, types, selector, data, fn, one ) {
++ var origFn, type;
++
++ // 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 ) {
++ on( elem, type, selector, data, types[ type ], one );
++ }
++ return elem;
++ }
++
++ 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 elem;
++ }
++
++ 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 );
++ };
++
++ // Use same guid so caller can remove using origFn
++ fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
++ }
++ return elem.each( function() {
++ jQuery.event.add( this, types, fn, data, selector );
++ } );
++}
++
++/*
++ * Helper functions for managing events -- not part of the public interface.
++ * Props to Dean Edwards' addEvent library for many of the ideas.
++ */
++jQuery.event = {
++
++ global: {},
++
++ add: function( elem, types, handler, data, selector ) {
++
++ var handleObjIn, eventHandle, tmp,
++ events, t, handleObj,
++ special, handlers, type, namespaces, origType,
++ elemData = dataPriv.get( elem );
++
++ // Don't attach events to noData or text/comment nodes (but allow plain objects)
++ if ( !elemData ) {
++ return;
++ }
++
++ // Caller can pass in an object of custom data in lieu of the handler
++ if ( handler.handler ) {
++ handleObjIn = handler;
++ handler = handleObjIn.handler;
++ 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 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" && jQuery.event.triggered !== e.type ?
++ jQuery.event.dispatch.apply( elem, arguments ) : undefined;
++ };
++ }
++
++ // Handle multiple events separated by a space
++ types = ( types || "" ).match( 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 ] = [];
++ handlers.delegateCount = 0;
++
++ // Only use addEventListener if the special events handler returns false
++ if ( !special.setup ||
++ special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
++
++ if ( elem.addEventListener ) {
++ elem.addEventListener( type, eventHandle );
++ }
++ }
++ }
++
++ if ( special.add ) {
++ special.add.call( elem, handleObj );
++
++ if ( !handleObj.handler.guid ) {
++ handleObj.handler.guid = handler.guid;
++ }
++ }
++
++ // 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;
++ }
++
++ },
++
++ // Detach an event or set of events from an element
++ remove: function( elem, types, handler, selector, mappedTypes ) {
++
++ var j, origCount, tmp,
++ events, t, handleObj,
++ special, handlers, type, namespaces, origType,
++ elemData = dataPriv.hasData( elem ) && dataPriv.get( elem );
++
++ if ( !elemData || !( events = elemData.events ) ) {
++ return;
++ }
++
++ // Once for each type.namespace in types; type may be omitted
++ types = ( types || "" ).match( 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 ] || {};
++ 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 ( special.remove ) {
++ special.remove.call( elem, handleObj );
++ }
++ }
++ }
++
++ // 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 );
++ }
++
++ delete events[ type ];
++ }
++ }
++
++ // Remove data and the expando if it's no longer used
++ if ( jQuery.isEmptyObject( events ) ) {
++ dataPriv.remove( elem, "handle events" );
++ }
++ },
++
++ dispatch: function( event ) {
++
++ // Make a writable jQuery.Event from the native event object
++ event = jQuery.event.fix( event );
++
++ var i, j, ret, matched, handleObj,
++ handlerQueue = [],
++ args = slice.call( arguments ),
++ handlers = ( dataPriv.get( 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.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.rnamespace || event.rnamespace.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();
++ }
++ }
++ }
++ }
++ }
++
++ // Call the postDispatch hook for the mapped type
++ if ( special.postDispatch ) {
++ special.postDispatch.call( this, event );
++ }
++
++ return event.result;
++ },
++
++ handlers: function( event, handlers ) {
++ var i, matches, sel, handleObj,
++ handlerQueue = [],
++ delegateCount = handlers.delegateCount,
++ cur = event.target;
++
++ // Support (at least): Chrome, IE9
++ // Find delegate handlers
++ // Black-hole SVG <use> instance trees (#13180)
++ //
++ // Support: Firefox<=42+
++ // Avoid non-left-click in FF but don't block IE radio events (#3861, gh-2343)
++ if ( delegateCount && cur.nodeType &&
++ ( event.type !== "click" || isNaN( event.button ) || event.button < 1 ) ) {
++
++ for ( ; cur !== this; cur = cur.parentNode || this ) {
++
++ // 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 ) > -1 :
++ 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;
++ },
++
++ // Includes some event props shared by KeyEvent and MouseEvent
++ props: ( "altKey bubbles cancelable ctrlKey currentTarget detail 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 offsetX offsetY pageX pageY " +
++ "screenX screenY toElement" ).split( " " ),
++ filter: function( event, original ) {
++ var eventDoc, doc, body,
++ button = original.button;
++
++ // 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 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;
++ }
++ },
++
++ fix: function( event ) {
++ if ( event[ jQuery.expando ] ) {
++ return event;
++ }
++
++ // 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 ];
++ }
++
++ // Support: Cordova 2.5 (WebKit) (#13255)
++ // All events should have a target; Cordova deviceready doesn't
++ if ( !event.target ) {
++ event.target = document;
++ }
++
++ // Support: Safari 6.0+, Chrome<28
++ // Target should not be a text node (#504, #13143)
++ if ( event.target.nodeType === 3 ) {
++ event.target = event.target.parentNode;
++ }
++
++ return fixHook.filter ? fixHook.filter( event, originalEvent ) : event;
++ },
++
++ special: {
++ 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 ) {
++ this.focus();
++ return false;
++ }
++ },
++ delegateType: "focusin"
++ },
++ blur: {
++ trigger: function() {
++ if ( this === safeActiveElement() && this.blur ) {
++ this.blur();
++ return false;
++ }
++ },
++ delegateType: "focusout"
++ },
++ click: {
++
++ // For checkbox, fire native event so checked state will be right
++ trigger: function() {
++ if ( this.type === "checkbox" && this.click && jQuery.nodeName( this, "input" ) ) {
++ 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: {
++ postDispatch: function( event ) {
++
++ // Support: Firefox 20+
++ // Firefox doesn't alert if the returnValue field is not set.
++ if ( event.result !== undefined && event.originalEvent ) {
++ event.originalEvent.returnValue = event.result;
++ }
++ }
++ }
++ }
++};
++
++jQuery.removeEvent = function( elem, type, handle ) {
++
++ // This "if" is needed for plain objects
++ if ( elem.removeEventListener ) {
++ elem.removeEventListener( type, handle );
++ }
++};
++
++jQuery.Event = function( src, props ) {
++
++ // Allow instantiation without the 'new' keyword
++ if ( !( this instanceof jQuery.Event ) ) {
++ return new jQuery.Event( src, props );
++ }
++
++ // Event object
++ if ( src && src.type ) {
++ this.originalEvent = src;
++ this.type = src.type;
++
++ // Events bubbling up the document may have been marked as prevented
++ // by a handler lower down the tree; reflect the correct value.
++ this.isDefaultPrevented = src.defaultPrevented ||
++ src.defaultPrevented === undefined &&
++
++ // Support: Android<4.0
++ src.returnValue === false ?
++ returnTrue :
++ returnFalse;
++
++ // Event type
++ } else {
++ this.type = src;
++ }
++
++ // Put explicitly provided properties onto the event object
++ if ( props ) {
++ jQuery.extend( this, props );
++ }
++
++ // 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;
++};
++
++// 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 = {
++ constructor: jQuery.Event,
++ isDefaultPrevented: returnFalse,
++ isPropagationStopped: returnFalse,
++ isImmediatePropagationStopped: returnFalse,
++
++ preventDefault: function() {
++ var e = this.originalEvent;
++
++ this.isDefaultPrevented = returnTrue;
++
++ if ( e ) {
++ e.preventDefault();
++ }
++ },
++ stopPropagation: function() {
++ var e = this.originalEvent;
++
++ this.isPropagationStopped = returnTrue;
++
++ if ( e ) {
++ e.stopPropagation();
++ }
++ },
++ stopImmediatePropagation: function() {
++ var e = this.originalEvent;
++
++ this.isImmediatePropagationStopped = returnTrue;
++
++ if ( e ) {
++ e.stopImmediatePropagation();
++ }
++
++ this.stopPropagation();
++ }
++};
++
++// Create mouseenter/leave events using mouseover/out and event-time checks
++// so that event delegation works in jQuery.
++// Do the same for pointerenter/pointerleave and pointerover/pointerout
++//
++// Support: Safari 7 only
++// Safari sends mouseenter too often; see:
++// https://code.google.com/p/chromium/issues/detail?id=470258
++// for the description of the bug (it existed in older Chrome versions as well).
++jQuery.each( {
++ mouseenter: "mouseover",
++ mouseleave: "mouseout",
++ pointerenter: "pointerover",
++ pointerleave: "pointerout"
++}, function( orig, fix ) {
++ jQuery.event.special[ orig ] = {
++ delegateType: fix,
++ bindType: fix,
++
++ handle: function( event ) {
++ var ret,
++ target = this,
++ related = event.relatedTarget,
++ handleObj = event.handleObj;
++
++ // For mouseenter/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;
++ }
++ };
++} );
++
++jQuery.fn.extend( {
++ on: function( types, selector, data, fn ) {
++ return on( this, types, selector, data, fn );
++ },
++ one: function( types, selector, data, fn ) {
++ return on( this, 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 );
++ } );
++ }
++} );
++
++
++var
++ rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,
++
++ // Support: IE 10-11, Edge 10240+
++ // In IE/Edge using regex groups here causes severe slowdowns.
++ // See https://connect.microsoft.com/IE/feedback/details/1736512/
++ rnoInnerhtml = /<script|<style|<link/i,
++
++ // checked="checked" or checked
++ rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
++ rscriptTypeMasked = /^true\/(.*)/,
++ rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;
++
++// Manipulating tables requires a tbody
++function manipulationTarget( elem, content ) {
++ return jQuery.nodeName( elem, "table" ) &&
++ jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ?
++
++ elem.getElementsByTagName( "tbody" )[ 0 ] ||
++ elem.appendChild( elem.ownerDocument.createElement( "tbody" ) ) :
++ elem;
++}
++
++// Replace/restore the type attribute of script elements for safe DOM manipulation
++function disableScript( elem ) {
++ elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type;
++ return elem;
++}
++function restoreScript( elem ) {
++ var match = rscriptTypeMasked.exec( elem.type );
++
++ if ( match ) {
++ elem.type = match[ 1 ];
++ } else {
++ elem.removeAttribute( "type" );
++ }
++
++ return elem;
++}
++
++function cloneCopyEvent( src, dest ) {
++ var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events;
++
++ if ( dest.nodeType !== 1 ) {
++ return;
++ }
++
++ // 1. Copy private data: events, handlers, etc.
++ if ( dataPriv.hasData( src ) ) {
++ pdataOld = dataPriv.access( src );
++ pdataCur = dataPriv.set( dest, pdataOld );
++ events = pdataOld.events;
++
++ if ( events ) {
++ delete pdataCur.handle;
++ pdataCur.events = {};
++
++ for ( type in events ) {
++ for ( i = 0, l = events[ type ].length; i < l; i++ ) {
++ jQuery.event.add( dest, type, events[ type ][ i ] );
++ }
++ }
++ }
++ }
++
++ // 2. Copy user data
++ if ( dataUser.hasData( src ) ) {
++ udataOld = dataUser.access( src );
++ udataCur = jQuery.extend( {}, udataOld );
++
++ dataUser.set( dest, udataCur );
++ }
++}
++
++// Fix IE bugs, see support tests
++function fixInput( src, dest ) {
++ var nodeName = dest.nodeName.toLowerCase();
++
++ // Fails to persist the checked state of a cloned checkbox or radio button.
++ if ( nodeName === "input" && rcheckableType.test( src.type ) ) {
++ dest.checked = src.checked;
++
++ // Fails to return the selected option to the default selected state when cloning options
++ } else if ( nodeName === "input" || nodeName === "textarea" ) {
++ dest.defaultValue = src.defaultValue;
++ }
++}
++
++function domManip( collection, args, callback, ignored ) {
++
++ // Flatten any nested arrays
++ args = concat.apply( [], args );
++
++ var fragment, first, scripts, hasScripts, node, doc,
++ i = 0,
++ l = collection.length,
++ iNoClone = l - 1,
++ value = args[ 0 ],
++ isFunction = jQuery.isFunction( value );
++
++ // We can't cloneNode fragments that contain checked, in WebKit
++ if ( isFunction ||
++ ( l > 1 && typeof value === "string" &&
++ !support.checkClone && rchecked.test( value ) ) ) {
++ return collection.each( function( index ) {
++ var self = collection.eq( index );
++ if ( isFunction ) {
++ args[ 0 ] = value.call( this, index, self.html() );
++ }
++ domManip( self, args, callback, ignored );
++ } );
++ }
++
++ if ( l ) {
++ fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored );
++ first = fragment.firstChild;
++
++ if ( fragment.childNodes.length === 1 ) {
++ fragment = first;
++ }
++
++ // Require either new content or an interest in ignored elements to invoke the callback
++ if ( first || ignored ) {
++ scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
++ hasScripts = scripts.length;
++
++ // Use the original fragment for the last item
++ // instead of the first because it can end up
++ // being emptied incorrectly in certain situations (#8070).
++ for ( ; i < l; i++ ) {
++ node = fragment;
++
++ if ( i !== iNoClone ) {
++ node = jQuery.clone( node, true, true );
++
++ // Keep references to cloned scripts for later restoration
++ if ( hasScripts ) {
++
++ // Support: Android<4.1, PhantomJS<2
++ // push.apply(_, arraylike) throws on ancient WebKit
++ jQuery.merge( scripts, getAll( node, "script" ) );
++ }
++ }
++
++ callback.call( collection[ i ], node, i );
++ }
++
++ if ( hasScripts ) {
++ doc = scripts[ scripts.length - 1 ].ownerDocument;
++
++ // Reenable scripts
++ jQuery.map( scripts, restoreScript );
++
++ // Evaluate executable scripts on first document insertion
++ for ( i = 0; i < hasScripts; i++ ) {
++ node = scripts[ i ];
++ if ( rscriptType.test( node.type || "" ) &&
++ !dataPriv.access( node, "globalEval" ) &&
++ jQuery.contains( doc, node ) ) {
++
++ if ( node.src ) {
++
++ // Optional AJAX dependency, but won't run scripts if not present
++ if ( jQuery._evalUrl ) {
++ jQuery._evalUrl( node.src );
++ }
++ } else {
++ jQuery.globalEval( node.textContent.replace( rcleanScript, "" ) );
++ }
++ }
++ }
++ }
++ }
++ }
++
++ return collection;
++}
++
++function remove( elem, selector, keepData ) {
++ var node,
++ nodes = selector ? jQuery.filter( selector, elem ) : elem,
++ i = 0;
++
++ for ( ; ( node = nodes[ i ] ) != null; i++ ) {
++ if ( !keepData && node.nodeType === 1 ) {
++ jQuery.cleanData( getAll( node ) );
++ }
++
++ if ( node.parentNode ) {
++ if ( keepData && jQuery.contains( node.ownerDocument, node ) ) {
++ setGlobalEval( getAll( node, "script" ) );
++ }
++ node.parentNode.removeChild( node );
++ }
++ }
++
++ return elem;
++}
++
++jQuery.extend( {
++ htmlPrefilter: function( html ) {
++ return html.replace( rxhtmlTag, "<$1></$2>" );
++ },
++
++ clone: function( elem, dataAndEvents, deepDataAndEvents ) {
++ var i, l, srcElements, destElements,
++ clone = elem.cloneNode( true ),
++ inPage = jQuery.contains( elem.ownerDocument, elem );
++
++ // Fix IE cloning issues
++ if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) &&
++ !jQuery.isXMLDoc( elem ) ) {
++
++ // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2
++ destElements = getAll( clone );
++ srcElements = getAll( elem );
++
++ for ( i = 0, l = srcElements.length; i < l; i++ ) {
++ fixInput( srcElements[ i ], destElements[ i ] );
++ }
++ }
++
++ // Copy the events from the original to the clone
++ if ( dataAndEvents ) {
++ if ( deepDataAndEvents ) {
++ srcElements = srcElements || getAll( elem );
++ destElements = destElements || getAll( clone );
++
++ for ( i = 0, l = srcElements.length; i < l; i++ ) {
++ cloneCopyEvent( srcElements[ i ], destElements[ i ] );
++ }
++ } else {
++ cloneCopyEvent( elem, clone );
++ }
++ }
++
++ // Preserve script evaluation history
++ destElements = getAll( clone, "script" );
++ if ( destElements.length > 0 ) {
++ setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
++ }
++
++ // Return the cloned set
++ return clone;
++ },
++
++ cleanData: function( elems ) {
++ var data, elem, type,
++ special = jQuery.event.special,
++ i = 0;
++
++ for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) {
++ if ( acceptData( elem ) ) {
++ if ( ( data = elem[ dataPriv.expando ] ) ) {
++ if ( data.events ) {
++ for ( type in data.events ) {
++ if ( special[ type ] ) {
++ jQuery.event.remove( elem, type );
++
++ // This is a shortcut to avoid jQuery.event.remove's overhead
++ } else {
++ jQuery.removeEvent( elem, type, data.handle );
++ }
++ }
++ }
++
++ // Support: Chrome <= 35-45+
++ // Assign undefined instead of using delete, see Data#remove
++ elem[ dataPriv.expando ] = undefined;
++ }
++ if ( elem[ dataUser.expando ] ) {
++
++ // Support: Chrome <= 35-45+
++ // Assign undefined instead of using delete, see Data#remove
++ elem[ dataUser.expando ] = undefined;
++ }
++ }
++ }
++ }
++} );
++
++jQuery.fn.extend( {
++
++ // Keep domManip exposed until 3.0 (gh-2225)
++ domManip: domManip,
++
++ detach: function( selector ) {
++ return remove( this, selector, true );
++ },
++
++ remove: function( selector ) {
++ return remove( this, selector );
++ },
++
++ text: function( value ) {
++ return access( this, function( value ) {
++ return value === undefined ?
++ jQuery.text( this ) :
++ this.empty().each( function() {
++ if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
++ this.textContent = value;
++ }
++ } );
++ }, null, value, arguments.length );
++ },
++
++ append: function() {
++ return domManip( this, arguments, function( elem ) {
++ if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
++ var target = manipulationTarget( this, elem );
++ target.appendChild( elem );
++ }
++ } );
++ },
++
++ prepend: function() {
++ return domManip( this, arguments, function( elem ) {
++ if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
++ var target = manipulationTarget( this, elem );
++ target.insertBefore( elem, target.firstChild );
++ }
++ } );
++ },
++
++ before: function() {
++ return domManip( this, arguments, function( elem ) {
++ if ( this.parentNode ) {
++ this.parentNode.insertBefore( elem, this );
++ }
++ } );
++ },
++
++ after: function() {
++ return domManip( this, arguments, function( elem ) {
++ if ( this.parentNode ) {
++ this.parentNode.insertBefore( elem, this.nextSibling );
++ }
++ } );
++ },
++
++ empty: function() {
++ var elem,
++ i = 0;
++
++ for ( ; ( elem = this[ i ] ) != null; i++ ) {
++ if ( elem.nodeType === 1 ) {
++
++ // Prevent memory leaks
++ jQuery.cleanData( getAll( elem, false ) );
++
++ // Remove any remaining nodes
++ elem.textContent = "";
++ }
++ }
++
++ return this;
++ },
++
++ clone: function( dataAndEvents, deepDataAndEvents ) {
++ dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
++ deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
++
++ return this.map( function() {
++ return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
++ } );
++ },
++
++ html: function( value ) {
++ return access( this, function( value ) {
++ var elem = this[ 0 ] || {},
++ i = 0,
++ l = this.length;
++
++ if ( value === undefined && elem.nodeType === 1 ) {
++ return elem.innerHTML;
++ }
++
++ // See if we can take a shortcut and just use innerHTML
++ if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
++ !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) {
++
++ value = jQuery.htmlPrefilter( value );
++
++ try {
++ for ( ; i < l; i++ ) {
++ elem = this[ i ] || {};
++
++ // Remove element nodes and prevent memory leaks
++ if ( elem.nodeType === 1 ) {
++ jQuery.cleanData( getAll( elem, false ) );
++ elem.innerHTML = value;
++ }
++ }
++
++ elem = 0;
++
++ // If using innerHTML throws an exception, use the fallback method
++ } catch ( e ) {}
++ }
++
++ if ( elem ) {
++ this.empty().append( value );
++ }
++ }, null, value, arguments.length );
++ },
++
++ replaceWith: function() {
++ var ignored = [];
++
++ // Make the changes, replacing each non-ignored context element with the new content
++ return domManip( this, arguments, function( elem ) {
++ var parent = this.parentNode;
++
++ if ( jQuery.inArray( this, ignored ) < 0 ) {
++ jQuery.cleanData( getAll( this ) );
++ if ( parent ) {
++ parent.replaceChild( elem, this );
++ }
++ }
++
++ // Force callback invocation
++ }, ignored );
++ }
++} );
++
++jQuery.each( {
++ appendTo: "append",
++ prependTo: "prepend",
++ insertBefore: "before",
++ insertAfter: "after",
++ replaceAll: "replaceWith"
++}, function( name, original ) {
++ jQuery.fn[ name ] = function( selector ) {
++ var elems,
++ ret = [],
++ insert = jQuery( selector ),
++ last = insert.length - 1,
++ i = 0;
++
++ for ( ; i <= last; i++ ) {
++ elems = i === last ? this : this.clone( true );
++ jQuery( insert[ i ] )[ original ]( elems );
++
++ // Support: QtWebKit
++ // .get() because push.apply(_, arraylike) throws
++ push.apply( ret, elems.get() );
++ }
++
++ return this.pushStack( ret );
++ };
++} );
++
++
++var iframe,
++ elemdisplay = {
++
++ // Support: Firefox
++ // We have to pre-define these values for FF (#10227)
++ HTML: "block",
++ BODY: "block"
++ };
++
++/**
++ * Retrieve the actual display of a element
++ * @param {String} name nodeName of the element
++ * @param {Object} doc Document object
++ */
++
++// Called only from within defaultDisplay
++function actualDisplay( name, doc ) {
++ var elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ),
++
++ display = jQuery.css( elem[ 0 ], "display" );
++
++ // We don't have any data stored on the element,
++ // so use "detach" method as fast way to get rid of the element
++ elem.detach();
++
++ return display;
++}
++
++/**
++ * Try to determine the default display value of an element
++ * @param {String} nodeName
++ */
++function defaultDisplay( nodeName ) {
++ var doc = document,
++ display = elemdisplay[ nodeName ];
++
++ if ( !display ) {
++ display = actualDisplay( nodeName, doc );
++
++ // If the simple way fails, read from inside an iframe
++ if ( display === "none" || !display ) {
++
++ // Use the already-created iframe if possible
++ iframe = ( iframe || jQuery( "<iframe frameborder='0' width='0' height='0'/>" ) )
++ .appendTo( doc.documentElement );
++
++ // Always write a new HTML skeleton so Webkit and Firefox don't choke on reuse
++ doc = iframe[ 0 ].contentDocument;
++
++ // Support: IE
++ doc.write();
++ doc.close();
++
++ display = actualDisplay( nodeName, doc );
++ iframe.detach();
++ }
++
++ // Store the correct default display
++ elemdisplay[ nodeName ] = display;
++ }
++
++ return display;
++}
++var rmargin = ( /^margin/ );
++
++var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );
++
++var getStyles = function( elem ) {
++
++ // Support: IE<=11+, Firefox<=30+ (#15098, #14150)
++ // IE throws on elements created in popups
++ // FF meanwhile throws on frame elements through "defaultView.getComputedStyle"
++ var view = elem.ownerDocument.defaultView;
++
++ if ( !view || !view.opener ) {
++ view = window;
++ }
++
++ return view.getComputedStyle( elem );
++ };
++
++var 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;
++};
++
++
++var documentElement = document.documentElement;
++
++
++
++( function() {
++ var pixelPositionVal, boxSizingReliableVal, pixelMarginRightVal, reliableMarginLeftVal,
++ container = document.createElement( "div" ),
++ div = document.createElement( "div" );
++
++ // Finish early in limited (non-browser) environments
++ if ( !div.style ) {
++ return;
++ }
++
++ // Support: IE9-11+
++ // Style of cloned element affects source element cloned (#8908)
++ div.style.backgroundClip = "content-box";
++ div.cloneNode( true ).style.backgroundClip = "";
++ support.clearCloneStyle = div.style.backgroundClip === "content-box";
++
++ container.style.cssText = "border:0;width:8px;height:0;top:0;left:-9999px;" +
++ "padding:0;margin-top:1px;position:absolute";
++ container.appendChild( div );
++
++ // Executing both pixelPosition & boxSizingReliable tests require only one layout
++ // so they're executed at the same time to save the second computation.
++ function computeStyleTests() {
++ div.style.cssText =
++
++ // Support: Firefox<29, Android 2.3
++ // Vendor-prefix box-sizing
++ "-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;" +
++ "position:relative;display:block;" +
++ "margin:auto;border:1px;padding:1px;" +
++ "top:1%;width:50%";
++ div.innerHTML = "";
++ documentElement.appendChild( container );
++
++ var divStyle = window.getComputedStyle( div );
++ pixelPositionVal = divStyle.top !== "1%";
++ reliableMarginLeftVal = divStyle.marginLeft === "2px";
++ boxSizingReliableVal = divStyle.width === "4px";
++
++ // Support: Android 4.0 - 4.3 only
++ // Some styles come back with percentage values, even though they shouldn't
++ div.style.marginRight = "50%";
++ pixelMarginRightVal = divStyle.marginRight === "4px";
++
++ documentElement.removeChild( container );
++ }
++
++ jQuery.extend( support, {
++ pixelPosition: function() {
++
++ // This test is executed only once but we still do memoizing
++ // since we can use the boxSizingReliable pre-computing.
++ // No need to check if the test was already performed, though.
++ computeStyleTests();
++ return pixelPositionVal;
++ },
++ boxSizingReliable: function() {
++ if ( boxSizingReliableVal == null ) {
++ computeStyleTests();
++ }
++ return boxSizingReliableVal;
++ },
++ pixelMarginRight: function() {
++
++ // Support: Android 4.0-4.3
++ // We're checking for boxSizingReliableVal here instead of pixelMarginRightVal
++ // since that compresses better and they're computed together anyway.
++ if ( boxSizingReliableVal == null ) {
++ computeStyleTests();
++ }
++ return pixelMarginRightVal;
++ },
++ reliableMarginLeft: function() {
++
++ // Support: IE <=8 only, Android 4.0 - 4.3 only, Firefox <=3 - 37
++ if ( boxSizingReliableVal == null ) {
++ computeStyleTests();
++ }
++ return reliableMarginLeftVal;
++ },
++ reliableMarginRight: function() {
++
++ // Support: Android 2.3
++ // Check if div with explicit width and no margin-right incorrectly
++ // gets computed margin-right based on width of container. (#3333)
++ // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
++ // This support function is only executed once so no memoizing is needed.
++ var ret,
++ marginDiv = div.appendChild( document.createElement( "div" ) );
++
++ // Reset CSS: box-sizing; display; margin; border; padding
++ marginDiv.style.cssText = div.style.cssText =
++
++ // Support: Android 2.3
++ // Vendor-prefix box-sizing
++ "-webkit-box-sizing:content-box;box-sizing:content-box;" +
++ "display:block;margin:0;border:0;padding:0";
++ marginDiv.style.marginRight = marginDiv.style.width = "0";
++ div.style.width = "1px";
++ documentElement.appendChild( container );
++
++ ret = !parseFloat( window.getComputedStyle( marginDiv ).marginRight );
++
++ documentElement.removeChild( container );
++ div.removeChild( marginDiv );
++
++ return ret;
++ }
++ } );
++} )();
++
++
++function curCSS( elem, name, computed ) {
++ var width, minWidth, maxWidth, ret,
++ style = elem.style;
++
++ computed = computed || getStyles( elem );
++ ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined;
++
++ // Support: Opera 12.1x only
++ // Fall back to style even without computed
++ // computed is undefined for elems on document fragments
++ if ( ( ret === "" || ret === undefined ) && !jQuery.contains( elem.ownerDocument, elem ) ) {
++ ret = jQuery.style( elem, name );
++ }
++
++ // Support: IE9
++ // getPropertyValue is only needed for .css('filter') (#12537)
++ if ( computed ) {
++
++ // A tribute to the "awesome hack by Dean Edwards"
++ // Android Browser returns percentage for some values,
++ // but width seems to be reliably pixels.
++ // This is against the CSSOM draft spec:
++ // http://dev.w3.org/csswg/cssom/#resolved-values
++ if ( !support.pixelMarginRight() && rnumnonpx.test( ret ) && rmargin.test( name ) ) {
++
++ // Remember the original values
++ width = style.width;
++ minWidth = style.minWidth;
++ maxWidth = style.maxWidth;
++
++ // Put in the new values to get a computed value out
++ style.minWidth = style.maxWidth = style.width = ret;
++ ret = computed.width;
++
++ // Revert the changed values
++ style.width = width;
++ style.minWidth = minWidth;
++ style.maxWidth = maxWidth;
++ }
++ }
++
++ return ret !== undefined ?
++
++ // Support: IE9-11+
++ // IE returns zIndex value as an integer.
++ ret + "" :
++ ret;
++}
++
++
++function addGetHookIf( conditionFn, hookFn ) {
++
++ // Define the hook, we'll check on the first run if it's really needed.
++ return {
++ get: function() {
++ if ( conditionFn() ) {
++
++ // Hook not needed (or it's not possible to use it due
++ // to missing dependency), remove it.
++ delete this.get;
++ return;
++ }
++
++ // Hook needed; redefine it so that the support test is not executed again.
++ return ( this.get = hookFn ).apply( this, arguments );
++ }
++ };
++}
++
++
++var
++
++ // Swappable if display is none or starts with table
++ // except "table", "table-cell", or "table-caption"
++ // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
++ rdisplayswap = /^(none|table(?!-c[ea]).+)/,
++
++ cssShow = { position: "absolute", visibility: "hidden", display: "block" },
++ cssNormalTransform = {
++ letterSpacing: "0",
++ fontWeight: "400"
++ },
++
++ cssPrefixes = [ "Webkit", "O", "Moz", "ms" ],
++ emptyStyle = document.createElement( "div" ).style;
++
++// Return a css property mapped to a potentially vendor prefixed property
++function vendorPropName( name ) {
++
++ // Shortcut for names that are not vendor prefixed
++ if ( name in emptyStyle ) {
++ return name;
++ }
++
++ // Check for vendor prefixed names
++ var capName = name[ 0 ].toUpperCase() + name.slice( 1 ),
++ i = cssPrefixes.length;
++
++ while ( i-- ) {
++ name = cssPrefixes[ i ] + capName;
++ if ( name in emptyStyle ) {
++ return name;
++ }
++ }
++}
++
++function setPositiveNumber( elem, value, subtract ) {
++
++ // Any relative (+/-) values have already been
++ // normalized at this point
++ var matches = rcssNum.exec( value );
++ return matches ?
++
++ // Guard against undefined "subtract", e.g., when used as in cssHooks
++ Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) :
++ value;
++}
++
++function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
++ var i = extra === ( isBorderBox ? "border" : "content" ) ?
++
++ // If we already have the right measurement, avoid augmentation
++ 4 :
++
++ // Otherwise initialize for horizontal or vertical properties
++ name === "width" ? 1 : 0,
++
++ val = 0;
++
++ for ( ; i < 4; i += 2 ) {
++
++ // Both box models exclude margin, so add it if we want it
++ if ( extra === "margin" ) {
++ val += jQuery.css( elem, extra + cssExpand[ i ], true, styles );
++ }
++
++ if ( isBorderBox ) {
++
++ // border-box includes padding, so remove it if we want content
++ if ( extra === "content" ) {
++ val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
++ }
++
++ // At this point, extra isn't border nor margin, so remove border
++ if ( extra !== "margin" ) {
++ val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
++ }
++ } else {
++
++ // At this point, extra isn't content, so add padding
++ val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
++
++ // At this point, extra isn't content nor padding, so add border
++ if ( extra !== "padding" ) {
++ val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
++ }
++ }
++ }
++
++ return val;
++}
++
++function getWidthOrHeight( elem, name, extra ) {
++
++ // Start with offset property, which is equivalent to the border-box value
++ var valueIsBorderBox = true,
++ val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
++ styles = getStyles( elem ),
++ isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
++
++ // Support: IE11 only
++ // In IE 11 fullscreen elements inside of an iframe have
++ // 100x too small dimensions (gh-1764).
++ if ( document.msFullscreenElement && window.top !== window ) {
++
++ // Support: IE11 only
++ // Running getBoundingClientRect on a disconnected node
++ // in IE throws an error.
++ if ( elem.getClientRects().length ) {
++ val = Math.round( elem.getBoundingClientRect()[ name ] * 100 );
++ }
++ }
++
++ // Some non-html elements return undefined for offsetWidth, so check for null/undefined
++ // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285
++ // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668
++ if ( val <= 0 || val == null ) {
++
++ // Fall back to computed then uncomputed css if necessary
++ val = curCSS( elem, name, styles );
++ if ( val < 0 || val == null ) {
++ val = elem.style[ name ];
++ }
++
++ // Computed unit is not pixels. Stop here and return.
++ if ( rnumnonpx.test( val ) ) {
++ return val;
++ }
++
++ // Check for style in case a browser which returns unreliable values
++ // for getComputedStyle silently falls back to the reliable elem.style
++ valueIsBorderBox = isBorderBox &&
++ ( support.boxSizingReliable() || val === elem.style[ name ] );
++
++ // Normalize "", auto, and prepare for extra
++ val = parseFloat( val ) || 0;
++ }
++
++ // Use the active box-sizing model to add/subtract irrelevant styles
++ return ( val +
++ augmentWidthOrHeight(
++ elem,
++ name,
++ extra || ( isBorderBox ? "border" : "content" ),
++ valueIsBorderBox,
++ styles
++ )
++ ) + "px";
++}
++
++function showHide( elements, show ) {
++ var display, elem, hidden,
++ values = [],
++ index = 0,
++ length = elements.length;
++
++ for ( ; index < length; index++ ) {
++ elem = elements[ index ];
++ if ( !elem.style ) {
++ continue;
++ }
++
++ values[ index ] = dataPriv.get( elem, "olddisplay" );
++ display = elem.style.display;
++ if ( show ) {
++
++ // Reset the inline display of this element to learn if it is
++ // being hidden by cascaded rules or not
++ if ( !values[ index ] && display === "none" ) {
++ elem.style.display = "";
++ }
++
++ // Set elements which have been overridden with display: none
++ // in a stylesheet to whatever the default browser style is
++ // for such an element
++ if ( elem.style.display === "" && isHidden( elem ) ) {
++ values[ index ] = dataPriv.access(
++ elem,
++ "olddisplay",
++ defaultDisplay( elem.nodeName )
++ );
++ }
++ } else {
++ hidden = isHidden( elem );
++
++ if ( display !== "none" || !hidden ) {
++ dataPriv.set(
++ elem,
++ "olddisplay",
++ hidden ? display : jQuery.css( elem, "display" )
++ );
++ }
++ }
++ }
++
++ // Set the display of most of the elements in a second loop
++ // to avoid the constant reflow
++ for ( index = 0; index < length; index++ ) {
++ elem = elements[ index ];
++ if ( !elem.style ) {
++ continue;
++ }
++ if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
++ elem.style.display = show ? values[ index ] || "" : "none";
++ }
++ }
++
++ return elements;
++}
++
++jQuery.extend( {
++
++ // Add in style property hooks for overriding the default
++ // behavior of getting and setting a style property
++ cssHooks: {
++ opacity: {
++ get: function( elem, computed ) {
++ if ( computed ) {
++
++ // We should always get a number back from opacity
++ var ret = curCSS( elem, "opacity" );
++ return ret === "" ? "1" : ret;
++ }
++ }
++ }
++ },
++
++ // Don't automatically add "px" to these possibly-unitless properties
++ cssNumber: {
++ "animationIterationCount": true,
++ "columnCount": true,
++ "fillOpacity": true,
++ "flexGrow": true,
++ "flexShrink": true,
++ "fontWeight": true,
++ "lineHeight": true,
++ "opacity": true,
++ "order": true,
++ "orphans": true,
++ "widows": true,
++ "zIndex": true,
++ "zoom": true
++ },
++
++ // Add in properties whose names you wish to fix before
++ // setting or getting the value
++ cssProps: {
++ "float": "cssFloat"
++ },
++
++ // Get and set the style property on a DOM Node
++ style: function( elem, name, value, extra ) {
++
++ // Don't set styles on text and comment nodes
++ if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
++ return;
++ }
++
++ // Make sure that we're working with the right name
++ var ret, type, hooks,
++ origName = jQuery.camelCase( name ),
++ style = elem.style;
++
++ name = jQuery.cssProps[ origName ] ||
++ ( jQuery.cssProps[ origName ] = vendorPropName( origName ) || origName );
++
++ // Gets hook for the prefixed version, then unprefixed version
++ hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
++
++ // Check if we're setting a value
++ if ( value !== undefined ) {
++ type = typeof value;
++
++ // Convert "+=" or "-=" to relative numbers (#7345)
++ if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) {
++ value = adjustCSS( elem, name, ret );
++
++ // Fixes bug #9237
++ type = "number";
++ }
++
++ // Make sure that null and NaN values aren't set (#7116)
++ if ( value == null || value !== value ) {
++ return;
++ }
++
++ // If a number was passed in, add the unit (except for certain CSS properties)
++ if ( type === "number" ) {
++ value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" );
++ }
++
++ // Support: IE9-11+
++ // background-* props affect original clone's values
++ if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) {
++ style[ name ] = "inherit";
++ }
++
++ // If a hook was provided, use that value, otherwise just set the specified value
++ if ( !hooks || !( "set" in hooks ) ||
++ ( value = hooks.set( elem, value, extra ) ) !== undefined ) {
++
++ style[ name ] = value;
++ }
++
++ } else {
++
++ // If a hook was provided get the non-computed value from there
++ if ( hooks && "get" in hooks &&
++ ( ret = hooks.get( elem, false, extra ) ) !== undefined ) {
++
++ return ret;
++ }
++
++ // Otherwise just get the value from the style object
++ return style[ name ];
++ }
++ },
++
++ css: function( elem, name, extra, styles ) {
++ var val, num, hooks,
++ origName = jQuery.camelCase( name );
++
++ // Make sure that we're working with the right name
++ name = jQuery.cssProps[ origName ] ||
++ ( jQuery.cssProps[ origName ] = vendorPropName( origName ) || origName );
++
++ // Try prefixed name followed by the unprefixed name
++ hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
++
++ // If a hook was provided get the computed value from there
++ if ( hooks && "get" in hooks ) {
++ val = hooks.get( elem, true, extra );
++ }
++
++ // Otherwise, if a way to get the computed value exists, use that
++ if ( val === undefined ) {
++ val = curCSS( elem, name, styles );
++ }
++
++ // Convert "normal" to computed value
++ if ( val === "normal" && name in cssNormalTransform ) {
++ val = cssNormalTransform[ name ];
++ }
++
++ // Make numeric if forced or a qualifier was provided and val looks numeric
++ if ( extra === "" || extra ) {
++ num = parseFloat( val );
++ return extra === true || isFinite( num ) ? num || 0 : val;
++ }
++ return val;
++ }
++} );
++
++jQuery.each( [ "height", "width" ], function( i, name ) {
++ jQuery.cssHooks[ name ] = {
++ get: function( elem, computed, extra ) {
++ if ( computed ) {
++
++ // Certain elements can have dimension info if we invisibly show them
++ // but it must have a current display style that would benefit
++ return rdisplayswap.test( jQuery.css( elem, "display" ) ) &&
++ elem.offsetWidth === 0 ?
++ swap( elem, cssShow, function() {
++ return getWidthOrHeight( elem, name, extra );
++ } ) :
++ getWidthOrHeight( elem, name, extra );
++ }
++ },
++
++ set: function( elem, value, extra ) {
++ var matches,
++ styles = extra && getStyles( elem ),
++ subtract = extra && augmentWidthOrHeight(
++ elem,
++ name,
++ extra,
++ jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
++ styles
++ );
++
++ // Convert to pixels if value adjustment is needed
++ if ( subtract && ( matches = rcssNum.exec( value ) ) &&
++ ( matches[ 3 ] || "px" ) !== "px" ) {
++
++ elem.style[ name ] = value;
++ value = jQuery.css( elem, name );
++ }
++
++ return setPositiveNumber( elem, value, subtract );
++ }
++ };
++} );
++
++jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft,
++ function( elem, computed ) {
++ if ( computed ) {
++ return ( parseFloat( curCSS( elem, "marginLeft" ) ) ||
++ elem.getBoundingClientRect().left -
++ swap( elem, { marginLeft: 0 }, function() {
++ return elem.getBoundingClientRect().left;
++ } )
++ ) + "px";
++ }
++ }
++);
++
++// Support: Android 2.3
++jQuery.cssHooks.marginRight = addGetHookIf( support.reliableMarginRight,
++ function( elem, computed ) {
++ if ( computed ) {
++ return swap( elem, { "display": "inline-block" },
++ curCSS, [ elem, "marginRight" ] );
++ }
++ }
++);
++
++// These hooks are used by animate to expand properties
++jQuery.each( {
++ margin: "",
++ padding: "",
++ border: "Width"
++}, function( prefix, suffix ) {
++ jQuery.cssHooks[ prefix + suffix ] = {
++ expand: function( value ) {
++ var i = 0,
++ expanded = {},
++
++ // Assumes a single number if not a string
++ parts = typeof value === "string" ? value.split( " " ) : [ value ];
++
++ for ( ; i < 4; i++ ) {
++ expanded[ prefix + cssExpand[ i ] + suffix ] =
++ parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
++ }
++
++ return expanded;
++ }
++ };
++
++ if ( !rmargin.test( prefix ) ) {
++ jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
++ }
++} );
++
++jQuery.fn.extend( {
++ css: function( name, value ) {
++ return access( this, function( elem, name, value ) {
++ var styles, len,
++ map = {},
++ i = 0;
++
++ if ( jQuery.isArray( name ) ) {
++ styles = getStyles( elem );
++ len = name.length;
++
++ for ( ; i < len; i++ ) {
++ map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
++ }
++
++ return map;
++ }
++
++ return value !== undefined ?
++ jQuery.style( elem, name, value ) :
++ jQuery.css( elem, name );
++ }, name, value, arguments.length > 1 );
++ },
++ show: function() {
++ return showHide( this, true );
++ },
++ hide: function() {
++ return showHide( this );
++ },
++ toggle: function( state ) {
++ if ( typeof state === "boolean" ) {
++ return state ? this.show() : this.hide();
++ }
++
++ return this.each( function() {
++ if ( isHidden( this ) ) {
++ jQuery( this ).show();
++ } else {
++ jQuery( this ).hide();
++ }
++ } );
++ }
++} );
++
++
++function Tween( elem, options, prop, end, easing ) {
++ return new Tween.prototype.init( elem, options, prop, end, easing );
++}
++jQuery.Tween = Tween;
++
++Tween.prototype = {
++ constructor: Tween,
++ init: function( elem, options, prop, end, easing, unit ) {
++ this.elem = elem;
++ this.prop = prop;
++ this.easing = easing || jQuery.easing._default;
++ this.options = options;
++ this.start = this.now = this.cur();
++ this.end = end;
++ this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
++ },
++ cur: function() {
++ var hooks = Tween.propHooks[ this.prop ];
++
++ return hooks && hooks.get ?
++ hooks.get( this ) :
++ Tween.propHooks._default.get( this );
++ },
++ run: function( percent ) {
++ var eased,
++ hooks = Tween.propHooks[ this.prop ];
++
++ if ( this.options.duration ) {
++ this.pos = eased = jQuery.easing[ this.easing ](
++ percent, this.options.duration * percent, 0, 1, this.options.duration
++ );
++ } else {
++ this.pos = eased = percent;
++ }
++ this.now = ( this.end - this.start ) * eased + this.start;
++
++ if ( this.options.step ) {
++ this.options.step.call( this.elem, this.now, this );
++ }
++
++ if ( hooks && hooks.set ) {
++ hooks.set( this );
++ } else {
++ Tween.propHooks._default.set( this );
++ }
++ return this;
++ }
++};
++
++Tween.prototype.init.prototype = Tween.prototype;
++
++Tween.propHooks = {
++ _default: {
++ get: function( tween ) {
++ var result;
++
++ // Use a property on the element directly when it is not a DOM element,
++ // or when there is no matching style property that exists.
++ if ( tween.elem.nodeType !== 1 ||
++ tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) {
++ return tween.elem[ tween.prop ];
++ }
++
++ // Passing an empty string as a 3rd parameter to .css will automatically
++ // attempt a parseFloat and fallback to a string if the parse fails.
++ // Simple values such as "10px" are parsed to Float;
++ // complex values such as "rotate(1rad)" are returned as-is.
++ result = jQuery.css( tween.elem, tween.prop, "" );
++
++ // Empty strings, null, undefined and "auto" are converted to 0.
++ return !result || result === "auto" ? 0 : result;
++ },
++ set: function( tween ) {
++
++ // Use step hook for back compat.
++ // Use cssHook if its there.
++ // Use .style if available and use plain properties where available.
++ if ( jQuery.fx.step[ tween.prop ] ) {
++ jQuery.fx.step[ tween.prop ]( tween );
++ } else if ( tween.elem.nodeType === 1 &&
++ ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null ||
++ jQuery.cssHooks[ tween.prop ] ) ) {
++ jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
++ } else {
++ tween.elem[ tween.prop ] = tween.now;
++ }
++ }
++ }
++};
++
++// Support: IE9
++// Panic based approach to setting things on disconnected nodes
++Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
++ set: function( tween ) {
++ if ( tween.elem.nodeType && tween.elem.parentNode ) {
++ tween.elem[ tween.prop ] = tween.now;
++ }
++ }
++};
++
++jQuery.easing = {
++ linear: function( p ) {
++ return p;
++ },
++ swing: function( p ) {
++ return 0.5 - Math.cos( p * Math.PI ) / 2;
++ },
++ _default: "swing"
++};
++
++jQuery.fx = Tween.prototype.init;
++
++// Back Compat <1.8 extension point
++jQuery.fx.step = {};
++
++
++
++
++var
++ fxNow, timerId,
++ rfxtypes = /^(?:toggle|show|hide)$/,
++ rrun = /queueHooks$/;
++
++// Animations created synchronously will run synchronously
++function createFxNow() {
++ window.setTimeout( function() {
++ fxNow = undefined;
++ } );
++ return ( fxNow = jQuery.now() );
++}
++
++// Generate parameters to create a standard animation
++function genFx( type, includeWidth ) {
++ var which,
++ i = 0,
++ attrs = { height: type };
++
++ // If we include width, step value is 1 to do all cssExpand values,
++ // otherwise step value is 2 to skip over Left and Right
++ includeWidth = includeWidth ? 1 : 0;
++ for ( ; i < 4 ; i += 2 - includeWidth ) {
++ which = cssExpand[ i ];
++ attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
++ }
++
++ if ( includeWidth ) {
++ attrs.opacity = attrs.width = type;
++ }
++
++ return attrs;
++}
++
++function createTween( value, prop, animation ) {
++ var tween,
++ collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ),
++ index = 0,
++ length = collection.length;
++ for ( ; index < length; index++ ) {
++ if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) {
++
++ // We're done with this property
++ return tween;
++ }
++ }
++}
++
++function defaultPrefilter( elem, props, opts ) {
++ /* jshint validthis: true */
++ var prop, value, toggle, tween, hooks, oldfire, display, checkDisplay,
++ anim = this,
++ orig = {},
++ style = elem.style,
++ hidden = elem.nodeType && isHidden( elem ),
++ dataShow = dataPriv.get( elem, "fxshow" );
++
++ // Handle queue: false promises
++ if ( !opts.queue ) {
++ hooks = jQuery._queueHooks( elem, "fx" );
++ if ( hooks.unqueued == null ) {
++ hooks.unqueued = 0;
++ oldfire = hooks.empty.fire;
++ hooks.empty.fire = function() {
++ if ( !hooks.unqueued ) {
++ oldfire();
++ }
++ };
++ }
++ hooks.unqueued++;
++
++ anim.always( function() {
++
++ // Ensure the complete handler is called before this completes
++ anim.always( function() {
++ hooks.unqueued--;
++ if ( !jQuery.queue( elem, "fx" ).length ) {
++ hooks.empty.fire();
++ }
++ } );
++ } );
++ }
++
++ // Height/width overflow pass
++ if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
++
++ // Make sure that nothing sneaks out
++ // Record all 3 overflow attributes because IE9-10 do not
++ // change the overflow attribute when overflowX and
++ // overflowY are set to the same value
++ opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
++
++ // Set display property to inline-block for height/width
++ // animations on inline elements that are having width/height animated
++ display = jQuery.css( elem, "display" );
++
++ // Test default display if display is currently "none"
++ checkDisplay = display === "none" ?
++ dataPriv.get( elem, "olddisplay" ) || defaultDisplay( elem.nodeName ) : display;
++
++ if ( checkDisplay === "inline" && jQuery.css( elem, "float" ) === "none" ) {
++ style.display = "inline-block";
++ }
++ }
++
++ if ( opts.overflow ) {
++ style.overflow = "hidden";
++ anim.always( function() {
++ style.overflow = opts.overflow[ 0 ];
++ style.overflowX = opts.overflow[ 1 ];
++ style.overflowY = opts.overflow[ 2 ];
++ } );
++ }
++
++ // show/hide pass
++ for ( prop in props ) {
++ value = props[ prop ];
++ if ( rfxtypes.exec( value ) ) {
++ delete props[ prop ];
++ toggle = toggle || value === "toggle";
++ if ( value === ( hidden ? "hide" : "show" ) ) {
++
++ // If there is dataShow left over from a stopped hide or show
++ // and we are going to proceed with show, we should pretend to be hidden
++ if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) {
++ hidden = true;
++ } else {
++ continue;
++ }
++ }
++ orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
++
++ // Any non-fx value stops us from restoring the original display value
++ } else {
++ display = undefined;
++ }
++ }
++
++ if ( !jQuery.isEmptyObject( orig ) ) {
++ if ( dataShow ) {
++ if ( "hidden" in dataShow ) {
++ hidden = dataShow.hidden;
++ }
++ } else {
++ dataShow = dataPriv.access( elem, "fxshow", {} );
++ }
++
++ // Store state if its toggle - enables .stop().toggle() to "reverse"
++ if ( toggle ) {
++ dataShow.hidden = !hidden;
++ }
++ if ( hidden ) {
++ jQuery( elem ).show();
++ } else {
++ anim.done( function() {
++ jQuery( elem ).hide();
++ } );
++ }
++ anim.done( function() {
++ var prop;
++
++ dataPriv.remove( elem, "fxshow" );
++ for ( prop in orig ) {
++ jQuery.style( elem, prop, orig[ prop ] );
++ }
++ } );
++ for ( prop in orig ) {
++ tween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );
++
++ if ( !( prop in dataShow ) ) {
++ dataShow[ prop ] = tween.start;
++ if ( hidden ) {
++ tween.end = tween.start;
++ tween.start = prop === "width" || prop === "height" ? 1 : 0;
++ }
++ }
++ }
++
++ // If this is a noop like .hide().hide(), restore an overwritten display value
++ } else if ( ( display === "none" ? defaultDisplay( elem.nodeName ) : display ) === "inline" ) {
++ style.display = display;
++ }
++}
++
++function propFilter( props, specialEasing ) {
++ var index, name, easing, value, hooks;
++
++ // camelCase, specialEasing and expand cssHook pass
++ for ( index in props ) {
++ name = jQuery.camelCase( index );
++ easing = specialEasing[ name ];
++ value = props[ index ];
++ if ( jQuery.isArray( value ) ) {
++ easing = value[ 1 ];
++ value = props[ index ] = value[ 0 ];
++ }
++
++ if ( index !== name ) {
++ props[ name ] = value;
++ delete props[ index ];
++ }
++
++ hooks = jQuery.cssHooks[ name ];
++ if ( hooks && "expand" in hooks ) {
++ value = hooks.expand( value );
++ delete props[ name ];
++
++ // Not quite $.extend, this won't overwrite existing keys.
++ // Reusing 'index' because we have the correct "name"
++ for ( index in value ) {
++ if ( !( index in props ) ) {
++ props[ index ] = value[ index ];
++ specialEasing[ index ] = easing;
++ }
++ }
++ } else {
++ specialEasing[ name ] = easing;
++ }
++ }
++}
++
++function Animation( elem, properties, options ) {
++ var result,
++ stopped,
++ index = 0,
++ length = Animation.prefilters.length,
++ deferred = jQuery.Deferred().always( function() {
++
++ // Don't match elem in the :animated selector
++ delete tick.elem;
++ } ),
++ tick = function() {
++ if ( stopped ) {
++ return false;
++ }
++ var currentTime = fxNow || createFxNow(),
++ remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
++
++ // Support: Android 2.3
++ // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497)
++ temp = remaining / animation.duration || 0,
++ percent = 1 - temp,
++ index = 0,
++ length = animation.tweens.length;
++
++ for ( ; index < length ; index++ ) {
++ animation.tweens[ index ].run( percent );
++ }
++
++ deferred.notifyWith( elem, [ animation, percent, remaining ] );
++
++ if ( percent < 1 && length ) {
++ return remaining;
++ } else {
++ deferred.resolveWith( elem, [ animation ] );
++ return false;
++ }
++ },
++ animation = deferred.promise( {
++ elem: elem,
++ props: jQuery.extend( {}, properties ),
++ opts: jQuery.extend( true, {
++ specialEasing: {},
++ easing: jQuery.easing._default
++ }, options ),
++ originalProperties: properties,
++ originalOptions: options,
++ startTime: fxNow || createFxNow(),
++ duration: options.duration,
++ tweens: [],
++ createTween: function( prop, end ) {
++ var tween = jQuery.Tween( elem, animation.opts, prop, end,
++ animation.opts.specialEasing[ prop ] || animation.opts.easing );
++ animation.tweens.push( tween );
++ return tween;
++ },
++ stop: function( gotoEnd ) {
++ var index = 0,
++
++ // If we are going to the end, we want to run all the tweens
++ // otherwise we skip this part
++ length = gotoEnd ? animation.tweens.length : 0;
++ if ( stopped ) {
++ return this;
++ }
++ stopped = true;
++ for ( ; index < length ; index++ ) {
++ animation.tweens[ index ].run( 1 );
++ }
++
++ // Resolve when we played the last frame; otherwise, reject
++ if ( gotoEnd ) {
++ deferred.notifyWith( elem, [ animation, 1, 0 ] );
++ deferred.resolveWith( elem, [ animation, gotoEnd ] );
++ } else {
++ deferred.rejectWith( elem, [ animation, gotoEnd ] );
++ }
++ return this;
++ }
++ } ),
++ props = animation.props;
++
++ propFilter( props, animation.opts.specialEasing );
++
++ for ( ; index < length ; index++ ) {
++ result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts );
++ if ( result ) {
++ if ( jQuery.isFunction( result.stop ) ) {
++ jQuery._queueHooks( animation.elem, animation.opts.queue ).stop =
++ jQuery.proxy( result.stop, result );
++ }
++ return result;
++ }
++ }
++
++ jQuery.map( props, createTween, animation );
++
++ if ( jQuery.isFunction( animation.opts.start ) ) {
++ animation.opts.start.call( elem, animation );
++ }
++
++ jQuery.fx.timer(
++ jQuery.extend( tick, {
++ elem: elem,
++ anim: animation,
++ queue: animation.opts.queue
++ } )
++ );
++
++ // attach callbacks from options
++ return animation.progress( animation.opts.progress )
++ .done( animation.opts.done, animation.opts.complete )
++ .fail( animation.opts.fail )
++ .always( animation.opts.always );
++}
++
++jQuery.Animation = jQuery.extend( Animation, {
++ tweeners: {
++ "*": [ function( prop, value ) {
++ var tween = this.createTween( prop, value );
++ adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween );
++ return tween;
++ } ]
++ },
++
++ tweener: function( props, callback ) {
++ if ( jQuery.isFunction( props ) ) {
++ callback = props;
++ props = [ "*" ];
++ } else {
++ props = props.match( rnotwhite );
++ }
++
++ var prop,
++ index = 0,
++ length = props.length;
++
++ for ( ; index < length ; index++ ) {
++ prop = props[ index ];
++ Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || [];
++ Animation.tweeners[ prop ].unshift( callback );
++ }
++ },
++
++ prefilters: [ defaultPrefilter ],
++
++ prefilter: function( callback, prepend ) {
++ if ( prepend ) {
++ Animation.prefilters.unshift( callback );
++ } else {
++ Animation.prefilters.push( callback );
++ }
++ }
++} );
++
++jQuery.speed = function( speed, easing, fn ) {
++ var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
++ complete: fn || !fn && easing ||
++ jQuery.isFunction( speed ) && speed,
++ duration: speed,
++ easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
++ };
++
++ opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ?
++ opt.duration : opt.duration in jQuery.fx.speeds ?
++ jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
++
++ // Normalize opt.queue - true/undefined/null -> "fx"
++ if ( opt.queue == null || opt.queue === true ) {
++ opt.queue = "fx";
++ }
++
++ // Queueing
++ opt.old = opt.complete;
++
++ opt.complete = function() {
++ if ( jQuery.isFunction( opt.old ) ) {
++ opt.old.call( this );
++ }
++
++ if ( opt.queue ) {
++ jQuery.dequeue( this, opt.queue );
++ }
++ };
++
++ return opt;
++};
++
++jQuery.fn.extend( {
++ fadeTo: function( speed, to, easing, callback ) {
++
++ // Show any hidden elements after setting opacity to 0
++ return this.filter( isHidden ).css( "opacity", 0 ).show()
++
++ // Animate to the value specified
++ .end().animate( { opacity: to }, speed, easing, callback );
++ },
++ animate: function( prop, speed, easing, callback ) {
++ var empty = jQuery.isEmptyObject( prop ),
++ optall = jQuery.speed( speed, easing, callback ),
++ doAnimation = function() {
++
++ // Operate on a copy of prop so per-property easing won't be lost
++ var anim = Animation( this, jQuery.extend( {}, prop ), optall );
++
++ // Empty animations, or finishing resolves immediately
++ if ( empty || dataPriv.get( this, "finish" ) ) {
++ anim.stop( true );
++ }
++ };
++ doAnimation.finish = doAnimation;
++
++ return empty || optall.queue === false ?
++ this.each( doAnimation ) :
++ this.queue( optall.queue, doAnimation );
++ },
++ stop: function( type, clearQueue, gotoEnd ) {
++ var stopQueue = function( hooks ) {
++ var stop = hooks.stop;
++ delete hooks.stop;
++ stop( gotoEnd );
++ };
++
++ if ( typeof type !== "string" ) {
++ gotoEnd = clearQueue;
++ clearQueue = type;
++ type = undefined;
++ }
++ if ( clearQueue && type !== false ) {
++ this.queue( type || "fx", [] );
++ }
++
++ return this.each( function() {
++ var dequeue = true,
++ index = type != null && type + "queueHooks",
++ timers = jQuery.timers,
++ data = dataPriv.get( this );
++
++ if ( index ) {
++ if ( data[ index ] && data[ index ].stop ) {
++ stopQueue( data[ index ] );
++ }
++ } else {
++ for ( index in data ) {
++ if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
++ stopQueue( data[ index ] );
++ }
++ }
++ }
++
++ for ( index = timers.length; index--; ) {
++ if ( timers[ index ].elem === this &&
++ ( type == null || timers[ index ].queue === type ) ) {
++
++ timers[ index ].anim.stop( gotoEnd );
++ dequeue = false;
++ timers.splice( index, 1 );
++ }
++ }
++
++ // Start the next in the queue if the last step wasn't forced.
++ // Timers currently will call their complete callbacks, which
++ // will dequeue but only if they were gotoEnd.
++ if ( dequeue || !gotoEnd ) {
++ jQuery.dequeue( this, type );
++ }
++ } );
++ },
++ finish: function( type ) {
++ if ( type !== false ) {
++ type = type || "fx";
++ }
++ return this.each( function() {
++ var index,
++ data = dataPriv.get( this ),
++ queue = data[ type + "queue" ],
++ hooks = data[ type + "queueHooks" ],
++ timers = jQuery.timers,
++ length = queue ? queue.length : 0;
++
++ // Enable finishing flag on private data
++ data.finish = true;
++
++ // Empty the queue first
++ jQuery.queue( this, type, [] );
++
++ if ( hooks && hooks.stop ) {
++ hooks.stop.call( this, true );
++ }
++
++ // Look for any active animations, and finish them
++ for ( index = timers.length; index--; ) {
++ if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
++ timers[ index ].anim.stop( true );
++ timers.splice( index, 1 );
++ }
++ }
++
++ // Look for any animations in the old queue and finish them
++ for ( index = 0; index < length; index++ ) {
++ if ( queue[ index ] && queue[ index ].finish ) {
++ queue[ index ].finish.call( this );
++ }
++ }
++
++ // Turn off finishing flag
++ delete data.finish;
++ } );
++ }
++} );
++
++jQuery.each( [ "toggle", "show", "hide" ], function( i, name ) {
++ var cssFn = jQuery.fn[ name ];
++ jQuery.fn[ name ] = function( speed, easing, callback ) {
++ return speed == null || typeof speed === "boolean" ?
++ cssFn.apply( this, arguments ) :
++ this.animate( genFx( name, true ), speed, easing, callback );
++ };
++} );
++
++// Generate shortcuts for custom animations
++jQuery.each( {
++ slideDown: genFx( "show" ),
++ slideUp: genFx( "hide" ),
++ slideToggle: genFx( "toggle" ),
++ fadeIn: { opacity: "show" },
++ fadeOut: { opacity: "hide" },
++ fadeToggle: { opacity: "toggle" }
++}, function( name, props ) {
++ jQuery.fn[ name ] = function( speed, easing, callback ) {
++ return this.animate( props, speed, easing, callback );
++ };
++} );
++
++jQuery.timers = [];
++jQuery.fx.tick = function() {
++ var timer,
++ i = 0,
++ timers = jQuery.timers;
++
++ fxNow = jQuery.now();
++
++ for ( ; i < timers.length; i++ ) {
++ timer = timers[ i ];
++
++ // Checks the timer has not already been removed
++ if ( !timer() && timers[ i ] === timer ) {
++ timers.splice( i--, 1 );
++ }
++ }
++
++ if ( !timers.length ) {
++ jQuery.fx.stop();
++ }
++ fxNow = undefined;
++};
++
++jQuery.fx.timer = function( timer ) {
++ jQuery.timers.push( timer );
++ if ( timer() ) {
++ jQuery.fx.start();
++ } else {
++ jQuery.timers.pop();
++ }
++};
++
++jQuery.fx.interval = 13;
++jQuery.fx.start = function() {
++ if ( !timerId ) {
++ timerId = window.setInterval( jQuery.fx.tick, jQuery.fx.interval );
++ }
++};
++
++jQuery.fx.stop = function() {
++ window.clearInterval( timerId );
++
++ timerId = null;
++};
++
++jQuery.fx.speeds = {
++ slow: 600,
++ fast: 200,
++
++ // Default speed
++ _default: 400
++};
++
++
++// Based off of the plugin by Clint Helfers, with permission.
++// http://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/
++jQuery.fn.delay = function( time, type ) {
++ time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
++ type = type || "fx";
++
++ return this.queue( type, function( next, hooks ) {
++ var timeout = window.setTimeout( next, time );
++ hooks.stop = function() {
++ window.clearTimeout( timeout );
++ };
++ } );
++};
++
++
++( function() {
++ var input = document.createElement( "input" ),
++ select = document.createElement( "select" ),
++ opt = select.appendChild( document.createElement( "option" ) );
++
++ input.type = "checkbox";
++
++ // Support: iOS<=5.1, Android<=4.2+
++ // Default value for a checkbox should be "on"
++ support.checkOn = input.value !== "";
++
++ // Support: IE<=11+
++ // Must access selectedIndex to make default options select
++ support.optSelected = opt.selected;
++
++ // Support: Android<=2.3
++ // Options inside disabled selects are incorrectly marked as disabled
++ select.disabled = true;
++ support.optDisabled = !opt.disabled;
++
++ // Support: IE<=11+
++ // An input loses its value after becoming a radio
++ input = document.createElement( "input" );
++ input.value = "t";
++ input.type = "radio";
++ support.radioValue = input.value === "t";
++} )();
++
++
++var boolHook,
++ attrHandle = jQuery.expr.attrHandle;
++
++jQuery.fn.extend( {
++ attr: function( name, value ) {
++ return access( this, jQuery.attr, name, value, arguments.length > 1 );
++ },
++
++ removeAttr: function( name ) {
++ return this.each( function() {
++ jQuery.removeAttr( this, name );
++ } );
++ }
++} );
++
++jQuery.extend( {
++ attr: function( elem, name, value ) {
++ var ret, hooks,
++ nType = elem.nodeType;
++
++ // Don't get/set attributes on text, comment and attribute nodes
++ if ( nType === 3 || nType === 8 || nType === 2 ) {
++ return;
++ }
++
++ // Fallback to prop when attributes are not supported
++ if ( typeof elem.getAttribute === "undefined" ) {
++ return jQuery.prop( elem, name, value );
++ }
++
++ // 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 : undefined );
++ }
++
++ if ( value !== undefined ) {
++ if ( value === null ) {
++ jQuery.removeAttr( elem, name );
++ return;
++ }
++
++ if ( hooks && "set" in hooks &&
++ ( ret = hooks.set( elem, value, name ) ) !== undefined ) {
++ return ret;
++ }
++
++ elem.setAttribute( name, value + "" );
++ return value;
++ }
++
++ if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {
++ return ret;
++ }
++
++ ret = jQuery.find.attr( elem, name );
++
++ // Non-existent attributes return null, we normalize to undefined
++ return ret == null ? undefined : ret;
++ },
++
++ attrHooks: {
++ type: {
++ set: function( elem, value ) {
++ if ( !support.radioValue && value === "radio" &&
++ jQuery.nodeName( elem, "input" ) ) {
++ var val = elem.value;
++ elem.setAttribute( "type", value );
++ if ( val ) {
++ elem.value = val;
++ }
++ return value;
++ }
++ }
++ }
++ },
++
++ removeAttr: function( elem, value ) {
++ var name, propName,
++ i = 0,
++ attrNames = value && value.match( 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
++ elem[ propName ] = false;
++ }
++
++ elem.removeAttribute( name );
++ }
++ }
++ }
++} );
++
++// Hooks for boolean attributes
++boolHook = {
++ set: function( elem, value, name ) {
++ if ( value === false ) {
++
++ // Remove boolean attributes when set to false
++ jQuery.removeAttr( elem, name );
++ } else {
++ elem.setAttribute( name, name );
++ }
++ return name;
++ }
++};
++jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) {
++ var getter = attrHandle[ name ] || jQuery.find.attr;
++
++ attrHandle[ name ] = function( elem, name, isXML ) {
++ var ret, handle;
++ if ( !isXML ) {
++
++ // Avoid an infinite loop by temporarily removing this function from the getter
++ handle = attrHandle[ name ];
++ attrHandle[ name ] = ret;
++ ret = getter( elem, name, isXML ) != null ?
++ name.toLowerCase() :
++ null;
++ attrHandle[ name ] = handle;
++ }
++ return ret;
++ };
++} );
++
++
++
++
++var rfocusable = /^(?:input|select|textarea|button)$/i,
++ rclickable = /^(?:a|area)$/i;
++
++jQuery.fn.extend( {
++ prop: function( name, value ) {
++ return access( this, jQuery.prop, name, value, arguments.length > 1 );
++ },
++
++ removeProp: function( name ) {
++ return this.each( function() {
++ delete this[ jQuery.propFix[ name ] || name ];
++ } );
++ }
++} );
++
++jQuery.extend( {
++ prop: function( elem, name, value ) {
++ var ret, hooks,
++ nType = elem.nodeType;
++
++ // Don't get/set properties on text, comment and attribute nodes
++ if ( nType === 3 || nType === 8 || nType === 2 ) {
++ return;
++ }
++
++ if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
++
++ // Fix name and attach hooks
++ name = jQuery.propFix[ name ] || name;
++ hooks = jQuery.propHooks[ name ];
++ }
++
++ if ( value !== undefined ) {
++ if ( hooks && "set" in hooks &&
++ ( ret = hooks.set( elem, value, name ) ) !== undefined ) {
++ return ret;
++ }
++
++ return ( elem[ name ] = value );
++ }
++
++ if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {
++ return ret;
++ }
++
++ return elem[ name ];
++ },
++
++ propHooks: {
++ tabIndex: {
++ get: function( elem ) {
++
++ // elem.tabIndex doesn't always return the
++ // correct value when it hasn't been explicitly set
++ // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
++ // 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 :
++ -1;
++ }
++ }
++ },
++
++ propFix: {
++ "for": "htmlFor",
++ "class": "className"
++ }
++} );
++
++// Support: IE <=11 only
++// Accessing the selectedIndex property
++// forces the browser to respect setting selected
++// on the option
++// The getter ensures a default option is selected
++// when in an optgroup
++if ( !support.optSelected ) {
++ jQuery.propHooks.selected = {
++ get: function( elem ) {
++ var parent = elem.parentNode;
++ if ( parent && parent.parentNode ) {
++ parent.parentNode.selectedIndex;
++ }
++ return null;
++ },
++ set: function( elem ) {
++ var parent = elem.parentNode;
++ if ( parent ) {
++ parent.selectedIndex;
++
++ if ( parent.parentNode ) {
++ parent.parentNode.selectedIndex;
++ }
++ }
++ }
++ };
++}
++
++jQuery.each( [
++ "tabIndex",
++ "readOnly",
++ "maxLength",
++ "cellSpacing",
++ "cellPadding",
++ "rowSpan",
++ "colSpan",
++ "useMap",
++ "frameBorder",
++ "contentEditable"
++], function() {
++ jQuery.propFix[ this.toLowerCase() ] = this;
++} );
++
++
++
++
++var rclass = /[\t\r\n\f]/g;
++
++function getClass( elem ) {
++ return elem.getAttribute && elem.getAttribute( "class" ) || "";
++}
++
++jQuery.fn.extend( {
++ addClass: function( value ) {
++ var classes, elem, cur, curValue, clazz, j, finalValue,
++ i = 0;
++
++ if ( jQuery.isFunction( value ) ) {
++ return this.each( function( j ) {
++ jQuery( this ).addClass( value.call( this, j, getClass( this ) ) );
++ } );
++ }
++
++ if ( typeof value === "string" && value ) {
++ classes = value.match( rnotwhite ) || [];
++
++ while ( ( elem = this[ i++ ] ) ) {
++ curValue = getClass( elem );
++ cur = elem.nodeType === 1 &&
++ ( " " + curValue + " " ).replace( rclass, " " );
++
++ if ( cur ) {
++ j = 0;
++ while ( ( clazz = classes[ j++ ] ) ) {
++ if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
++ cur += clazz + " ";
++ }
++ }
++
++ // Only assign if different to avoid unneeded rendering.
++ finalValue = jQuery.trim( cur );
++ if ( curValue !== finalValue ) {
++ elem.setAttribute( "class", finalValue );
++ }
++ }
++ }
++ }
++
++ return this;
++ },
++
++ removeClass: function( value ) {
++ var classes, elem, cur, curValue, clazz, j, finalValue,
++ i = 0;
++
++ if ( jQuery.isFunction( value ) ) {
++ return this.each( function( j ) {
++ jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) );
++ } );
++ }
++
++ if ( !arguments.length ) {
++ return this.attr( "class", "" );
++ }
++
++ if ( typeof value === "string" && value ) {
++ classes = value.match( rnotwhite ) || [];
++
++ while ( ( elem = this[ i++ ] ) ) {
++ curValue = getClass( elem );
++
++ // This expression is here for better compressibility (see addClass)
++ cur = elem.nodeType === 1 &&
++ ( " " + curValue + " " ).replace( rclass, " " );
++
++ if ( cur ) {
++ j = 0;
++ while ( ( clazz = classes[ j++ ] ) ) {
++
++ // Remove *all* instances
++ while ( cur.indexOf( " " + clazz + " " ) > -1 ) {
++ cur = cur.replace( " " + clazz + " ", " " );
++ }
++ }
++
++ // Only assign if different to avoid unneeded rendering.
++ finalValue = jQuery.trim( cur );
++ if ( curValue !== finalValue ) {
++ elem.setAttribute( "class", finalValue );
++ }
++ }
++ }
++ }
++
++ return this;
++ },
++
++ toggleClass: function( value, stateVal ) {
++ 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 ) {
++ jQuery( this ).toggleClass(
++ value.call( this, i, getClass( this ), stateVal ),
++ stateVal
++ );
++ } );
++ }
++
++ return this.each( function() {
++ var className, i, self, classNames;
++
++ if ( type === "string" ) {
++
++ // Toggle individual class names
++ i = 0;
++ self = jQuery( this );
++ classNames = value.match( rnotwhite ) || [];
++
++ while ( ( className = classNames[ i++ ] ) ) {
++
++ // Check each className given, space separated list
++ if ( self.hasClass( className ) ) {
++ self.removeClass( className );
++ } else {
++ self.addClass( className );
++ }
++ }
++
++ // Toggle whole class name
++ } else if ( value === undefined || type === "boolean" ) {
++ className = getClass( this );
++ if ( className ) {
++
++ // Store className if set
++ dataPriv.set( this, "__className__", 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.
++ if ( this.setAttribute ) {
++ this.setAttribute( "class",
++ className || value === false ?
++ "" :
++ dataPriv.get( this, "__className__" ) || ""
++ );
++ }
++ }
++ } );
++ },
++
++ hasClass: function( selector ) {
++ var className, elem,
++ i = 0;
++
++ className = " " + selector + " ";
++ while ( ( elem = this[ i++ ] ) ) {
++ if ( elem.nodeType === 1 &&
++ ( " " + getClass( elem ) + " " ).replace( rclass, " " )
++ .indexOf( className ) > -1
++ ) {
++ return true;
++ }
++ }
++
++ return false;
++ }
++} );
++
++
++
++
++var rreturn = /\r/g,
++ rspaces = /[\x20\t\r\n\f]+/g;
++
++jQuery.fn.extend( {
++ val: function( value ) {
++ var hooks, ret, isFunction,
++ elem = this[ 0 ];
++
++ if ( !arguments.length ) {
++ if ( elem ) {
++ hooks = jQuery.valHooks[ elem.type ] ||
++ jQuery.valHooks[ elem.nodeName.toLowerCase() ];
++
++ if ( hooks &&
++ "get" in hooks &&
++ ( ret = hooks.get( elem, "value" ) ) !== undefined
++ ) {
++ return ret;
++ }
++
++ ret = elem.value;
++
++ return typeof ret === "string" ?
++
++ // Handle most common string cases
++ ret.replace( rreturn, "" ) :
++
++ // Handle cases where value is null/undef or number
++ ret == null ? "" : ret;
++ }
++
++ return;
++ }
++
++ isFunction = jQuery.isFunction( value );
++
++ return this.each( function( i ) {
++ var val;
++
++ if ( this.nodeType !== 1 ) {
++ return;
++ }
++
++ if ( isFunction ) {
++ val = value.call( this, i, jQuery( this ).val() );
++ } else {
++ val = value;
++ }
++
++ // Treat null/undefined as ""; convert numbers to string
++ if ( val == null ) {
++ val = "";
++
++ } else if ( typeof val === "number" ) {
++ val += "";
++
++ } else if ( jQuery.isArray( val ) ) {
++ val = jQuery.map( val, function( value ) {
++ return value == null ? "" : value + "";
++ } );
++ }
++
++ hooks = jQuery.valHooks[ this.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 ) {
++ this.value = val;
++ }
++ } );
++ }
++} );
++
++jQuery.extend( {
++ valHooks: {
++ option: {
++ get: function( elem ) {
++
++ var val = jQuery.find.attr( elem, "value" );
++ return val != null ?
++ val :
++
++ // Support: IE10-11+
++ // option.text throws exceptions (#14686, #14858)
++ // Strip and collapse whitespace
++ // https://html.spec.whatwg.org/#strip-and-collapse-whitespace
++ jQuery.trim( jQuery.text( elem ) ).replace( rspaces, " " );
++ }
++ },
++ select: {
++ get: function( elem ) {
++ var value, option,
++ options = elem.options,
++ 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 ( ; i < max; i++ ) {
++ option = options[ i ];
++
++ // IE8-9 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
++ ( support.optDisabled ?
++ !option.disabled : option.getAttribute( "disabled" ) === null ) &&
++ ( !option.parentNode.disabled ||
++ !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
++
++ // Get the specific value for the option
++ value = jQuery( option ).val();
++
++ // We don't need an array for one selects
++ if ( one ) {
++ return value;
++ }
++
++ // Multi-Selects return an array
++ values.push( value );
++ }
++ }
++
++ return values;
++ },
++
++ set: function( elem, value ) {
++ var optionSet, option,
++ options = elem.options,
++ values = jQuery.makeArray( value ),
++ i = options.length;
++
++ while ( i-- ) {
++ option = options[ i ];
++ if ( option.selected =
++ jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1
++ ) {
++ optionSet = true;
++ }
++ }
++
++ // Force browsers to behave consistently when non-matching value is set
++ if ( !optionSet ) {
++ elem.selectedIndex = -1;
++ }
++ return values;
++ }
++ }
++ }
++} );
++
++// Radios and checkboxes getter/setter
++jQuery.each( [ "radio", "checkbox" ], function() {
++ jQuery.valHooks[ this ] = {
++ set: function( elem, value ) {
++ if ( jQuery.isArray( value ) ) {
++ return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 );
++ }
++ }
++ };
++ if ( !support.checkOn ) {
++ jQuery.valHooks[ this ].get = function( elem ) {
++ return elem.getAttribute( "value" ) === null ? "on" : elem.value;
++ };
++ }
++} );
++
++
++
++
++// Return jQuery for attributes-only inclusion
++
++
++var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/;
++
++jQuery.extend( jQuery.event, {
++
++ trigger: function( event, data, elem, onlyHandlers ) {
++
++ var i, cur, tmp, bubbleType, ontype, handle, special,
++ eventPath = [ elem || document ],
++ type = hasOwn.call( event, "type" ) ? event.type : event,
++ namespaces = 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( "." ) > -1 ) {
++
++ // Namespaced trigger; create a regexp to match event type in handle()
++ namespaces = type.split( "." );
++ type = namespaces.shift();
++ namespaces.sort();
++ }
++ 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.rnamespace = event.namespace ?
++ new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) :
++ null;
++
++ // Clean up the event in case it is being reused
++ event.result = undefined;
++ if ( !event.target ) {
++ event.target = elem;
++ }
++
++ // Clone any incoming data and prepend the event, creating the handler arg list
++ 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 = ( dataPriv.get( cur, "events" ) || {} )[ event.type ] &&
++ dataPriv.get( cur, "handle" );
++ if ( handle ) {
++ handle.apply( cur, data );
++ }
++
++ // Native handler
++ handle = ontype && cur[ ontype ];
++ if ( handle && handle.apply && acceptData( cur ) ) {
++ event.result = handle.apply( cur, data );
++ if ( event.result === false ) {
++ event.preventDefault();
++ }
++ }
++ }
++ event.type = type;
++
++ // If nobody prevented the default action, do it now
++ if ( !onlyHandlers && !event.isDefaultPrevented() ) {
++
++ if ( ( !special._default ||
++ special._default.apply( eventPath.pop(), data ) === false ) &&
++ acceptData( elem ) ) {
++
++ // Call a native DOM method on the target with the same name name as the event.
++ // Don't do default actions on window, that's where global variables be (#6170)
++ if ( ontype && jQuery.isFunction( 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;
++ elem[ type ]();
++ jQuery.event.triggered = undefined;
++
++ if ( tmp ) {
++ elem[ ontype ] = tmp;
++ }
++ }
++ }
++ }
++
++ return event.result;
++ },
++
++ // Piggyback on a donor event to simulate a different one
++ simulate: function( type, elem, event ) {
++ var e = jQuery.extend(
++ new jQuery.Event(),
++ event,
++ {
++ type: type,
++ isSimulated: true
++
++ // Previously, `originalEvent: {}` was set here, so stopPropagation call
++ // would not be triggered on donor event, since in our own
++ // jQuery.event.stopPropagation function we had a check for existence of
++ // originalEvent.stopPropagation method, so, consequently it would be a noop.
++ //
++ // But now, this "simulate" function is used only for events
++ // for which stopPropagation() is noop, so there is no need for that anymore.
++ //
++ // For the 1.x branch though, guard for "click" and "submit"
++ // events is still used, but was moved to jQuery.event.stopPropagation function
++ // because `originalEvent` should point to the original event for the constancy
++ // with other events and for more focused logic
++ }
++ );
++
++ jQuery.event.trigger( e, null, elem );
++
++ if ( e.isDefaultPrevented() ) {
++ event.preventDefault();
++ }
++ }
++
++} );
++
++jQuery.fn.extend( {
++
++ trigger: function( type, data ) {
++ return this.each( function() {
++ jQuery.event.trigger( type, data, this );
++ } );
++ },
++ triggerHandler: function( type, data ) {
++ var elem = this[ 0 ];
++ if ( elem ) {
++ return jQuery.event.trigger( type, data, elem, true );
++ }
++ }
++} );
++
++
++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 contextmenu" ).split( " " ),
++ function( i, name ) {
++
++ // Handle event binding
++ jQuery.fn[ name ] = function( data, fn ) {
++ return arguments.length > 0 ?
++ this.on( name, null, data, fn ) :
++ this.trigger( name );
++ };
++} );
++
++jQuery.fn.extend( {
++ hover: function( fnOver, fnOut ) {
++ return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
++ }
++} );
++
++
++
++
++support.focusin = "onfocusin" in window;
++
++
++// Support: Firefox
++// Firefox doesn't have focus(in | out) events
++// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787
++//
++// Support: Chrome, Safari
++// focus(in | out) events fire after focus & blur events,
++// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order
++// Related ticket - https://code.google.com/p/chromium/issues/detail?id=449857
++if ( !support.focusin ) {
++ jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) {
++
++ // Attach a single capturing handler on the document while someone wants focusin/focusout
++ var handler = function( event ) {
++ jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) );
++ };
++
++ jQuery.event.special[ fix ] = {
++ setup: function() {
++ var doc = this.ownerDocument || this,
++ attaches = dataPriv.access( doc, fix );
++
++ if ( !attaches ) {
++ doc.addEventListener( orig, handler, true );
++ }
++ dataPriv.access( doc, fix, ( attaches || 0 ) + 1 );
++ },
++ teardown: function() {
++ var doc = this.ownerDocument || this,
++ attaches = dataPriv.access( doc, fix ) - 1;
++
++ if ( !attaches ) {
++ doc.removeEventListener( orig, handler, true );
++ dataPriv.remove( doc, fix );
++
++ } else {
++ dataPriv.access( doc, fix, attaches );
++ }
++ }
++ };
++ } );
++}
++var location = window.location;
++
++var nonce = jQuery.now();
++
++var rquery = ( /\?/ );
++
++
++
++// Support: Android 2.3
++// Workaround failure to string-cast null input
++jQuery.parseJSON = function( data ) {
++ return JSON.parse( data + "" );
++};
++
++
++// Cross-browser xml parsing
++jQuery.parseXML = function( data ) {
++ var xml;
++ if ( !data || typeof data !== "string" ) {
++ return null;
++ }
++
++ // Support: IE9
++ try {
++ xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" );
++ } catch ( e ) {
++ xml = undefined;
++ }
++
++ if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) {
++ jQuery.error( "Invalid XML: " + data );
++ }
++ return xml;
++};
++
++
++var
++ rhash = /#.*$/,
++ rts = /([?&])_=[^&]*/,
++ rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg,
++
++ // #7653, #8125, #8152: local protocol detection
++ rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
++ rnoContent = /^(?:GET|HEAD)$/,
++ rprotocol = /^\/\//,
++
++ /* Prefilters
++ * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
++ * 2) These are called:
++ * - BEFORE asking for a transport
++ * - AFTER param serialization (s.data is a string if s.processData is true)
++ * 3) key is the dataType
++ * 4) the catchall symbol "*" can be used
++ * 5) execution will start with transport dataType and THEN continue down to "*" if needed
++ */
++ prefilters = {},
++
++ /* Transports bindings
++ * 1) key is the dataType
++ * 2) the catchall symbol "*" can be used
++ * 3) selection will start with transport dataType and THEN go to "*" if needed
++ */
++ transports = {},
++
++ // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
++ allTypes = "*/".concat( "*" ),
++
++ // Anchor tag for parsing the document origin
++ originAnchor = document.createElement( "a" );
++ originAnchor.href = location.href;
++
++// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
++function addToPrefiltersOrTransports( structure ) {
++
++ // dataTypeExpression is optional and defaults to "*"
++ return function( dataTypeExpression, func ) {
++
++ if ( typeof dataTypeExpression !== "string" ) {
++ func = dataTypeExpression;
++ dataTypeExpression = "*";
++ }
++
++ var dataType,
++ i = 0,
++ dataTypes = dataTypeExpression.toLowerCase().match( rnotwhite ) || [];
++
++ if ( jQuery.isFunction( func ) ) {
++
++ // For each dataType in the dataTypeExpression
++ while ( ( dataType = dataTypes[ i++ ] ) ) {
++
++ // Prepend if requested
++ if ( dataType[ 0 ] === "+" ) {
++ dataType = dataType.slice( 1 ) || "*";
++ ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func );
++
++ // Otherwise append
++ } else {
++ ( structure[ dataType ] = structure[ dataType ] || [] ).push( func );
++ }
++ }
++ }
++ };
++}
++
++// Base inspection function for prefilters and transports
++function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {
++
++ var inspected = {},
++ seekingTransport = ( structure === transports );
++
++ function inspect( dataType ) {
++ var selected;
++ inspected[ dataType ] = true;
++ jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
++ var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
++ if ( typeof dataTypeOrTransport === "string" &&
++ !seekingTransport && !inspected[ dataTypeOrTransport ] ) {
++
++ options.dataTypes.unshift( dataTypeOrTransport );
++ inspect( dataTypeOrTransport );
++ return false;
++ } else if ( seekingTransport ) {
++ return !( selected = dataTypeOrTransport );
++ }
++ } );
++ return selected;
++ }
++
++ return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
++}
++
++// A special extend for ajax options
++// that takes "flat" options (not to be deep extended)
++// Fixes #9887
++function ajaxExtend( target, src ) {
++ var key, deep,
++ flatOptions = jQuery.ajaxSettings.flatOptions || {};
++
++ for ( key in src ) {
++ if ( src[ key ] !== undefined ) {
++ ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ];
++ }
++ }
++ if ( deep ) {
++ jQuery.extend( true, target, deep );
++ }
++
++ return target;
++}
++
++/* Handles responses to an ajax request:
++ * - finds the right dataType (mediates between content-type and expected dataType)
++ * - returns the corresponding response
++ */
++function ajaxHandleResponses( s, jqXHR, responses ) {
++
++ var ct, type, finalDataType, firstDataType,
++ contents = s.contents,
++ dataTypes = s.dataTypes;
++
++ // Remove auto dataType and get content-type in the process
++ while ( dataTypes[ 0 ] === "*" ) {
++ dataTypes.shift();
++ if ( ct === undefined ) {
++ ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" );
++ }
++ }
++
++ // Check if we're dealing with a known content-type
++ if ( ct ) {
++ for ( type in contents ) {
++ if ( contents[ type ] && contents[ type ].test( ct ) ) {
++ dataTypes.unshift( type );
++ break;
++ }
++ }
++ }
++
++ // Check to see if we have a response for the expected dataType
++ if ( dataTypes[ 0 ] in responses ) {
++ finalDataType = dataTypes[ 0 ];
++ } else {
++
++ // Try convertible dataTypes
++ for ( type in responses ) {
++ if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) {
++ finalDataType = type;
++ break;
++ }
++ if ( !firstDataType ) {
++ firstDataType = type;
++ }
++ }
++
++ // Or just use first one
++ finalDataType = finalDataType || firstDataType;
++ }
++
++ // If we found a dataType
++ // We add the dataType to the list if needed
++ // and return the corresponding response
++ if ( finalDataType ) {
++ if ( finalDataType !== dataTypes[ 0 ] ) {
++ dataTypes.unshift( finalDataType );
++ }
++ return responses[ finalDataType ];
++ }
++}
++
++/* Chain conversions given the request and the original response
++ * Also sets the responseXXX fields on the jqXHR instance
++ */
++function ajaxConvert( s, response, jqXHR, isSuccess ) {
++ var conv2, current, conv, tmp, prev,
++ converters = {},
++
++ // Work with a copy of dataTypes in case we need to modify it for conversion
++ dataTypes = s.dataTypes.slice();
++
++ // Create converters map with lowercased keys
++ if ( dataTypes[ 1 ] ) {
++ for ( conv in s.converters ) {
++ converters[ conv.toLowerCase() ] = s.converters[ conv ];
++ }
++ }
++
++ current = dataTypes.shift();
++
++ // Convert to each sequential dataType
++ while ( current ) {
++
++ if ( s.responseFields[ current ] ) {
++ jqXHR[ s.responseFields[ current ] ] = response;
++ }
++
++ // Apply the dataFilter if provided
++ if ( !prev && isSuccess && s.dataFilter ) {
++ response = s.dataFilter( response, s.dataType );
++ }
++
++ prev = current;
++ current = dataTypes.shift();
++
++ if ( current ) {
++
++ // There's only work to do if current dataType is non-auto
++ if ( current === "*" ) {
++
++ current = prev;
++
++ // Convert response if prev dataType is non-auto and differs from current
++ } else if ( prev !== "*" && prev !== current ) {
++
++ // Seek a direct converter
++ conv = converters[ prev + " " + current ] || converters[ "* " + current ];
++
++ // If none found, seek a pair
++ if ( !conv ) {
++ for ( conv2 in converters ) {
++
++ // If conv2 outputs current
++ tmp = conv2.split( " " );
++ if ( tmp[ 1 ] === current ) {
++
++ // If prev can be converted to accepted input
++ conv = converters[ prev + " " + tmp[ 0 ] ] ||
++ converters[ "* " + tmp[ 0 ] ];
++ if ( conv ) {
++
++ // Condense equivalence converters
++ if ( conv === true ) {
++ conv = converters[ conv2 ];
++
++ // Otherwise, insert the intermediate dataType
++ } else if ( converters[ conv2 ] !== true ) {
++ current = tmp[ 0 ];
++ dataTypes.unshift( tmp[ 1 ] );
++ }
++ break;
++ }
++ }
++ }
++ }
++
++ // Apply converter (if not an equivalence)
++ if ( conv !== true ) {
++
++ // Unless errors are allowed to bubble, catch and return them
++ if ( conv && s.throws ) {
++ response = conv( response );
++ } else {
++ try {
++ response = conv( response );
++ } catch ( e ) {
++ return {
++ state: "parsererror",
++ error: conv ? e : "No conversion from " + prev + " to " + current
++ };
++ }
++ }
++ }
++ }
++ }
++ }
++
++ return { state: "success", data: response };
++}
++
++jQuery.extend( {
++
++ // Counter for holding the number of active queries
++ active: 0,
++
++ // Last-Modified header cache for next request
++ lastModified: {},
++ etag: {},
++
++ ajaxSettings: {
++ url: location.href,
++ type: "GET",
++ isLocal: rlocalProtocol.test( location.protocol ),
++ global: true,
++ processData: true,
++ async: true,
++ contentType: "application/x-www-form-urlencoded; charset=UTF-8",
++ /*
++ timeout: 0,
++ data: null,
++ dataType: null,
++ username: null,
++ password: null,
++ cache: null,
++ throws: false,
++ traditional: false,
++ headers: {},
++ */
++
++ accepts: {
++ "*": allTypes,
++ text: "text/plain",
++ html: "text/html",
++ xml: "application/xml, text/xml",
++ json: "application/json, text/javascript"
++ },
++
++ contents: {
++ xml: /\bxml\b/,
++ html: /\bhtml/,
++ json: /\bjson\b/
++ },
++
++ responseFields: {
++ xml: "responseXML",
++ text: "responseText",
++ json: "responseJSON"
++ },
++
++ // Data converters
++ // Keys separate source (or catchall "*") and destination types with a single space
++ converters: {
++
++ // Convert anything to text
++ "* text": String,
++
++ // Text to html (true = no transformation)
++ "text html": true,
++
++ // Evaluate text as a json expression
++ "text json": jQuery.parseJSON,
++
++ // Parse text as xml
++ "text xml": jQuery.parseXML
++ },
++
++ // For options that shouldn't be deep extended:
++ // you can add your own custom options here if
++ // and when you create one that shouldn't be
++ // deep extended (see ajaxExtend)
++ flatOptions: {
++ url: true,
++ context: true
++ }
++ },
++
++ // Creates a full fledged settings object into target
++ // with both ajaxSettings and settings fields.
++ // If target is omitted, writes into ajaxSettings.
++ ajaxSetup: function( target, settings ) {
++ return settings ?
++
++ // Building a settings object
++ ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :
++
++ // Extending ajaxSettings
++ ajaxExtend( jQuery.ajaxSettings, target );
++ },
++
++ ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
++ ajaxTransport: addToPrefiltersOrTransports( transports ),
++
++ // Main method
++ ajax: function( url, options ) {
++
++ // If url is an object, simulate pre-1.5 signature
++ if ( typeof url === "object" ) {
++ options = url;
++ url = undefined;
++ }
++
++ // Force options to be an object
++ options = options || {};
++
++ var transport,
++
++ // URL without anti-cache param
++ cacheURL,
++
++ // Response headers
++ responseHeadersString,
++ responseHeaders,
++
++ // timeout handle
++ timeoutTimer,
++
++ // Url cleanup var
++ urlAnchor,
++
++ // To know if global events are to be dispatched
++ fireGlobals,
++
++ // Loop variable
++ i,
++
++ // Create the final options object
++ s = jQuery.ajaxSetup( {}, options ),
++
++ // Callbacks context
++ callbackContext = s.context || s,
++
++ // Context for global events is callbackContext if it is a DOM node or jQuery collection
++ globalEventContext = s.context &&
++ ( callbackContext.nodeType || callbackContext.jquery ) ?
++ jQuery( callbackContext ) :
++ jQuery.event,
++
++ // Deferreds
++ deferred = jQuery.Deferred(),
++ completeDeferred = jQuery.Callbacks( "once memory" ),
++
++ // Status-dependent callbacks
++ statusCode = s.statusCode || {},
++
++ // Headers (they are sent all at once)
++ requestHeaders = {},
++ requestHeadersNames = {},
++
++ // The jqXHR state
++ state = 0,
++
++ // Default abort message
++ strAbort = "canceled",
++
++ // Fake xhr
++ jqXHR = {
++ readyState: 0,
++
++ // Builds headers hashtable if needed
++ getResponseHeader: function( key ) {
++ var match;
++ if ( state === 2 ) {
++ if ( !responseHeaders ) {
++ responseHeaders = {};
++ while ( ( match = rheaders.exec( responseHeadersString ) ) ) {
++ responseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ];
++ }
++ }
++ match = responseHeaders[ key.toLowerCase() ];
++ }
++ return match == null ? null : match;
++ },
++
++ // Raw string
++ getAllResponseHeaders: function() {
++ return state === 2 ? responseHeadersString : null;
++ },
++
++ // Caches the header
++ setRequestHeader: function( name, value ) {
++ var lname = name.toLowerCase();
++ if ( !state ) {
++ name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
++ requestHeaders[ name ] = value;
++ }
++ return this;
++ },
++
++ // Overrides response content-type header
++ overrideMimeType: function( type ) {
++ if ( !state ) {
++ s.mimeType = type;
++ }
++ return this;
++ },
++
++ // Status-dependent callbacks
++ statusCode: function( map ) {
++ var code;
++ if ( map ) {
++ if ( state < 2 ) {
++ for ( code in map ) {
++
++ // Lazy-add the new callback in a way that preserves old ones
++ statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
++ }
++ } else {
++
++ // Execute the appropriate callbacks
++ jqXHR.always( map[ jqXHR.status ] );
++ }
++ }
++ return this;
++ },
++
++ // Cancel the request
++ abort: function( statusText ) {
++ var finalText = statusText || strAbort;
++ if ( transport ) {
++ transport.abort( finalText );
++ }
++ done( 0, finalText );
++ return this;
++ }
++ };
++
++ // Attach deferreds
++ deferred.promise( jqXHR ).complete = completeDeferred.add;
++ jqXHR.success = jqXHR.done;
++ jqXHR.error = jqXHR.fail;
++
++ // Remove hash character (#7531: and string promotion)
++ // Add protocol if not provided (prefilters might expect it)
++ // Handle falsy url in the settings object (#10093: consistency with old signature)
++ // We also use the url parameter if available
++ s.url = ( ( url || s.url || location.href ) + "" ).replace( rhash, "" )
++ .replace( rprotocol, location.protocol + "//" );
++
++ // Alias method option to type as per ticket #12004
++ s.type = options.method || options.type || s.method || s.type;
++
++ // Extract dataTypes list
++ s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( rnotwhite ) || [ "" ];
++
++ // A cross-domain request is in order when the origin doesn't match the current origin.
++ if ( s.crossDomain == null ) {
++ urlAnchor = document.createElement( "a" );
++
++ // Support: IE8-11+
++ // IE throws exception if url is malformed, e.g. http://example.com:80x/
++ try {
++ urlAnchor.href = s.url;
++
++ // Support: IE8-11+
++ // Anchor's host property isn't correctly set when s.url is relative
++ urlAnchor.href = urlAnchor.href;
++ s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !==
++ urlAnchor.protocol + "//" + urlAnchor.host;
++ } catch ( e ) {
++
++ // If there is an error parsing the URL, assume it is crossDomain,
++ // it can be rejected by the transport if it is invalid
++ s.crossDomain = true;
++ }
++ }
++
++ // Convert data if not already a string
++ if ( s.data && s.processData && typeof s.data !== "string" ) {
++ s.data = jQuery.param( s.data, s.traditional );
++ }
++
++ // Apply prefilters
++ inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
++
++ // If request was aborted inside a prefilter, stop there
++ if ( state === 2 ) {
++ return jqXHR;
++ }
++
++ // We can fire global events as of now if asked to
++ // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118)
++ fireGlobals = jQuery.event && s.global;
++
++ // Watch for a new set of requests
++ if ( fireGlobals && jQuery.active++ === 0 ) {
++ jQuery.event.trigger( "ajaxStart" );
++ }
++
++ // Uppercase the type
++ s.type = s.type.toUpperCase();
++
++ // Determine if request has content
++ s.hasContent = !rnoContent.test( s.type );
++
++ // Save the URL in case we're toying with the If-Modified-Since
++ // and/or If-None-Match header later on
++ cacheURL = s.url;
++
++ // More options handling for requests with no content
++ if ( !s.hasContent ) {
++
++ // If data is available, append data to url
++ if ( s.data ) {
++ cacheURL = ( s.url += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
++
++ // #9682: remove data so that it's not used in an eventual retry
++ delete s.data;
++ }
++
++ // Add anti-cache in url if needed
++ if ( s.cache === false ) {
++ s.url = rts.test( cacheURL ) ?
++
++ // If there is already a '_' parameter, set its value
++ cacheURL.replace( rts, "$1_=" + nonce++ ) :
++
++ // Otherwise add one to the end
++ cacheURL + ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + nonce++;
++ }
++ }
++
++ // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
++ if ( s.ifModified ) {
++ if ( jQuery.lastModified[ cacheURL ] ) {
++ jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
++ }
++ if ( jQuery.etag[ cacheURL ] ) {
++ jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
++ }
++ }
++
++ // Set the correct header, if data is being sent
++ if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
++ jqXHR.setRequestHeader( "Content-Type", s.contentType );
++ }
++
++ // Set the Accepts header for the server, depending on the dataType
++ jqXHR.setRequestHeader(
++ "Accept",
++ s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ?
++ s.accepts[ s.dataTypes[ 0 ] ] +
++ ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
++ s.accepts[ "*" ]
++ );
++
++ // Check for headers option
++ for ( i in s.headers ) {
++ jqXHR.setRequestHeader( i, s.headers[ i ] );
++ }
++
++ // Allow custom headers/mimetypes and early abort
++ if ( s.beforeSend &&
++ ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
++
++ // Abort if not done already and return
++ return jqXHR.abort();
++ }
++
++ // Aborting is no longer a cancellation
++ strAbort = "abort";
++
++ // Install callbacks on deferreds
++ for ( i in { success: 1, error: 1, complete: 1 } ) {
++ jqXHR[ i ]( s[ i ] );
++ }
++
++ // Get transport
++ transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
++
++ // If no transport, we auto-abort
++ if ( !transport ) {
++ done( -1, "No Transport" );
++ } else {
++ jqXHR.readyState = 1;
++
++ // Send global event
++ if ( fireGlobals ) {
++ globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
++ }
++
++ // If request was aborted inside ajaxSend, stop there
++ if ( state === 2 ) {
++ return jqXHR;
++ }
++
++ // Timeout
++ if ( s.async && s.timeout > 0 ) {
++ timeoutTimer = window.setTimeout( function() {
++ jqXHR.abort( "timeout" );
++ }, s.timeout );
++ }
++
++ try {
++ state = 1;
++ transport.send( requestHeaders, done );
++ } catch ( e ) {
++
++ // Propagate exception as error if not done
++ if ( state < 2 ) {
++ done( -1, e );
++
++ // Simply rethrow otherwise
++ } else {
++ throw e;
++ }
++ }
++ }
++
++ // Callback for when everything is done
++ function done( status, nativeStatusText, responses, headers ) {
++ var isSuccess, success, error, response, modified,
++ statusText = nativeStatusText;
++
++ // Called once
++ if ( state === 2 ) {
++ return;
++ }
++
++ // State is "done" now
++ state = 2;
++
++ // Clear timeout if it exists
++ if ( timeoutTimer ) {
++ window.clearTimeout( timeoutTimer );
++ }
++
++ // Dereference transport for early garbage collection
++ // (no matter how long the jqXHR object will be used)
++ transport = undefined;
++
++ // Cache response headers
++ responseHeadersString = headers || "";
++
++ // Set readyState
++ jqXHR.readyState = status > 0 ? 4 : 0;
++
++ // Determine if successful
++ isSuccess = status >= 200 && status < 300 || status === 304;
++
++ // Get response data
++ if ( responses ) {
++ response = ajaxHandleResponses( s, jqXHR, responses );
++ }
++
++ // Convert no matter what (that way responseXXX fields are always set)
++ response = ajaxConvert( s, response, jqXHR, isSuccess );
++
++ // If successful, handle type chaining
++ if ( isSuccess ) {
++
++ // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
++ if ( s.ifModified ) {
++ modified = jqXHR.getResponseHeader( "Last-Modified" );
++ if ( modified ) {
++ jQuery.lastModified[ cacheURL ] = modified;
++ }
++ modified = jqXHR.getResponseHeader( "etag" );
++ if ( modified ) {
++ jQuery.etag[ cacheURL ] = modified;
++ }
++ }
++
++ // if no content
++ if ( status === 204 || s.type === "HEAD" ) {
++ statusText = "nocontent";
++
++ // if not modified
++ } else if ( status === 304 ) {
++ statusText = "notmodified";
++
++ // If we have data, let's convert it
++ } else {
++ statusText = response.state;
++ success = response.data;
++ error = response.error;
++ isSuccess = !error;
++ }
++ } else {
++
++ // Extract error from statusText and normalize for non-aborts
++ error = statusText;
++ if ( status || !statusText ) {
++ statusText = "error";
++ if ( status < 0 ) {
++ status = 0;
++ }
++ }
++ }
++
++ // Set data for the fake xhr object
++ jqXHR.status = status;
++ jqXHR.statusText = ( nativeStatusText || statusText ) + "";
++
++ // Success/Error
++ if ( isSuccess ) {
++ deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
++ } else {
++ deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
++ }
++
++ // Status-dependent callbacks
++ jqXHR.statusCode( statusCode );
++ statusCode = undefined;
++
++ if ( fireGlobals ) {
++ globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
++ [ jqXHR, s, isSuccess ? success : error ] );
++ }
++
++ // Complete
++ completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
++
++ if ( fireGlobals ) {
++ globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
++
++ // Handle the global AJAX counter
++ if ( !( --jQuery.active ) ) {
++ jQuery.event.trigger( "ajaxStop" );
++ }
++ }
++ }
++
++ return jqXHR;
++ },
++
++ getJSON: function( url, data, callback ) {
++ return jQuery.get( url, data, callback, "json" );
++ },
++
++ getScript: function( url, callback ) {
++ return jQuery.get( url, undefined, callback, "script" );
++ }
++} );
++
++jQuery.each( [ "get", "post" ], function( i, method ) {
++ jQuery[ method ] = function( url, data, callback, type ) {
++
++ // Shift arguments if data argument was omitted
++ if ( jQuery.isFunction( data ) ) {
++ type = type || callback;
++ callback = data;
++ data = undefined;
++ }
++
++ // The url can be an options object (which then must have .url)
++ return jQuery.ajax( jQuery.extend( {
++ url: url,
++ type: method,
++ dataType: type,
++ data: data,
++ success: callback
++ }, jQuery.isPlainObject( url ) && url ) );
++ };
++} );
++
++
++jQuery._evalUrl = function( url ) {
++ return jQuery.ajax( {
++ url: url,
++
++ // Make this explicit, since user can override this through ajaxSetup (#11264)
++ type: "GET",
++ dataType: "script",
++ async: false,
++ global: false,
++ "throws": true
++ } );
++};
++
++
++jQuery.fn.extend( {
++ wrapAll: function( html ) {
++ var wrap;
++
++ if ( jQuery.isFunction( html ) ) {
++ return this.each( function( i ) {
++ jQuery( this ).wrapAll( html.call( this, i ) );
++ } );
++ }
++
++ if ( this[ 0 ] ) {
++
++ // The elements to wrap the target around
++ wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true );
++
++ if ( this[ 0 ].parentNode ) {
++ wrap.insertBefore( this[ 0 ] );
++ }
++
++ wrap.map( function() {
++ var elem = this;
++
++ while ( elem.firstElementChild ) {
++ elem = elem.firstElementChild;
++ }
++
++ return elem;
++ } ).append( this );
++ }
++
++ return this;
++ },
++
++ wrapInner: function( html ) {
++ if ( jQuery.isFunction( html ) ) {
++ return this.each( function( i ) {
++ jQuery( this ).wrapInner( html.call( this, i ) );
++ } );
++ }
++
++ return this.each( function() {
++ var self = jQuery( this ),
++ contents = self.contents();
++
++ if ( contents.length ) {
++ contents.wrapAll( html );
++
++ } else {
++ self.append( html );
++ }
++ } );
++ },
++
++ wrap: function( html ) {
++ var isFunction = jQuery.isFunction( html );
++
++ return this.each( function( i ) {
++ jQuery( this ).wrapAll( isFunction ? html.call( this, i ) : html );
++ } );
++ },
++
++ unwrap: function() {
++ return this.parent().each( function() {
++ if ( !jQuery.nodeName( this, "body" ) ) {
++ jQuery( this ).replaceWith( this.childNodes );
++ }
++ } ).end();
++ }
++} );
++
++
++jQuery.expr.filters.hidden = function( elem ) {
++ return !jQuery.expr.filters.visible( elem );
++};
++jQuery.expr.filters.visible = function( elem ) {
++
++ // Support: Opera <= 12.12
++ // Opera reports offsetWidths and offsetHeights less than zero on some elements
++ // Use OR instead of AND as the element is not visible if either is true
++ // See tickets #10406 and #13132
++ return elem.offsetWidth > 0 || elem.offsetHeight > 0 || elem.getClientRects().length > 0;
++};
++
++
++
++
++var r20 = /%20/g,
++ rbracket = /\[\]$/,
++ rCRLF = /\r?\n/g,
++ rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
++ rsubmittable = /^(?:input|select|textarea|keygen)/i;
++
++function buildParams( prefix, obj, traditional, add ) {
++ var name;
++
++ if ( jQuery.isArray( obj ) ) {
++
++ // Serialize array item.
++ jQuery.each( obj, function( i, v ) {
++ if ( traditional || rbracket.test( prefix ) ) {
++
++ // Treat each array item as a scalar.
++ add( prefix, v );
++
++ } else {
++
++ // Item is non-scalar (array or object), encode its numeric index.
++ buildParams(
++ prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]",
++ v,
++ traditional,
++ add
++ );
++ }
++ } );
++
++ } else if ( !traditional && jQuery.type( obj ) === "object" ) {
++
++ // Serialize object item.
++ for ( name in obj ) {
++ buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
++ }
++
++ } else {
++
++ // Serialize scalar item.
++ add( prefix, obj );
++ }
++}
++
++// Serialize an array of form elements or a set of
++// key/values into a query string
++jQuery.param = function( a, traditional ) {
++ var prefix,
++ s = [],
++ add = function( key, value ) {
++
++ // If value is a function, invoke it and return its value
++ value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value );
++ s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
++ };
++
++ // Set traditional to true for jQuery <= 1.3.2 behavior.
++ if ( traditional === undefined ) {
++ traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;
++ }
++
++ // If an array was passed in, assume that it is an array of form elements.
++ if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
++
++ // Serialize the form elements
++ jQuery.each( a, function() {
++ add( this.name, this.value );
++ } );
++
++ } else {
++
++ // If traditional, encode the "old" way (the way 1.3.2 or older
++ // did it), otherwise encode params recursively.
++ for ( prefix in a ) {
++ buildParams( prefix, a[ prefix ], traditional, add );
++ }
++ }
++
++ // Return the resulting serialization
++ return s.join( "&" ).replace( r20, "+" );
++};
++
++jQuery.fn.extend( {
++ serialize: function() {
++ return jQuery.param( this.serializeArray() );
++ },
++ serializeArray: function() {
++ return this.map( function() {
++
++ // Can add propHook for "elements" to filter or add form elements
++ var elements = jQuery.prop( this, "elements" );
++ return elements ? jQuery.makeArray( elements ) : this;
++ } )
++ .filter( function() {
++ var type = this.type;
++
++ // Use .is( ":disabled" ) so that fieldset[disabled] works
++ return this.name && !jQuery( this ).is( ":disabled" ) &&
++ rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
++ ( this.checked || !rcheckableType.test( type ) );
++ } )
++ .map( function( i, elem ) {
++ var val = jQuery( this ).val();
++
++ return val == null ?
++ null :
++ jQuery.isArray( val ) ?
++ jQuery.map( val, function( val ) {
++ return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
++ } ) :
++ { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
++ } ).get();
++ }
++} );
++
++
++jQuery.ajaxSettings.xhr = function() {
++ try {
++ return new window.XMLHttpRequest();
++ } catch ( e ) {}
++};
++
++var xhrSuccessStatus = {
++
++ // File protocol always yields status code 0, assume 200
++ 0: 200,
++
++ // Support: IE9
++ // #1450: sometimes IE returns 1223 when it should be 204
++ 1223: 204
++ },
++ xhrSupported = jQuery.ajaxSettings.xhr();
++
++support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
++support.ajax = xhrSupported = !!xhrSupported;
++
++jQuery.ajaxTransport( function( options ) {
++ var callback, errorCallback;
++
++ // Cross domain only allowed if supported through XMLHttpRequest
++ if ( support.cors || xhrSupported && !options.crossDomain ) {
++ return {
++ send: function( headers, complete ) {
++ var i,
++ xhr = options.xhr();
++
++ xhr.open(
++ options.type,
++ options.url,
++ options.async,
++ options.username,
++ options.password
++ );
++
++ // Apply custom fields if provided
++ if ( options.xhrFields ) {
++ for ( i in options.xhrFields ) {
++ xhr[ i ] = options.xhrFields[ i ];
++ }
++ }
++
++ // Override mime type if needed
++ if ( options.mimeType && xhr.overrideMimeType ) {
++ xhr.overrideMimeType( options.mimeType );
++ }
++
++ // X-Requested-With header
++ // For cross-domain requests, seeing as conditions for a preflight are
++ // akin to a jigsaw puzzle, we simply never set it to be sure.
++ // (it can always be set on a per-request basis or even using ajaxSetup)
++ // For same-domain requests, won't change header if already provided.
++ if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) {
++ headers[ "X-Requested-With" ] = "XMLHttpRequest";
++ }
++
++ // Set headers
++ for ( i in headers ) {
++ xhr.setRequestHeader( i, headers[ i ] );
++ }
++
++ // Callback
++ callback = function( type ) {
++ return function() {
++ if ( callback ) {
++ callback = errorCallback = xhr.onload =
++ xhr.onerror = xhr.onabort = xhr.onreadystatechange = null;
++
++ if ( type === "abort" ) {
++ xhr.abort();
++ } else if ( type === "error" ) {
++
++ // Support: IE9
++ // On a manual native abort, IE9 throws
++ // errors on any property access that is not readyState
++ if ( typeof xhr.status !== "number" ) {
++ complete( 0, "error" );
++ } else {
++ complete(
++
++ // File: protocol always yields status 0; see #8605, #14207
++ xhr.status,
++ xhr.statusText
++ );
++ }
++ } else {
++ complete(
++ xhrSuccessStatus[ xhr.status ] || xhr.status,
++ xhr.statusText,
++
++ // Support: IE9 only
++ // IE9 has no XHR2 but throws on binary (trac-11426)
++ // For XHR2 non-text, let the caller handle it (gh-2498)
++ ( xhr.responseType || "text" ) !== "text" ||
++ typeof xhr.responseText !== "string" ?
++ { binary: xhr.response } :
++ { text: xhr.responseText },
++ xhr.getAllResponseHeaders()
++ );
++ }
++ }
++ };
++ };
++
++ // Listen to events
++ xhr.onload = callback();
++ errorCallback = xhr.onerror = callback( "error" );
++
++ // Support: IE9
++ // Use onreadystatechange to replace onabort
++ // to handle uncaught aborts
++ if ( xhr.onabort !== undefined ) {
++ xhr.onabort = errorCallback;
++ } else {
++ xhr.onreadystatechange = function() {
++
++ // Check readyState before timeout as it changes
++ if ( xhr.readyState === 4 ) {
++
++ // Allow onerror to be called first,
++ // but that will not handle a native abort
++ // Also, save errorCallback to a variable
++ // as xhr.onerror cannot be accessed
++ window.setTimeout( function() {
++ if ( callback ) {
++ errorCallback();
++ }
++ } );
++ }
++ };
++ }
++
++ // Create the abort callback
++ callback = callback( "abort" );
++
++ try {
++
++ // Do send the request (this may raise an exception)
++ xhr.send( options.hasContent && options.data || null );
++ } catch ( e ) {
++
++ // #14683: Only rethrow if this hasn't been notified as an error yet
++ if ( callback ) {
++ throw e;
++ }
++ }
++ },
++
++ abort: function() {
++ if ( callback ) {
++ callback();
++ }
++ }
++ };
++ }
++} );
++
++
++
++
++// Install script dataType
++jQuery.ajaxSetup( {
++ accepts: {
++ script: "text/javascript, application/javascript, " +
++ "application/ecmascript, application/x-ecmascript"
++ },
++ contents: {
++ script: /\b(?:java|ecma)script\b/
++ },
++ converters: {
++ "text script": function( text ) {
++ jQuery.globalEval( text );
++ return text;
++ }
++ }
++} );
++
++// Handle cache's special case and crossDomain
++jQuery.ajaxPrefilter( "script", function( s ) {
++ if ( s.cache === undefined ) {
++ s.cache = false;
++ }
++ if ( s.crossDomain ) {
++ s.type = "GET";
++ }
++} );
++
++// Bind script tag hack transport
++jQuery.ajaxTransport( "script", function( s ) {
++
++ // This transport only deals with cross domain requests
++ if ( s.crossDomain ) {
++ var script, callback;
++ return {
++ send: function( _, complete ) {
++ script = jQuery( "<script>" ).prop( {
++ charset: s.scriptCharset,
++ src: s.url
++ } ).on(
++ "load error",
++ callback = function( evt ) {
++ script.remove();
++ callback = null;
++ if ( evt ) {
++ complete( evt.type === "error" ? 404 : 200, evt.type );
++ }
++ }
++ );
++
++ // Use native DOM manipulation to avoid our domManip AJAX trickery
++ document.head.appendChild( script[ 0 ] );
++ },
++ abort: function() {
++ if ( callback ) {
++ callback();
++ }
++ }
++ };
++ }
++} );
++
++
++
++
++var oldCallbacks = [],
++ rjsonp = /(=)\?(?=&|$)|\?\?/;
++
++// Default jsonp settings
++jQuery.ajaxSetup( {
++ jsonp: "callback",
++ jsonpCallback: function() {
++ var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) );
++ this[ callback ] = true;
++ return callback;
++ }
++} );
++
++// Detect, normalize options and install callbacks for jsonp requests
++jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
++
++ var callbackName, overwritten, responseContainer,
++ jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
++ "url" :
++ typeof s.data === "string" &&
++ ( s.contentType || "" )
++ .indexOf( "application/x-www-form-urlencoded" ) === 0 &&
++ rjsonp.test( s.data ) && "data"
++ );
++
++ // Handle iff the expected data type is "jsonp" or we have a parameter to set
++ if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {
++
++ // Get callback name, remembering preexisting value associated with it
++ callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?
++ s.jsonpCallback() :
++ s.jsonpCallback;
++
++ // Insert callback into url or form data
++ if ( jsonProp ) {
++ s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
++ } else if ( s.jsonp !== false ) {
++ s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
++ }
++
++ // Use data converter to retrieve json after script execution
++ s.converters[ "script json" ] = function() {
++ if ( !responseContainer ) {
++ jQuery.error( callbackName + " was not called" );
++ }
++ return responseContainer[ 0 ];
++ };
++
++ // Force json dataType
++ s.dataTypes[ 0 ] = "json";
++
++ // Install callback
++ overwritten = window[ callbackName ];
++ window[ callbackName ] = function() {
++ responseContainer = arguments;
++ };
++
++ // Clean-up function (fires after converters)
++ jqXHR.always( function() {
++
++ // If previous value didn't exist - remove it
++ if ( overwritten === undefined ) {
++ jQuery( window ).removeProp( callbackName );
++
++ // Otherwise restore preexisting value
++ } else {
++ window[ callbackName ] = overwritten;
++ }
++
++ // Save back as free
++ if ( s[ callbackName ] ) {
++
++ // Make sure that re-using the options doesn't screw things around
++ s.jsonpCallback = originalSettings.jsonpCallback;
++
++ // Save the callback name for future use
++ oldCallbacks.push( callbackName );
++ }
++
++ // Call if it was a function and we have a response
++ if ( responseContainer && jQuery.isFunction( overwritten ) ) {
++ overwritten( responseContainer[ 0 ] );
++ }
++
++ responseContainer = overwritten = undefined;
++ } );
++
++ // Delegate to script
++ return "script";
++ }
++} );
++
++
++
++
++// Argument "data" should be 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
++jQuery.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 = buildFragment( [ data ], context, scripts );
++
++ if ( scripts && scripts.length ) {
++ jQuery( scripts ).remove();
++ }
++
++ return jQuery.merge( [], parsed.childNodes );
++};
++
++
++// Keep a copy of the old load method
++var _load = jQuery.fn.load;
++
++/**
++ * Load a url into a page
++ */
++jQuery.fn.load = function( url, params, callback ) {
++ if ( typeof url !== "string" && _load ) {
++ return _load.apply( this, arguments );
++ }
++
++ var selector, type, response,
++ self = this,
++ off = url.indexOf( " " );
++
++ if ( off > -1 ) {
++ selector = jQuery.trim( url.slice( off ) );
++ url = url.slice( 0, off );
++ }
++
++ // If it's a function
++ if ( jQuery.isFunction( params ) ) {
++
++ // We assume that it's the callback
++ callback = params;
++ params = undefined;
++
++ // Otherwise, build a param string
++ } else if ( params && typeof params === "object" ) {
++ type = "POST";
++ }
++
++ // If we have elements to modify, make the request
++ if ( self.length > 0 ) {
++ jQuery.ajax( {
++ url: url,
++
++ // If "type" variable is undefined, then "GET" method will be used.
++ // Make value of this field explicit since
++ // user can override it through ajaxSetup method
++ type: type || "GET",
++ dataType: "html",
++ data: params
++ } ).done( function( responseText ) {
++
++ // Save response for use in complete callback
++ response = arguments;
++
++ self.html( selector ?
++
++ // If a selector was specified, locate the right elements in a dummy div
++ // Exclude scripts to avoid IE 'Permission Denied' errors
++ jQuery( "<div>" ).append( jQuery.parseHTML( responseText ) ).find( selector ) :
++
++ // Otherwise use the full result
++ responseText );
++
++ // If the request succeeds, this function gets "data", "status", "jqXHR"
++ // but they are ignored because response was set above.
++ // If it fails, this function gets "jqXHR", "status", "error"
++ } ).always( callback && function( jqXHR, status ) {
++ self.each( function() {
++ callback.apply( this, response || [ jqXHR.responseText, status, jqXHR ] );
++ } );
++ } );
++ }
++
++ return this;
++};
++
++
++
++
++// Attach a bunch of functions for handling common AJAX events
++jQuery.each( [
++ "ajaxStart",
++ "ajaxStop",
++ "ajaxComplete",
++ "ajaxError",
++ "ajaxSuccess",
++ "ajaxSend"
++], function( i, type ) {
++ jQuery.fn[ type ] = function( fn ) {
++ return this.on( type, fn );
++ };
++} );
++
++
++
++
++jQuery.expr.filters.animated = function( elem ) {
++ return jQuery.grep( jQuery.timers, function( fn ) {
++ return elem === fn.elem;
++ } ).length;
++};
++
++
++
++
++/**
++ * Gets a window from an element
++ */
++function getWindow( elem ) {
++ return jQuery.isWindow( elem ) ? elem : elem.nodeType === 9 && elem.defaultView;
++}
++
++jQuery.offset = {
++ setOffset: function( elem, options, i ) {
++ var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
++ position = jQuery.css( elem, "position" ),
++ curElem = jQuery( elem ),
++ props = {};
++
++ // Set position first, in-case top/left are set even on static elem
++ if ( position === "static" ) {
++ elem.style.position = "relative";
++ }
++
++ curOffset = curElem.offset();
++ curCSSTop = jQuery.css( elem, "top" );
++ curCSSLeft = jQuery.css( elem, "left" );
++ calculatePosition = ( position === "absolute" || position === "fixed" ) &&
++ ( curCSSTop + curCSSLeft ).indexOf( "auto" ) > -1;
++
++ // Need to be able to calculate position if either
++ // top or left is auto and position is either absolute or fixed
++ if ( calculatePosition ) {
++ curPosition = curElem.position();
++ curTop = curPosition.top;
++ curLeft = curPosition.left;
++
++ } else {
++ curTop = parseFloat( curCSSTop ) || 0;
++ curLeft = parseFloat( curCSSLeft ) || 0;
++ }
++
++ if ( jQuery.isFunction( options ) ) {
++
++ // Use jQuery.extend here to allow modification of coordinates argument (gh-1848)
++ options = options.call( elem, i, jQuery.extend( {}, curOffset ) );
++ }
++
++ if ( options.top != null ) {
++ props.top = ( options.top - curOffset.top ) + curTop;
++ }
++ if ( options.left != null ) {
++ props.left = ( options.left - curOffset.left ) + curLeft;
++ }
++
++ if ( "using" in options ) {
++ options.using.call( elem, props );
++
++ } else {
++ curElem.css( props );
++ }
++ }
++};
++
++jQuery.fn.extend( {
++ offset: function( options ) {
++ if ( arguments.length ) {
++ return options === undefined ?
++ this :
++ this.each( function( i ) {
++ jQuery.offset.setOffset( this, options, i );
++ } );
++ }
++
++ var docElem, win,
++ elem = this[ 0 ],
++ box = { top: 0, left: 0 },
++ doc = elem && elem.ownerDocument;
++
++ if ( !doc ) {
++ return;
++ }
++
++ docElem = doc.documentElement;
++
++ // Make sure it's not a disconnected DOM node
++ if ( !jQuery.contains( docElem, elem ) ) {
++ return box;
++ }
++
++ box = elem.getBoundingClientRect();
++ win = getWindow( doc );
++ return {
++ top: box.top + win.pageYOffset - docElem.clientTop,
++ left: box.left + win.pageXOffset - docElem.clientLeft
++ };
++ },
++
++ position: function() {
++ if ( !this[ 0 ] ) {
++ return;
++ }
++
++ var offsetParent, offset,
++ elem = this[ 0 ],
++ parentOffset = { top: 0, left: 0 };
++
++ // Fixed elements are offset from window (parentOffset = {top:0, left: 0},
++ // because it is its only offset parent
++ if ( jQuery.css( elem, "position" ) === "fixed" ) {
++
++ // Assume getBoundingClientRect is there when computed position is fixed
++ offset = elem.getBoundingClientRect();
++
++ } else {
++
++ // Get *real* offsetParent
++ offsetParent = this.offsetParent();
++
++ // Get correct offsets
++ offset = this.offset();
++ if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) {
++ parentOffset = offsetParent.offset();
++ }
++
++ // Add offsetParent borders
++ parentOffset.top += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true );
++ parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true );
++ }
++
++ // Subtract parent offsets and element margins
++ return {
++ top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
++ left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true )
++ };
++ },
++
++ // This method will return documentElement in the following cases:
++ // 1) For the element inside the iframe without offsetParent, this method will return
++ // documentElement of the parent window
++ // 2) For the hidden or detached element
++ // 3) For body or html element, i.e. in case of the html node - it will return itself
++ //
++ // but those exceptions were never presented as a real life use-cases
++ // and might be considered as more preferable results.
++ //
++ // This logic, however, is not guaranteed and can change at any point in the future
++ offsetParent: function() {
++ return this.map( function() {
++ var offsetParent = this.offsetParent;
++
++ while ( offsetParent && jQuery.css( offsetParent, "position" ) === "static" ) {
++ offsetParent = offsetParent.offsetParent;
++ }
++
++ return offsetParent || documentElement;
++ } );
++ }
++} );
++
++// Create scrollLeft and scrollTop methods
++jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) {
++ var top = "pageYOffset" === prop;
++
++ jQuery.fn[ method ] = function( val ) {
++ return access( this, function( elem, method, val ) {
++ var win = getWindow( elem );
++
++ if ( val === undefined ) {
++ return win ? win[ prop ] : elem[ method ];
++ }
++
++ if ( win ) {
++ win.scrollTo(
++ !top ? val : win.pageXOffset,
++ top ? val : win.pageYOffset
++ );
++
++ } else {
++ elem[ method ] = val;
++ }
++ }, method, val, arguments.length );
++ };
++} );
++
++// Support: Safari<7-8+, Chrome<37-44+
++// Add the top/left cssHooks using jQuery.fn.position
++// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
++// Blink bug: https://code.google.com/p/chromium/issues/detail?id=229280
++// getComputedStyle returns percent when specified for top/left/bottom/right;
++// rather than make the css module depend on the offset module, just check for it here
++jQuery.each( [ "top", "left" ], function( i, prop ) {
++ jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition,
++ function( elem, computed ) {
++ if ( computed ) {
++ computed = curCSS( elem, prop );
++
++ // If curCSS returns percentage, fallback to offset
++ return rnumnonpx.test( computed ) ?
++ jQuery( elem ).position()[ prop ] + "px" :
++ computed;
++ }
++ }
++ );
++} );
++
++
++// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
++jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
++ jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name },
++ function( defaultExtra, funcName ) {
++
++ // Margin is only for outerHeight, outerWidth
++ jQuery.fn[ funcName ] = function( margin, value ) {
++ var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
++ extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
++
++ return access( this, function( elem, type, value ) {
++ var doc;
++
++ if ( jQuery.isWindow( elem ) ) {
++
++ // As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
++ // isn't a whole lot we can do. See pull request at this URL for discussion:
++ // https://github.com/jquery/jquery/pull/764
++ return elem.document.documentElement[ "client" + name ];
++ }
++
++ // Get document width or height
++ if ( elem.nodeType === 9 ) {
++ doc = elem.documentElement;
++
++ // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height],
++ // whichever is greatest
++ return Math.max(
++ elem.body[ "scroll" + name ], doc[ "scroll" + name ],
++ elem.body[ "offset" + name ], doc[ "offset" + name ],
++ doc[ "client" + name ]
++ );
++ }
++
++ return value === undefined ?
++
++ // Get width or height on the element, requesting but not forcing parseFloat
++ jQuery.css( elem, type, extra ) :
++
++ // Set width or height on the element
++ jQuery.style( elem, type, value, extra );
++ }, type, chainable ? margin : undefined, chainable, null );
++ };
++ } );
++} );
++
++
++jQuery.fn.extend( {
++
++ bind: function( types, data, fn ) {
++ return this.on( types, null, data, fn );
++ },
++ unbind: function( types, fn ) {
++ return this.off( types, null, fn );
++ },
++
++ delegate: function( selector, types, data, fn ) {
++ return this.on( types, selector, data, fn );
++ },
++ undelegate: function( selector, types, fn ) {
++
++ // ( namespace ) or ( selector, types [, fn] )
++ return arguments.length === 1 ?
++ this.off( selector, "**" ) :
++ this.off( types, selector || "**", fn );
++ },
++ size: function() {
++ return this.length;
++ }
++} );
++
++jQuery.fn.andSelf = jQuery.fn.addBack;
++
++
++
++
++// Register as a named AMD module, since jQuery can be concatenated with other
++// files that may use define, but not via a proper concatenation script that
++// understands anonymous AMD modules. A named AMD is safest and most robust
++// way to register. Lowercase jquery is used because AMD module names are
++// derived from file names, and jQuery is normally delivered in a lowercase
++// file name. Do this after creating the global so that if an AMD module wants
++// to call noConflict to hide this version of jQuery, it will work.
++
++// Note that for maximum portability, libraries that are not jQuery should
++// declare themselves as anonymous modules, and avoid setting a global if an
++// AMD loader is present. jQuery is a special case. For more information, see
++// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon
++
++if ( typeof define === "function" && define.amd ) {
++ define( "jquery", [], function() {
++ return jQuery;
++ } );
++}
++
++
++
++var
++
++ // Map over jQuery in case of overwrite
++ _jQuery = window.jQuery,
++
++ // Map over the $ in case of overwrite
++ _$ = window.$;
++
++jQuery.noConflict = function( deep ) {
++ if ( window.$ === jQuery ) {
++ window.$ = _$;
++ }
++
++ if ( deep && window.jQuery === jQuery ) {
++ window.jQuery = _jQuery;
++ }
++
++ return jQuery;
++};
++
++// Expose jQuery and $ identifiers, even in AMD
++// (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
++// and CommonJS for browser emulators (#13566)
++if ( !noGlobal ) {
++ window.jQuery = window.$ = jQuery;
++}
++
++return jQuery;
++}));
--- /dev/null
--- /dev/null
++/*!
++ * Chart.js
++ * http://chartjs.org/
++ * Version: 2.4.0
++ *
++ * Copyright 2016 Nick Downie
++ * Released under the MIT license
++ * https://github.com/chartjs/Chart.js/blob/master/LICENSE.md
++ */
++(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Chart = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
++
++},{}],2:[function(require,module,exports){
++/* MIT license */\r
++var colorNames = require(6);\r
++\r
++module.exports = {\r
++ getRgba: getRgba,\r
++ getHsla: getHsla,\r
++ getRgb: getRgb,\r
++ getHsl: getHsl,\r
++ getHwb: getHwb,\r
++ getAlpha: getAlpha,\r
++\r
++ hexString: hexString,\r
++ rgbString: rgbString,\r
++ rgbaString: rgbaString,\r
++ percentString: percentString,\r
++ percentaString: percentaString,\r
++ hslString: hslString,\r
++ hslaString: hslaString,\r
++ hwbString: hwbString,\r
++ keyword: keyword\r
++}\r
++\r
++function getRgba(string) {\r
++ if (!string) {\r
++ return;\r
++ }\r
++ var abbr = /^#([a-fA-F0-9]{3})$/,\r
++ hex = /^#([a-fA-F0-9]{6})$/,\r
++ rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/,\r
++ per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/,\r
++ keyword = /(\w+)/;\r
++\r
++ var rgb = [0, 0, 0],\r
++ a = 1,\r
++ match = string.match(abbr);\r
++ if (match) {\r
++ match = match[1];\r
++ for (var i = 0; i < rgb.length; i++) {\r
++ rgb[i] = parseInt(match[i] + match[i], 16);\r
++ }\r
++ }\r
++ else if (match = string.match(hex)) {\r
++ match = match[1];\r
++ for (var i = 0; i < rgb.length; i++) {\r
++ rgb[i] = parseInt(match.slice(i * 2, i * 2 + 2), 16);\r
++ }\r
++ }\r
++ else if (match = string.match(rgba)) {\r
++ for (var i = 0; i < rgb.length; i++) {\r
++ rgb[i] = parseInt(match[i + 1]);\r
++ }\r
++ a = parseFloat(match[4]);\r
++ }\r
++ else if (match = string.match(per)) {\r
++ for (var i = 0; i < rgb.length; i++) {\r
++ rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55);\r
++ }\r
++ a = parseFloat(match[4]);\r
++ }\r
++ else if (match = string.match(keyword)) {\r
++ if (match[1] == "transparent") {\r
++ return [0, 0, 0, 0];\r
++ }\r
++ rgb = colorNames[match[1]];\r
++ if (!rgb) {\r
++ return;\r
++ }\r
++ }\r
++\r
++ for (var i = 0; i < rgb.length; i++) {\r
++ rgb[i] = scale(rgb[i], 0, 255);\r
++ }\r
++ if (!a && a != 0) {\r
++ a = 1;\r
++ }\r
++ else {\r
++ a = scale(a, 0, 1);\r
++ }\r
++ rgb[3] = a;\r
++ return rgb;\r
++}\r
++\r
++function getHsla(string) {\r
++ if (!string) {\r
++ return;\r
++ }\r
++ var hsl = /^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/;\r
++ var match = string.match(hsl);\r
++ if (match) {\r
++ var alpha = parseFloat(match[4]);\r
++ var h = scale(parseInt(match[1]), 0, 360),\r
++ s = scale(parseFloat(match[2]), 0, 100),\r
++ l = scale(parseFloat(match[3]), 0, 100),\r
++ a = scale(isNaN(alpha) ? 1 : alpha, 0, 1);\r
++ return [h, s, l, a];\r
++ }\r
++}\r
++\r
++function getHwb(string) {\r
++ if (!string) {\r
++ return;\r
++ }\r
++ var hwb = /^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/;\r
++ var match = string.match(hwb);\r
++ if (match) {\r
++ var alpha = parseFloat(match[4]);\r
++ var h = scale(parseInt(match[1]), 0, 360),\r
++ w = scale(parseFloat(match[2]), 0, 100),\r
++ b = scale(parseFloat(match[3]), 0, 100),\r
++ a = scale(isNaN(alpha) ? 1 : alpha, 0, 1);\r
++ return [h, w, b, a];\r
++ }\r
++}\r
++\r
++function getRgb(string) {\r
++ var rgba = getRgba(string);\r
++ return rgba && rgba.slice(0, 3);\r
++}\r
++\r
++function getHsl(string) {\r
++ var hsla = getHsla(string);\r
++ return hsla && hsla.slice(0, 3);\r
++}\r
++\r
++function getAlpha(string) {\r
++ var vals = getRgba(string);\r
++ if (vals) {\r
++ return vals[3];\r
++ }\r
++ else if (vals = getHsla(string)) {\r
++ return vals[3];\r
++ }\r
++ else if (vals = getHwb(string)) {\r
++ return vals[3];\r
++ }\r
++}\r
++\r
++// generators\r
++function hexString(rgb) {\r
++ return "#" + hexDouble(rgb[0]) + hexDouble(rgb[1])\r
++ + hexDouble(rgb[2]);\r
++}\r
++\r
++function rgbString(rgba, alpha) {\r
++ if (alpha < 1 || (rgba[3] && rgba[3] < 1)) {\r
++ return rgbaString(rgba, alpha);\r
++ }\r
++ return "rgb(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + ")";\r
++}\r
++\r
++function rgbaString(rgba, alpha) {\r
++ if (alpha === undefined) {\r
++ alpha = (rgba[3] !== undefined ? rgba[3] : 1);\r
++ }\r
++ return "rgba(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2]\r
++ + ", " + alpha + ")";\r
++}\r
++\r
++function percentString(rgba, alpha) {\r
++ if (alpha < 1 || (rgba[3] && rgba[3] < 1)) {\r
++ return percentaString(rgba, alpha);\r
++ }\r
++ var r = Math.round(rgba[0]/255 * 100),\r
++ g = Math.round(rgba[1]/255 * 100),\r
++ b = Math.round(rgba[2]/255 * 100);\r
++\r
++ return "rgb(" + r + "%, " + g + "%, " + b + "%)";\r
++}\r
++\r
++function percentaString(rgba, alpha) {\r
++ var r = Math.round(rgba[0]/255 * 100),\r
++ g = Math.round(rgba[1]/255 * 100),\r
++ b = Math.round(rgba[2]/255 * 100);\r
++ return "rgba(" + r + "%, " + g + "%, " + b + "%, " + (alpha || rgba[3] || 1) + ")";\r
++}\r
++\r
++function hslString(hsla, alpha) {\r
++ if (alpha < 1 || (hsla[3] && hsla[3] < 1)) {\r
++ return hslaString(hsla, alpha);\r
++ }\r
++ return "hsl(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%)";\r
++}\r
++\r
++function hslaString(hsla, alpha) {\r
++ if (alpha === undefined) {\r
++ alpha = (hsla[3] !== undefined ? hsla[3] : 1);\r
++ }\r
++ return "hsla(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%, "\r
++ + alpha + ")";\r
++}\r
++\r
++// hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax\r
++// (hwb have alpha optional & 1 is default value)\r
++function hwbString(hwb, alpha) {\r
++ if (alpha === undefined) {\r
++ alpha = (hwb[3] !== undefined ? hwb[3] : 1);\r
++ }\r
++ return "hwb(" + hwb[0] + ", " + hwb[1] + "%, " + hwb[2] + "%"\r
++ + (alpha !== undefined && alpha !== 1 ? ", " + alpha : "") + ")";\r
++}\r
++\r
++function keyword(rgb) {\r
++ return reverseNames[rgb.slice(0, 3)];\r
++}\r
++\r
++// helpers\r
++function scale(num, min, max) {\r
++ return Math.min(Math.max(min, num), max);\r
++}\r
++\r
++function hexDouble(num) {\r
++ var str = num.toString(16).toUpperCase();\r
++ return (str.length < 2) ? "0" + str : str;\r
++}\r
++\r
++\r
++//create a list of reverse color names\r
++var reverseNames = {};\r
++for (var name in colorNames) {\r
++ reverseNames[colorNames[name]] = name;\r
++}\r
++
++},{"6":6}],3:[function(require,module,exports){
++/* MIT license */\r
++var convert = require(5);\r
++var string = require(2);\r
++\r
++var Color = function (obj) {\r
++ if (obj instanceof Color) {\r
++ return obj;\r
++ }\r
++ if (!(this instanceof Color)) {\r
++ return new Color(obj);\r
++ }\r
++\r
++ this.values = {\r
++ rgb: [0, 0, 0],\r
++ hsl: [0, 0, 0],\r
++ hsv: [0, 0, 0],\r
++ hwb: [0, 0, 0],\r
++ cmyk: [0, 0, 0, 0],\r
++ alpha: 1\r
++ };\r
++\r
++ // parse Color() argument\r
++ var vals;\r
++ if (typeof obj === 'string') {\r
++ vals = string.getRgba(obj);\r
++ if (vals) {\r
++ this.setValues('rgb', vals);\r
++ } else if (vals = string.getHsla(obj)) {\r
++ this.setValues('hsl', vals);\r
++ } else if (vals = string.getHwb(obj)) {\r
++ this.setValues('hwb', vals);\r
++ } else {\r
++ throw new Error('Unable to parse color from string "' + obj + '"');\r
++ }\r
++ } else if (typeof obj === 'object') {\r
++ vals = obj;\r
++ if (vals.r !== undefined || vals.red !== undefined) {\r
++ this.setValues('rgb', vals);\r
++ } else if (vals.l !== undefined || vals.lightness !== undefined) {\r
++ this.setValues('hsl', vals);\r
++ } else if (vals.v !== undefined || vals.value !== undefined) {\r
++ this.setValues('hsv', vals);\r
++ } else if (vals.w !== undefined || vals.whiteness !== undefined) {\r
++ this.setValues('hwb', vals);\r
++ } else if (vals.c !== undefined || vals.cyan !== undefined) {\r
++ this.setValues('cmyk', vals);\r
++ } else {\r
++ throw new Error('Unable to parse color from object ' + JSON.stringify(obj));\r
++ }\r
++ }\r
++};\r
++\r
++Color.prototype = {\r
++ rgb: function () {\r
++ return this.setSpace('rgb', arguments);\r
++ },\r
++ hsl: function () {\r
++ return this.setSpace('hsl', arguments);\r
++ },\r
++ hsv: function () {\r
++ return this.setSpace('hsv', arguments);\r
++ },\r
++ hwb: function () {\r
++ return this.setSpace('hwb', arguments);\r
++ },\r
++ cmyk: function () {\r
++ return this.setSpace('cmyk', arguments);\r
++ },\r
++\r
++ rgbArray: function () {\r
++ return this.values.rgb;\r
++ },\r
++ hslArray: function () {\r
++ return this.values.hsl;\r
++ },\r
++ hsvArray: function () {\r
++ return this.values.hsv;\r
++ },\r
++ hwbArray: function () {\r
++ var values = this.values;\r
++ if (values.alpha !== 1) {\r
++ return values.hwb.concat([values.alpha]);\r
++ }\r
++ return values.hwb;\r
++ },\r
++ cmykArray: function () {\r
++ return this.values.cmyk;\r
++ },\r
++ rgbaArray: function () {\r
++ var values = this.values;\r
++ return values.rgb.concat([values.alpha]);\r
++ },\r
++ hslaArray: function () {\r
++ var values = this.values;\r
++ return values.hsl.concat([values.alpha]);\r
++ },\r
++ alpha: function (val) {\r
++ if (val === undefined) {\r
++ return this.values.alpha;\r
++ }\r
++ this.setValues('alpha', val);\r
++ return this;\r
++ },\r
++\r
++ red: function (val) {\r
++ return this.setChannel('rgb', 0, val);\r
++ },\r
++ green: function (val) {\r
++ return this.setChannel('rgb', 1, val);\r
++ },\r
++ blue: function (val) {\r
++ return this.setChannel('rgb', 2, val);\r
++ },\r
++ hue: function (val) {\r
++ if (val) {\r
++ val %= 360;\r
++ val = val < 0 ? 360 + val : val;\r
++ }\r
++ return this.setChannel('hsl', 0, val);\r
++ },\r
++ saturation: function (val) {\r
++ return this.setChannel('hsl', 1, val);\r
++ },\r
++ lightness: function (val) {\r
++ return this.setChannel('hsl', 2, val);\r
++ },\r
++ saturationv: function (val) {\r
++ return this.setChannel('hsv', 1, val);\r
++ },\r
++ whiteness: function (val) {\r
++ return this.setChannel('hwb', 1, val);\r
++ },\r
++ blackness: function (val) {\r
++ return this.setChannel('hwb', 2, val);\r
++ },\r
++ value: function (val) {\r
++ return this.setChannel('hsv', 2, val);\r
++ },\r
++ cyan: function (val) {\r
++ return this.setChannel('cmyk', 0, val);\r
++ },\r
++ magenta: function (val) {\r
++ return this.setChannel('cmyk', 1, val);\r
++ },\r
++ yellow: function (val) {\r
++ return this.setChannel('cmyk', 2, val);\r
++ },\r
++ black: function (val) {\r
++ return this.setChannel('cmyk', 3, val);\r
++ },\r
++\r
++ hexString: function () {\r
++ return string.hexString(this.values.rgb);\r
++ },\r
++ rgbString: function () {\r
++ return string.rgbString(this.values.rgb, this.values.alpha);\r
++ },\r
++ rgbaString: function () {\r
++ return string.rgbaString(this.values.rgb, this.values.alpha);\r
++ },\r
++ percentString: function () {\r
++ return string.percentString(this.values.rgb, this.values.alpha);\r
++ },\r
++ hslString: function () {\r
++ return string.hslString(this.values.hsl, this.values.alpha);\r
++ },\r
++ hslaString: function () {\r
++ return string.hslaString(this.values.hsl, this.values.alpha);\r
++ },\r
++ hwbString: function () {\r
++ return string.hwbString(this.values.hwb, this.values.alpha);\r
++ },\r
++ keyword: function () {\r
++ return string.keyword(this.values.rgb, this.values.alpha);\r
++ },\r
++\r
++ rgbNumber: function () {\r
++ var rgb = this.values.rgb;\r
++ return (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];\r
++ },\r
++\r
++ luminosity: function () {\r
++ // http://www.w3.org/TR/WCAG20/#relativeluminancedef\r
++ var rgb = this.values.rgb;\r
++ var lum = [];\r
++ for (var i = 0; i < rgb.length; i++) {\r
++ var chan = rgb[i] / 255;\r
++ lum[i] = (chan <= 0.03928) ? chan / 12.92 : Math.pow(((chan + 0.055) / 1.055), 2.4);\r
++ }\r
++ return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2];\r
++ },\r
++\r
++ contrast: function (color2) {\r
++ // http://www.w3.org/TR/WCAG20/#contrast-ratiodef\r
++ var lum1 = this.luminosity();\r
++ var lum2 = color2.luminosity();\r
++ if (lum1 > lum2) {\r
++ return (lum1 + 0.05) / (lum2 + 0.05);\r
++ }\r
++ return (lum2 + 0.05) / (lum1 + 0.05);\r
++ },\r
++\r
++ level: function (color2) {\r
++ var contrastRatio = this.contrast(color2);\r
++ if (contrastRatio >= 7.1) {\r
++ return 'AAA';\r
++ }\r
++\r
++ return (contrastRatio >= 4.5) ? 'AA' : '';\r
++ },\r
++\r
++ dark: function () {\r
++ // YIQ equation from http://24ways.org/2010/calculating-color-contrast\r
++ var rgb = this.values.rgb;\r
++ var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000;\r
++ return yiq < 128;\r
++ },\r
++\r
++ light: function () {\r
++ return !this.dark();\r
++ },\r
++\r
++ negate: function () {\r
++ var rgb = [];\r
++ for (var i = 0; i < 3; i++) {\r
++ rgb[i] = 255 - this.values.rgb[i];\r
++ }\r
++ this.setValues('rgb', rgb);\r
++ return this;\r
++ },\r
++\r
++ lighten: function (ratio) {\r
++ var hsl = this.values.hsl;\r
++ hsl[2] += hsl[2] * ratio;\r
++ this.setValues('hsl', hsl);\r
++ return this;\r
++ },\r
++\r
++ darken: function (ratio) {\r
++ var hsl = this.values.hsl;\r
++ hsl[2] -= hsl[2] * ratio;\r
++ this.setValues('hsl', hsl);\r
++ return this;\r
++ },\r
++\r
++ saturate: function (ratio) {\r
++ var hsl = this.values.hsl;\r
++ hsl[1] += hsl[1] * ratio;\r
++ this.setValues('hsl', hsl);\r
++ return this;\r
++ },\r
++\r
++ desaturate: function (ratio) {\r
++ var hsl = this.values.hsl;\r
++ hsl[1] -= hsl[1] * ratio;\r
++ this.setValues('hsl', hsl);\r
++ return this;\r
++ },\r
++\r
++ whiten: function (ratio) {\r
++ var hwb = this.values.hwb;\r
++ hwb[1] += hwb[1] * ratio;\r
++ this.setValues('hwb', hwb);\r
++ return this;\r
++ },\r
++\r
++ blacken: function (ratio) {\r
++ var hwb = this.values.hwb;\r
++ hwb[2] += hwb[2] * ratio;\r
++ this.setValues('hwb', hwb);\r
++ return this;\r
++ },\r
++\r
++ greyscale: function () {\r
++ var rgb = this.values.rgb;\r
++ // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale\r
++ var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11;\r
++ this.setValues('rgb', [val, val, val]);\r
++ return this;\r
++ },\r
++\r
++ clearer: function (ratio) {\r
++ var alpha = this.values.alpha;\r
++ this.setValues('alpha', alpha - (alpha * ratio));\r
++ return this;\r
++ },\r
++\r
++ opaquer: function (ratio) {\r
++ var alpha = this.values.alpha;\r
++ this.setValues('alpha', alpha + (alpha * ratio));\r
++ return this;\r
++ },\r
++\r
++ rotate: function (degrees) {\r
++ var hsl = this.values.hsl;\r
++ var hue = (hsl[0] + degrees) % 360;\r
++ hsl[0] = hue < 0 ? 360 + hue : hue;\r
++ this.setValues('hsl', hsl);\r
++ return this;\r
++ },\r
++\r
++ /**\r
++ * Ported from sass implementation in C\r
++ * https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209\r
++ */\r
++ mix: function (mixinColor, weight) {\r
++ var color1 = this;\r
++ var color2 = mixinColor;\r
++ var p = weight === undefined ? 0.5 : weight;\r
++\r
++ var w = 2 * p - 1;\r
++ var a = color1.alpha() - color2.alpha();\r
++\r
++ var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;\r
++ var w2 = 1 - w1;\r
++\r
++ return this\r
++ .rgb(\r
++ w1 * color1.red() + w2 * color2.red(),\r
++ w1 * color1.green() + w2 * color2.green(),\r
++ w1 * color1.blue() + w2 * color2.blue()\r
++ )\r
++ .alpha(color1.alpha() * p + color2.alpha() * (1 - p));\r
++ },\r
++\r
++ toJSON: function () {\r
++ return this.rgb();\r
++ },\r
++\r
++ clone: function () {\r
++ // NOTE(SB): using node-clone creates a dependency to Buffer when using browserify,\r
++ // making the final build way to big to embed in Chart.js. So let's do it manually,\r
++ // assuming that values to clone are 1 dimension arrays containing only numbers,\r
++ // except 'alpha' which is a number.\r
++ var result = new Color();\r
++ var source = this.values;\r
++ var target = result.values;\r
++ var value, type;\r
++\r
++ for (var prop in source) {\r
++ if (source.hasOwnProperty(prop)) {\r
++ value = source[prop];\r
++ type = ({}).toString.call(value);\r
++ if (type === '[object Array]') {\r
++ target[prop] = value.slice(0);\r
++ } else if (type === '[object Number]') {\r
++ target[prop] = value;\r
++ } else {\r
++ console.error('unexpected color value:', value);\r
++ }\r
++ }\r
++ }\r
++\r
++ return result;\r
++ }\r
++};\r
++\r
++Color.prototype.spaces = {\r
++ rgb: ['red', 'green', 'blue'],\r
++ hsl: ['hue', 'saturation', 'lightness'],\r
++ hsv: ['hue', 'saturation', 'value'],\r
++ hwb: ['hue', 'whiteness', 'blackness'],\r
++ cmyk: ['cyan', 'magenta', 'yellow', 'black']\r
++};\r
++\r
++Color.prototype.maxes = {\r
++ rgb: [255, 255, 255],\r
++ hsl: [360, 100, 100],\r
++ hsv: [360, 100, 100],\r
++ hwb: [360, 100, 100],\r
++ cmyk: [100, 100, 100, 100]\r
++};\r
++\r
++Color.prototype.getValues = function (space) {\r
++ var values = this.values;\r
++ var vals = {};\r
++\r
++ for (var i = 0; i < space.length; i++) {\r
++ vals[space.charAt(i)] = values[space][i];\r
++ }\r
++\r
++ if (values.alpha !== 1) {\r
++ vals.a = values.alpha;\r
++ }\r
++\r
++ // {r: 255, g: 255, b: 255, a: 0.4}\r
++ return vals;\r
++};\r
++\r
++Color.prototype.setValues = function (space, vals) {\r
++ var values = this.values;\r
++ var spaces = this.spaces;\r
++ var maxes = this.maxes;\r
++ var alpha = 1;\r
++ var i;\r
++\r
++ if (space === 'alpha') {\r
++ alpha = vals;\r
++ } else if (vals.length) {\r
++ // [10, 10, 10]\r
++ values[space] = vals.slice(0, space.length);\r
++ alpha = vals[space.length];\r
++ } else if (vals[space.charAt(0)] !== undefined) {\r
++ // {r: 10, g: 10, b: 10}\r
++ for (i = 0; i < space.length; i++) {\r
++ values[space][i] = vals[space.charAt(i)];\r
++ }\r
++\r
++ alpha = vals.a;\r
++ } else if (vals[spaces[space][0]] !== undefined) {\r
++ // {red: 10, green: 10, blue: 10}\r
++ var chans = spaces[space];\r
++\r
++ for (i = 0; i < space.length; i++) {\r
++ values[space][i] = vals[chans[i]];\r
++ }\r
++\r
++ alpha = vals.alpha;\r
++ }\r
++\r
++ values.alpha = Math.max(0, Math.min(1, (alpha === undefined ? values.alpha : alpha)));\r
++\r
++ if (space === 'alpha') {\r
++ return false;\r
++ }\r
++\r
++ var capped;\r
++\r
++ // cap values of the space prior converting all values\r
++ for (i = 0; i < space.length; i++) {\r
++ capped = Math.max(0, Math.min(maxes[space][i], values[space][i]));\r
++ values[space][i] = Math.round(capped);\r
++ }\r
++\r
++ // convert to all the other color spaces\r
++ for (var sname in spaces) {\r
++ if (sname !== space) {\r
++ values[sname] = convert[space][sname](values[space]);\r
++ }\r
++ }\r
++\r
++ return true;\r
++};\r
++\r
++Color.prototype.setSpace = function (space, args) {\r
++ var vals = args[0];\r
++\r
++ if (vals === undefined) {\r
++ // color.rgb()\r
++ return this.getValues(space);\r
++ }\r
++\r
++ // color.rgb(10, 10, 10)\r
++ if (typeof vals === 'number') {\r
++ vals = Array.prototype.slice.call(args);\r
++ }\r
++\r
++ this.setValues(space, vals);\r
++ return this;\r
++};\r
++\r
++Color.prototype.setChannel = function (space, index, val) {\r
++ var svalues = this.values[space];\r
++ if (val === undefined) {\r
++ // color.red()\r
++ return svalues[index];\r
++ } else if (val === svalues[index]) {\r
++ // color.red(color.red())\r
++ return this;\r
++ }\r
++\r
++ // color.red(100)\r
++ svalues[index] = val;\r
++ this.setValues(space, svalues);\r
++\r
++ return this;\r
++};\r
++\r
++if (typeof window !== 'undefined') {\r
++ window.Color = Color;\r
++}\r
++\r
++module.exports = Color;\r
++
++},{"2":2,"5":5}],4:[function(require,module,exports){
++/* MIT license */
++
++module.exports = {
++ rgb2hsl: rgb2hsl,
++ rgb2hsv: rgb2hsv,
++ rgb2hwb: rgb2hwb,
++ rgb2cmyk: rgb2cmyk,
++ rgb2keyword: rgb2keyword,
++ rgb2xyz: rgb2xyz,
++ rgb2lab: rgb2lab,
++ rgb2lch: rgb2lch,
++
++ hsl2rgb: hsl2rgb,
++ hsl2hsv: hsl2hsv,
++ hsl2hwb: hsl2hwb,
++ hsl2cmyk: hsl2cmyk,
++ hsl2keyword: hsl2keyword,
++
++ hsv2rgb: hsv2rgb,
++ hsv2hsl: hsv2hsl,
++ hsv2hwb: hsv2hwb,
++ hsv2cmyk: hsv2cmyk,
++ hsv2keyword: hsv2keyword,
++
++ hwb2rgb: hwb2rgb,
++ hwb2hsl: hwb2hsl,
++ hwb2hsv: hwb2hsv,
++ hwb2cmyk: hwb2cmyk,
++ hwb2keyword: hwb2keyword,
++
++ cmyk2rgb: cmyk2rgb,
++ cmyk2hsl: cmyk2hsl,
++ cmyk2hsv: cmyk2hsv,
++ cmyk2hwb: cmyk2hwb,
++ cmyk2keyword: cmyk2keyword,
++
++ keyword2rgb: keyword2rgb,
++ keyword2hsl: keyword2hsl,
++ keyword2hsv: keyword2hsv,
++ keyword2hwb: keyword2hwb,
++ keyword2cmyk: keyword2cmyk,
++ keyword2lab: keyword2lab,
++ keyword2xyz: keyword2xyz,
++
++ xyz2rgb: xyz2rgb,
++ xyz2lab: xyz2lab,
++ xyz2lch: xyz2lch,
++
++ lab2xyz: lab2xyz,
++ lab2rgb: lab2rgb,
++ lab2lch: lab2lch,
++
++ lch2lab: lch2lab,
++ lch2xyz: lch2xyz,
++ lch2rgb: lch2rgb
++}
++
++
++function rgb2hsl(rgb) {
++ var r = rgb[0]/255,
++ g = rgb[1]/255,
++ b = rgb[2]/255,
++ min = Math.min(r, g, b),
++ max = Math.max(r, g, b),
++ delta = max - min,
++ h, s, l;
++
++ if (max == min)
++ h = 0;
++ else if (r == max)
++ h = (g - b) / delta;
++ else if (g == max)
++ h = 2 + (b - r) / delta;
++ else if (b == max)
++ h = 4 + (r - g)/ delta;
++
++ h = Math.min(h * 60, 360);
++
++ if (h < 0)
++ h += 360;
++
++ l = (min + max) / 2;
++
++ if (max == min)
++ s = 0;
++ else if (l <= 0.5)
++ s = delta / (max + min);
++ else
++ s = delta / (2 - max - min);
++
++ return [h, s * 100, l * 100];
++}
++
++function rgb2hsv(rgb) {
++ var r = rgb[0],
++ g = rgb[1],
++ b = rgb[2],
++ min = Math.min(r, g, b),
++ max = Math.max(r, g, b),
++ delta = max - min,
++ h, s, v;
++
++ if (max == 0)
++ s = 0;
++ else
++ s = (delta/max * 1000)/10;
++
++ if (max == min)
++ h = 0;
++ else if (r == max)
++ h = (g - b) / delta;
++ else if (g == max)
++ h = 2 + (b - r) / delta;
++ else if (b == max)
++ h = 4 + (r - g) / delta;
++
++ h = Math.min(h * 60, 360);
++
++ if (h < 0)
++ h += 360;
++
++ v = ((max / 255) * 1000) / 10;
++
++ return [h, s, v];
++}
++
++function rgb2hwb(rgb) {
++ var r = rgb[0],
++ g = rgb[1],
++ b = rgb[2],
++ h = rgb2hsl(rgb)[0],
++ w = 1/255 * Math.min(r, Math.min(g, b)),
++ b = 1 - 1/255 * Math.max(r, Math.max(g, b));
++
++ return [h, w * 100, b * 100];
++}
++
++function rgb2cmyk(rgb) {
++ var r = rgb[0] / 255,
++ g = rgb[1] / 255,
++ b = rgb[2] / 255,
++ c, m, y, k;
++
++ k = Math.min(1 - r, 1 - g, 1 - b);
++ c = (1 - r - k) / (1 - k) || 0;
++ m = (1 - g - k) / (1 - k) || 0;
++ y = (1 - b - k) / (1 - k) || 0;
++ return [c * 100, m * 100, y * 100, k * 100];
++}
++
++function rgb2keyword(rgb) {
++ return reverseKeywords[JSON.stringify(rgb)];
++}
++
++function rgb2xyz(rgb) {
++ var r = rgb[0] / 255,
++ g = rgb[1] / 255,
++ b = rgb[2] / 255;
++
++ // assume sRGB
++ r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92);
++ g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92);
++ b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92);
++
++ var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805);
++ var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722);
++ var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505);
++
++ return [x * 100, y *100, z * 100];
++}
++
++function rgb2lab(rgb) {
++ var xyz = rgb2xyz(rgb),
++ x = xyz[0],
++ y = xyz[1],
++ z = xyz[2],
++ l, a, b;
++
++ x /= 95.047;
++ y /= 100;
++ z /= 108.883;
++
++ x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116);
++ y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116);
++ z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116);
++
++ l = (116 * y) - 16;
++ a = 500 * (x - y);
++ b = 200 * (y - z);
++
++ return [l, a, b];
++}
++
++function rgb2lch(args) {
++ return lab2lch(rgb2lab(args));
++}
++
++function hsl2rgb(hsl) {
++ var h = hsl[0] / 360,
++ s = hsl[1] / 100,
++ l = hsl[2] / 100,
++ t1, t2, t3, rgb, val;
++
++ if (s == 0) {
++ val = l * 255;
++ return [val, val, val];
++ }
++
++ if (l < 0.5)
++ t2 = l * (1 + s);
++ else
++ t2 = l + s - l * s;
++ t1 = 2 * l - t2;
++
++ rgb = [0, 0, 0];
++ for (var i = 0; i < 3; i++) {
++ t3 = h + 1 / 3 * - (i - 1);
++ t3 < 0 && t3++;
++ t3 > 1 && t3--;
++
++ if (6 * t3 < 1)
++ val = t1 + (t2 - t1) * 6 * t3;
++ else if (2 * t3 < 1)
++ val = t2;
++ else if (3 * t3 < 2)
++ val = t1 + (t2 - t1) * (2 / 3 - t3) * 6;
++ else
++ val = t1;
++
++ rgb[i] = val * 255;
++ }
++
++ return rgb;
++}
++
++function hsl2hsv(hsl) {
++ var h = hsl[0],
++ s = hsl[1] / 100,
++ l = hsl[2] / 100,
++ sv, v;
++
++ if(l === 0) {
++ // no need to do calc on black
++ // also avoids divide by 0 error
++ return [0, 0, 0];
++ }
++
++ l *= 2;
++ s *= (l <= 1) ? l : 2 - l;
++ v = (l + s) / 2;
++ sv = (2 * s) / (l + s);
++ return [h, sv * 100, v * 100];
++}
++
++function hsl2hwb(args) {
++ return rgb2hwb(hsl2rgb(args));
++}
++
++function hsl2cmyk(args) {
++ return rgb2cmyk(hsl2rgb(args));
++}
++
++function hsl2keyword(args) {
++ return rgb2keyword(hsl2rgb(args));
++}
++
++
++function hsv2rgb(hsv) {
++ var h = hsv[0] / 60,
++ s = hsv[1] / 100,
++ v = hsv[2] / 100,
++ hi = Math.floor(h) % 6;
++
++ var f = h - Math.floor(h),
++ p = 255 * v * (1 - s),
++ q = 255 * v * (1 - (s * f)),
++ t = 255 * v * (1 - (s * (1 - f))),
++ v = 255 * v;
++
++ switch(hi) {
++ case 0:
++ return [v, t, p];
++ case 1:
++ return [q, v, p];
++ case 2:
++ return [p, v, t];
++ case 3:
++ return [p, q, v];
++ case 4:
++ return [t, p, v];
++ case 5:
++ return [v, p, q];
++ }
++}
++
++function hsv2hsl(hsv) {
++ var h = hsv[0],
++ s = hsv[1] / 100,
++ v = hsv[2] / 100,
++ sl, l;
++
++ l = (2 - s) * v;
++ sl = s * v;
++ sl /= (l <= 1) ? l : 2 - l;
++ sl = sl || 0;
++ l /= 2;
++ return [h, sl * 100, l * 100];
++}
++
++function hsv2hwb(args) {
++ return rgb2hwb(hsv2rgb(args))
++}
++
++function hsv2cmyk(args) {
++ return rgb2cmyk(hsv2rgb(args));
++}
++
++function hsv2keyword(args) {
++ return rgb2keyword(hsv2rgb(args));
++}
++
++// http://dev.w3.org/csswg/css-color/#hwb-to-rgb
++function hwb2rgb(hwb) {
++ var h = hwb[0] / 360,
++ wh = hwb[1] / 100,
++ bl = hwb[2] / 100,
++ ratio = wh + bl,
++ i, v, f, n;
++
++ // wh + bl cant be > 1
++ if (ratio > 1) {
++ wh /= ratio;
++ bl /= ratio;
++ }
++
++ i = Math.floor(6 * h);
++ v = 1 - bl;
++ f = 6 * h - i;
++ if ((i & 0x01) != 0) {
++ f = 1 - f;
++ }
++ n = wh + f * (v - wh); // linear interpolation
++
++ switch (i) {
++ default:
++ case 6:
++ case 0: r = v; g = n; b = wh; break;
++ case 1: r = n; g = v; b = wh; break;
++ case 2: r = wh; g = v; b = n; break;
++ case 3: r = wh; g = n; b = v; break;
++ case 4: r = n; g = wh; b = v; break;
++ case 5: r = v; g = wh; b = n; break;
++ }
++
++ return [r * 255, g * 255, b * 255];
++}
++
++function hwb2hsl(args) {
++ return rgb2hsl(hwb2rgb(args));
++}
++
++function hwb2hsv(args) {
++ return rgb2hsv(hwb2rgb(args));
++}
++
++function hwb2cmyk(args) {
++ return rgb2cmyk(hwb2rgb(args));
++}
++
++function hwb2keyword(args) {
++ return rgb2keyword(hwb2rgb(args));
++}
++
++function cmyk2rgb(cmyk) {
++ var c = cmyk[0] / 100,
++ m = cmyk[1] / 100,
++ y = cmyk[2] / 100,
++ k = cmyk[3] / 100,
++ r, g, b;
++
++ r = 1 - Math.min(1, c * (1 - k) + k);
++ g = 1 - Math.min(1, m * (1 - k) + k);
++ b = 1 - Math.min(1, y * (1 - k) + k);
++ return [r * 255, g * 255, b * 255];
++}
++
++function cmyk2hsl(args) {
++ return rgb2hsl(cmyk2rgb(args));
++}
++
++function cmyk2hsv(args) {
++ return rgb2hsv(cmyk2rgb(args));
++}
++
++function cmyk2hwb(args) {
++ return rgb2hwb(cmyk2rgb(args));
++}
++
++function cmyk2keyword(args) {
++ return rgb2keyword(cmyk2rgb(args));
++}
++
++
++function xyz2rgb(xyz) {
++ var x = xyz[0] / 100,
++ y = xyz[1] / 100,
++ z = xyz[2] / 100,
++ r, g, b;
++
++ r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986);
++ g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415);
++ b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570);
++
++ // assume sRGB
++ r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055)
++ : r = (r * 12.92);
++
++ g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055)
++ : g = (g * 12.92);
++
++ b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055)
++ : b = (b * 12.92);
++
++ r = Math.min(Math.max(0, r), 1);
++ g = Math.min(Math.max(0, g), 1);
++ b = Math.min(Math.max(0, b), 1);
++
++ return [r * 255, g * 255, b * 255];
++}
++
++function xyz2lab(xyz) {
++ var x = xyz[0],
++ y = xyz[1],
++ z = xyz[2],
++ l, a, b;
++
++ x /= 95.047;
++ y /= 100;
++ z /= 108.883;
++
++ x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116);
++ y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116);
++ z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116);
++
++ l = (116 * y) - 16;
++ a = 500 * (x - y);
++ b = 200 * (y - z);
++
++ return [l, a, b];
++}
++
++function xyz2lch(args) {
++ return lab2lch(xyz2lab(args));
++}
++
++function lab2xyz(lab) {
++ var l = lab[0],
++ a = lab[1],
++ b = lab[2],
++ x, y, z, y2;
++
++ if (l <= 8) {
++ y = (l * 100) / 903.3;
++ y2 = (7.787 * (y / 100)) + (16 / 116);
++ } else {
++ y = 100 * Math.pow((l + 16) / 116, 3);
++ y2 = Math.pow(y / 100, 1/3);
++ }
++
++ x = x / 95.047 <= 0.008856 ? x = (95.047 * ((a / 500) + y2 - (16 / 116))) / 7.787 : 95.047 * Math.pow((a / 500) + y2, 3);
++
++ z = z / 108.883 <= 0.008859 ? z = (108.883 * (y2 - (b / 200) - (16 / 116))) / 7.787 : 108.883 * Math.pow(y2 - (b / 200), 3);
++
++ return [x, y, z];
++}
++
++function lab2lch(lab) {
++ var l = lab[0],
++ a = lab[1],
++ b = lab[2],
++ hr, h, c;
++
++ hr = Math.atan2(b, a);
++ h = hr * 360 / 2 / Math.PI;
++ if (h < 0) {
++ h += 360;
++ }
++ c = Math.sqrt(a * a + b * b);
++ return [l, c, h];
++}
++
++function lab2rgb(args) {
++ return xyz2rgb(lab2xyz(args));
++}
++
++function lch2lab(lch) {
++ var l = lch[0],
++ c = lch[1],
++ h = lch[2],
++ a, b, hr;
++
++ hr = h / 360 * 2 * Math.PI;
++ a = c * Math.cos(hr);
++ b = c * Math.sin(hr);
++ return [l, a, b];
++}
++
++function lch2xyz(args) {
++ return lab2xyz(lch2lab(args));
++}
++
++function lch2rgb(args) {
++ return lab2rgb(lch2lab(args));
++}
++
++function keyword2rgb(keyword) {
++ return cssKeywords[keyword];
++}
++
++function keyword2hsl(args) {
++ return rgb2hsl(keyword2rgb(args));
++}
++
++function keyword2hsv(args) {
++ return rgb2hsv(keyword2rgb(args));
++}
++
++function keyword2hwb(args) {
++ return rgb2hwb(keyword2rgb(args));
++}
++
++function keyword2cmyk(args) {
++ return rgb2cmyk(keyword2rgb(args));
++}
++
++function keyword2lab(args) {
++ return rgb2lab(keyword2rgb(args));
++}
++
++function keyword2xyz(args) {
++ return rgb2xyz(keyword2rgb(args));
++}
++
++var cssKeywords = {
++ aliceblue: [240,248,255],
++ antiquewhite: [250,235,215],
++ aqua: [0,255,255],
++ aquamarine: [127,255,212],
++ azure: [240,255,255],
++ beige: [245,245,220],
++ bisque: [255,228,196],
++ black: [0,0,0],
++ blanchedalmond: [255,235,205],
++ blue: [0,0,255],
++ blueviolet: [138,43,226],
++ brown: [165,42,42],
++ burlywood: [222,184,135],
++ cadetblue: [95,158,160],
++ chartreuse: [127,255,0],
++ chocolate: [210,105,30],
++ coral: [255,127,80],
++ cornflowerblue: [100,149,237],
++ cornsilk: [255,248,220],
++ crimson: [220,20,60],
++ cyan: [0,255,255],
++ darkblue: [0,0,139],
++ darkcyan: [0,139,139],
++ darkgoldenrod: [184,134,11],
++ darkgray: [169,169,169],
++ darkgreen: [0,100,0],
++ darkgrey: [169,169,169],
++ darkkhaki: [189,183,107],
++ darkmagenta: [139,0,139],
++ darkolivegreen: [85,107,47],
++ darkorange: [255,140,0],
++ darkorchid: [153,50,204],
++ darkred: [139,0,0],
++ darksalmon: [233,150,122],
++ darkseagreen: [143,188,143],
++ darkslateblue: [72,61,139],
++ darkslategray: [47,79,79],
++ darkslategrey: [47,79,79],
++ darkturquoise: [0,206,209],
++ darkviolet: [148,0,211],
++ deeppink: [255,20,147],
++ deepskyblue: [0,191,255],
++ dimgray: [105,105,105],
++ dimgrey: [105,105,105],
++ dodgerblue: [30,144,255],
++ firebrick: [178,34,34],
++ floralwhite: [255,250,240],
++ forestgreen: [34,139,34],
++ fuchsia: [255,0,255],
++ gainsboro: [220,220,220],
++ ghostwhite: [248,248,255],
++ gold: [255,215,0],
++ goldenrod: [218,165,32],
++ gray: [128,128,128],
++ green: [0,128,0],
++ greenyellow: [173,255,47],
++ grey: [128,128,128],
++ honeydew: [240,255,240],
++ hotpink: [255,105,180],
++ indianred: [205,92,92],
++ indigo: [75,0,130],
++ ivory: [255,255,240],
++ khaki: [240,230,140],
++ lavender: [230,230,250],
++ lavenderblush: [255,240,245],
++ lawngreen: [124,252,0],
++ lemonchiffon: [255,250,205],
++ lightblue: [173,216,230],
++ lightcoral: [240,128,128],
++ lightcyan: [224,255,255],
++ lightgoldenrodyellow: [250,250,210],
++ lightgray: [211,211,211],
++ lightgreen: [144,238,144],
++ lightgrey: [211,211,211],
++ lightpink: [255,182,193],
++ lightsalmon: [255,160,122],
++ lightseagreen: [32,178,170],
++ lightskyblue: [135,206,250],
++ lightslategray: [119,136,153],
++ lightslategrey: [119,136,153],
++ lightsteelblue: [176,196,222],
++ lightyellow: [255,255,224],
++ lime: [0,255,0],
++ limegreen: [50,205,50],
++ linen: [250,240,230],
++ magenta: [255,0,255],
++ maroon: [128,0,0],
++ mediumaquamarine: [102,205,170],
++ mediumblue: [0,0,205],
++ mediumorchid: [186,85,211],
++ mediumpurple: [147,112,219],
++ mediumseagreen: [60,179,113],
++ mediumslateblue: [123,104,238],
++ mediumspringgreen: [0,250,154],
++ mediumturquoise: [72,209,204],
++ mediumvioletred: [199,21,133],
++ midnightblue: [25,25,112],
++ mintcream: [245,255,250],
++ mistyrose: [255,228,225],
++ moccasin: [255,228,181],
++ navajowhite: [255,222,173],
++ navy: [0,0,128],
++ oldlace: [253,245,230],
++ olive: [128,128,0],
++ olivedrab: [107,142,35],
++ orange: [255,165,0],
++ orangered: [255,69,0],
++ orchid: [218,112,214],
++ palegoldenrod: [238,232,170],
++ palegreen: [152,251,152],
++ paleturquoise: [175,238,238],
++ palevioletred: [219,112,147],
++ papayawhip: [255,239,213],
++ peachpuff: [255,218,185],
++ peru: [205,133,63],
++ pink: [255,192,203],
++ plum: [221,160,221],
++ powderblue: [176,224,230],
++ purple: [128,0,128],
++ rebeccapurple: [102, 51, 153],
++ red: [255,0,0],
++ rosybrown: [188,143,143],
++ royalblue: [65,105,225],
++ saddlebrown: [139,69,19],
++ salmon: [250,128,114],
++ sandybrown: [244,164,96],
++ seagreen: [46,139,87],
++ seashell: [255,245,238],
++ sienna: [160,82,45],
++ silver: [192,192,192],
++ skyblue: [135,206,235],
++ slateblue: [106,90,205],
++ slategray: [112,128,144],
++ slategrey: [112,128,144],
++ snow: [255,250,250],
++ springgreen: [0,255,127],
++ steelblue: [70,130,180],
++ tan: [210,180,140],
++ teal: [0,128,128],
++ thistle: [216,191,216],
++ tomato: [255,99,71],
++ turquoise: [64,224,208],
++ violet: [238,130,238],
++ wheat: [245,222,179],
++ white: [255,255,255],
++ whitesmoke: [245,245,245],
++ yellow: [255,255,0],
++ yellowgreen: [154,205,50]
++};
++
++var reverseKeywords = {};
++for (var key in cssKeywords) {
++ reverseKeywords[JSON.stringify(cssKeywords[key])] = key;
++}
++
++},{}],5:[function(require,module,exports){
++var conversions = require(4);
++
++var convert = function() {
++ return new Converter();
++}
++
++for (var func in conversions) {
++ // export Raw versions
++ convert[func + "Raw"] = (function(func) {
++ // accept array or plain args
++ return function(arg) {
++ if (typeof arg == "number")
++ arg = Array.prototype.slice.call(arguments);
++ return conversions[func](arg);
++ }
++ })(func);
++
++ var pair = /(\w+)2(\w+)/.exec(func),
++ from = pair[1],
++ to = pair[2];
++
++ // export rgb2hsl and ["rgb"]["hsl"]
++ convert[from] = convert[from] || {};
++
++ convert[from][to] = convert[func] = (function(func) {
++ return function(arg) {
++ if (typeof arg == "number")
++ arg = Array.prototype.slice.call(arguments);
++
++ var val = conversions[func](arg);
++ if (typeof val == "string" || val === undefined)
++ return val; // keyword
++
++ for (var i = 0; i < val.length; i++)
++ val[i] = Math.round(val[i]);
++ return val;
++ }
++ })(func);
++}
++
++
++/* Converter does lazy conversion and caching */
++var Converter = function() {
++ this.convs = {};
++};
++
++/* Either get the values for a space or
++ set the values for a space, depending on args */
++Converter.prototype.routeSpace = function(space, args) {
++ var values = args[0];
++ if (values === undefined) {
++ // color.rgb()
++ return this.getValues(space);
++ }
++ // color.rgb(10, 10, 10)
++ if (typeof values == "number") {
++ values = Array.prototype.slice.call(args);
++ }
++
++ return this.setValues(space, values);
++};
++
++/* Set the values for a space, invalidating cache */
++Converter.prototype.setValues = function(space, values) {
++ this.space = space;
++ this.convs = {};
++ this.convs[space] = values;
++ return this;
++};
++
++/* Get the values for a space. If there's already
++ a conversion for the space, fetch it, otherwise
++ compute it */
++Converter.prototype.getValues = function(space) {
++ var vals = this.convs[space];
++ if (!vals) {
++ var fspace = this.space,
++ from = this.convs[fspace];
++ vals = convert[fspace][space](from);
++
++ this.convs[space] = vals;
++ }
++ return vals;
++};
++
++["rgb", "hsl", "hsv", "cmyk", "keyword"].forEach(function(space) {
++ Converter.prototype[space] = function(vals) {
++ return this.routeSpace(space, arguments);
++ }
++});
++
++module.exports = convert;
++},{"4":4}],6:[function(require,module,exports){
++module.exports = {\r
++ "aliceblue": [240, 248, 255],\r
++ "antiquewhite": [250, 235, 215],\r
++ "aqua": [0, 255, 255],\r
++ "aquamarine": [127, 255, 212],\r
++ "azure": [240, 255, 255],\r
++ "beige": [245, 245, 220],\r
++ "bisque": [255, 228, 196],\r
++ "black": [0, 0, 0],\r
++ "blanchedalmond": [255, 235, 205],\r
++ "blue": [0, 0, 255],\r
++ "blueviolet": [138, 43, 226],\r
++ "brown": [165, 42, 42],\r
++ "burlywood": [222, 184, 135],\r
++ "cadetblue": [95, 158, 160],\r
++ "chartreuse": [127, 255, 0],\r
++ "chocolate": [210, 105, 30],\r
++ "coral": [255, 127, 80],\r
++ "cornflowerblue": [100, 149, 237],\r
++ "cornsilk": [255, 248, 220],\r
++ "crimson": [220, 20, 60],\r
++ "cyan": [0, 255, 255],\r
++ "darkblue": [0, 0, 139],\r
++ "darkcyan": [0, 139, 139],\r
++ "darkgoldenrod": [184, 134, 11],\r
++ "darkgray": [169, 169, 169],\r
++ "darkgreen": [0, 100, 0],\r
++ "darkgrey": [169, 169, 169],\r
++ "darkkhaki": [189, 183, 107],\r
++ "darkmagenta": [139, 0, 139],\r
++ "darkolivegreen": [85, 107, 47],\r
++ "darkorange": [255, 140, 0],\r
++ "darkorchid": [153, 50, 204],\r
++ "darkred": [139, 0, 0],\r
++ "darksalmon": [233, 150, 122],\r
++ "darkseagreen": [143, 188, 143],\r
++ "darkslateblue": [72, 61, 139],\r
++ "darkslategray": [47, 79, 79],\r
++ "darkslategrey": [47, 79, 79],\r
++ "darkturquoise": [0, 206, 209],\r
++ "darkviolet": [148, 0, 211],\r
++ "deeppink": [255, 20, 147],\r
++ "deepskyblue": [0, 191, 255],\r
++ "dimgray": [105, 105, 105],\r
++ "dimgrey": [105, 105, 105],\r
++ "dodgerblue": [30, 144, 255],\r
++ "firebrick": [178, 34, 34],\r
++ "floralwhite": [255, 250, 240],\r
++ "forestgreen": [34, 139, 34],\r
++ "fuchsia": [255, 0, 255],\r
++ "gainsboro": [220, 220, 220],\r
++ "ghostwhite": [248, 248, 255],\r
++ "gold": [255, 215, 0],\r
++ "goldenrod": [218, 165, 32],\r
++ "gray": [128, 128, 128],\r
++ "green": [0, 128, 0],\r
++ "greenyellow": [173, 255, 47],\r
++ "grey": [128, 128, 128],\r
++ "honeydew": [240, 255, 240],\r
++ "hotpink": [255, 105, 180],\r
++ "indianred": [205, 92, 92],\r
++ "indigo": [75, 0, 130],\r
++ "ivory": [255, 255, 240],\r
++ "khaki": [240, 230, 140],\r
++ "lavender": [230, 230, 250],\r
++ "lavenderblush": [255, 240, 245],\r
++ "lawngreen": [124, 252, 0],\r
++ "lemonchiffon": [255, 250, 205],\r
++ "lightblue": [173, 216, 230],\r
++ "lightcoral": [240, 128, 128],\r
++ "lightcyan": [224, 255, 255],\r
++ "lightgoldenrodyellow": [250, 250, 210],\r
++ "lightgray": [211, 211, 211],\r
++ "lightgreen": [144, 238, 144],\r
++ "lightgrey": [211, 211, 211],\r
++ "lightpink": [255, 182, 193],\r
++ "lightsalmon": [255, 160, 122],\r
++ "lightseagreen": [32, 178, 170],\r
++ "lightskyblue": [135, 206, 250],\r
++ "lightslategray": [119, 136, 153],\r
++ "lightslategrey": [119, 136, 153],\r
++ "lightsteelblue": [176, 196, 222],\r
++ "lightyellow": [255, 255, 224],\r
++ "lime": [0, 255, 0],\r
++ "limegreen": [50, 205, 50],\r
++ "linen": [250, 240, 230],\r
++ "magenta": [255, 0, 255],\r
++ "maroon": [128, 0, 0],\r
++ "mediumaquamarine": [102, 205, 170],\r
++ "mediumblue": [0, 0, 205],\r
++ "mediumorchid": [186, 85, 211],\r
++ "mediumpurple": [147, 112, 219],\r
++ "mediumseagreen": [60, 179, 113],\r
++ "mediumslateblue": [123, 104, 238],\r
++ "mediumspringgreen": [0, 250, 154],\r
++ "mediumturquoise": [72, 209, 204],\r
++ "mediumvioletred": [199, 21, 133],\r
++ "midnightblue": [25, 25, 112],\r
++ "mintcream": [245, 255, 250],\r
++ "mistyrose": [255, 228, 225],\r
++ "moccasin": [255, 228, 181],\r
++ "navajowhite": [255, 222, 173],\r
++ "navy": [0, 0, 128],\r
++ "oldlace": [253, 245, 230],\r
++ "olive": [128, 128, 0],\r
++ "olivedrab": [107, 142, 35],\r
++ "orange": [255, 165, 0],\r
++ "orangered": [255, 69, 0],\r
++ "orchid": [218, 112, 214],\r
++ "palegoldenrod": [238, 232, 170],\r
++ "palegreen": [152, 251, 152],\r
++ "paleturquoise": [175, 238, 238],\r
++ "palevioletred": [219, 112, 147],\r
++ "papayawhip": [255, 239, 213],\r
++ "peachpuff": [255, 218, 185],\r
++ "peru": [205, 133, 63],\r
++ "pink": [255, 192, 203],\r
++ "plum": [221, 160, 221],\r
++ "powderblue": [176, 224, 230],\r
++ "purple": [128, 0, 128],\r
++ "rebeccapurple": [102, 51, 153],\r
++ "red": [255, 0, 0],\r
++ "rosybrown": [188, 143, 143],\r
++ "royalblue": [65, 105, 225],\r
++ "saddlebrown": [139, 69, 19],\r
++ "salmon": [250, 128, 114],\r
++ "sandybrown": [244, 164, 96],\r
++ "seagreen": [46, 139, 87],\r
++ "seashell": [255, 245, 238],\r
++ "sienna": [160, 82, 45],\r
++ "silver": [192, 192, 192],\r
++ "skyblue": [135, 206, 235],\r
++ "slateblue": [106, 90, 205],\r
++ "slategray": [112, 128, 144],\r
++ "slategrey": [112, 128, 144],\r
++ "snow": [255, 250, 250],\r
++ "springgreen": [0, 255, 127],\r
++ "steelblue": [70, 130, 180],\r
++ "tan": [210, 180, 140],\r
++ "teal": [0, 128, 128],\r
++ "thistle": [216, 191, 216],\r
++ "tomato": [255, 99, 71],\r
++ "turquoise": [64, 224, 208],\r
++ "violet": [238, 130, 238],\r
++ "wheat": [245, 222, 179],\r
++ "white": [255, 255, 255],\r
++ "whitesmoke": [245, 245, 245],\r
++ "yellow": [255, 255, 0],\r
++ "yellowgreen": [154, 205, 50]\r
++};
++},{}],7:[function(require,module,exports){
++/**
++ * @namespace Chart
++ */
++var Chart = require(28)();
++
++require(26)(Chart);
++require(22)(Chart);
++require(25)(Chart);
++require(21)(Chart);
++require(23)(Chart);
++require(24)(Chart);
++require(29)(Chart);
++require(33)(Chart);
++require(31)(Chart);
++require(34)(Chart);
++require(32)(Chart);
++require(35)(Chart);
++require(30)(Chart);
++require(27)(Chart);
++require(36)(Chart);
++
++require(37)(Chart);
++require(38)(Chart);
++require(39)(Chart);
++require(40)(Chart);
++
++require(43)(Chart);
++require(41)(Chart);
++require(42)(Chart);
++require(44)(Chart);
++require(45)(Chart);
++require(46)(Chart);
++
++// Controllers must be loaded after elements
++// See Chart.core.datasetController.dataElementType
++require(15)(Chart);
++require(16)(Chart);
++require(17)(Chart);
++require(18)(Chart);
++require(19)(Chart);
++require(20)(Chart);
++
++require(8)(Chart);
++require(9)(Chart);
++require(10)(Chart);
++require(11)(Chart);
++require(12)(Chart);
++require(13)(Chart);
++require(14)(Chart);
++
++window.Chart = module.exports = Chart;
++
++},{"10":10,"11":11,"12":12,"13":13,"14":14,"15":15,"16":16,"17":17,"18":18,"19":19,"20":20,"21":21,"22":22,"23":23,"24":24,"25":25,"26":26,"27":27,"28":28,"29":29,"30":30,"31":31,"32":32,"33":33,"34":34,"35":35,"36":36,"37":37,"38":38,"39":39,"40":40,"41":41,"42":42,"43":43,"44":44,"45":45,"46":46,"8":8,"9":9}],8:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++
++ Chart.Bar = function(context, config) {
++ config.type = 'bar';
++
++ return new Chart(context, config);
++ };
++
++};
++
++},{}],9:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++
++ Chart.Bubble = function(context, config) {
++ config.type = 'bubble';
++ return new Chart(context, config);
++ };
++
++};
++
++},{}],10:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++
++ Chart.Doughnut = function(context, config) {
++ config.type = 'doughnut';
++
++ return new Chart(context, config);
++ };
++
++};
++
++},{}],11:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++
++ Chart.Line = function(context, config) {
++ config.type = 'line';
++
++ return new Chart(context, config);
++ };
++
++};
++
++},{}],12:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++
++ Chart.PolarArea = function(context, config) {
++ config.type = 'polarArea';
++
++ return new Chart(context, config);
++ };
++
++};
++
++},{}],13:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++
++ Chart.Radar = function(context, config) {
++ config.type = 'radar';
++
++ return new Chart(context, config);
++ };
++
++};
++
++},{}],14:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++
++ var defaultConfig = {
++ hover: {
++ mode: 'single'
++ },
++
++ scales: {
++ xAxes: [{
++ type: 'linear', // scatter should not use a category axis
++ position: 'bottom',
++ id: 'x-axis-1' // need an ID so datasets can reference the scale
++ }],
++ yAxes: [{
++ type: 'linear',
++ position: 'left',
++ id: 'y-axis-1'
++ }]
++ },
++
++ tooltips: {
++ callbacks: {
++ title: function() {
++ // Title doesn't make sense for scatter since we format the data as a point
++ return '';
++ },
++ label: function(tooltipItem) {
++ return '(' + tooltipItem.xLabel + ', ' + tooltipItem.yLabel + ')';
++ }
++ }
++ }
++ };
++
++ // Register the default config for this type
++ Chart.defaults.scatter = defaultConfig;
++
++ // Scatter charts use line controllers
++ Chart.controllers.scatter = Chart.controllers.line;
++
++ Chart.Scatter = function(context, config) {
++ config.type = 'scatter';
++ return new Chart(context, config);
++ };
++
++};
++
++},{}],15:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++
++ var helpers = Chart.helpers;
++
++ Chart.defaults.bar = {
++ hover: {
++ mode: 'label'
++ },
++
++ scales: {
++ xAxes: [{
++ type: 'category',
++
++ // Specific to Bar Controller
++ categoryPercentage: 0.8,
++ barPercentage: 0.9,
++
++ // grid line settings
++ gridLines: {
++ offsetGridLines: true
++ }
++ }],
++ yAxes: [{
++ type: 'linear'
++ }]
++ }
++ };
++
++ Chart.controllers.bar = Chart.DatasetController.extend({
++
++ dataElementType: Chart.elements.Rectangle,
++
++ initialize: function(chart, datasetIndex) {
++ Chart.DatasetController.prototype.initialize.call(this, chart, datasetIndex);
++
++ // Use this to indicate that this is a bar dataset.
++ this.getMeta().bar = true;
++ },
++
++ // Get the number of datasets that display bars. We use this to correctly calculate the bar width
++ getBarCount: function() {
++ var me = this;
++ var barCount = 0;
++ helpers.each(me.chart.data.datasets, function(dataset, datasetIndex) {
++ var meta = me.chart.getDatasetMeta(datasetIndex);
++ if (meta.bar && me.chart.isDatasetVisible(datasetIndex)) {
++ ++barCount;
++ }
++ }, me);
++ return barCount;
++ },
++
++ update: function(reset) {
++ var me = this;
++ helpers.each(me.getMeta().data, function(rectangle, index) {
++ me.updateElement(rectangle, index, reset);
++ }, me);
++ },
++
++ updateElement: function(rectangle, index, reset) {
++ var me = this;
++ var meta = me.getMeta();
++ var xScale = me.getScaleForId(meta.xAxisID);
++ var yScale = me.getScaleForId(meta.yAxisID);
++ var scaleBase = yScale.getBasePixel();
++ var rectangleElementOptions = me.chart.options.elements.rectangle;
++ var custom = rectangle.custom || {};
++ var dataset = me.getDataset();
++
++ rectangle._xScale = xScale;
++ rectangle._yScale = yScale;
++ rectangle._datasetIndex = me.index;
++ rectangle._index = index;
++
++ var ruler = me.getRuler(index);
++ rectangle._model = {
++ x: me.calculateBarX(index, me.index, ruler),
++ y: reset ? scaleBase : me.calculateBarY(index, me.index),
++
++ // Tooltip
++ label: me.chart.data.labels[index],
++ datasetLabel: dataset.label,
++
++ // Appearance
++ base: reset ? scaleBase : me.calculateBarBase(me.index, index),
++ width: me.calculateBarWidth(ruler),
++ backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor),
++ borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleElementOptions.borderSkipped,
++ borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor),
++ borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth)
++ };
++
++ rectangle.pivot();
++ },
++
++ calculateBarBase: function(datasetIndex, index) {
++ var me = this;
++ var meta = me.getMeta();
++ var yScale = me.getScaleForId(meta.yAxisID);
++ var base = 0;
++
++ if (yScale.options.stacked) {
++ var chart = me.chart;
++ var datasets = chart.data.datasets;
++ var value = Number(datasets[datasetIndex].data[index]);
++
++ for (var i = 0; i < datasetIndex; i++) {
++ var currentDs = datasets[i];
++ var currentDsMeta = chart.getDatasetMeta(i);
++ if (currentDsMeta.bar && currentDsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) {
++ var currentVal = Number(currentDs.data[index]);
++ base += value < 0 ? Math.min(currentVal, 0) : Math.max(currentVal, 0);
++ }
++ }
++
++ return yScale.getPixelForValue(base);
++ }
++
++ return yScale.getBasePixel();
++ },
++
++ getRuler: function(index) {
++ var me = this;
++ var meta = me.getMeta();
++ var xScale = me.getScaleForId(meta.xAxisID);
++ var datasetCount = me.getBarCount();
++
++ var tickWidth;
++
++ if (xScale.options.type === 'category') {
++ tickWidth = xScale.getPixelForTick(index + 1) - xScale.getPixelForTick(index);
++ } else {
++ // Average width
++ tickWidth = xScale.width / xScale.ticks.length;
++ }
++ var categoryWidth = tickWidth * xScale.options.categoryPercentage;
++ var categorySpacing = (tickWidth - (tickWidth * xScale.options.categoryPercentage)) / 2;
++ var fullBarWidth = categoryWidth / datasetCount;
++
++ if (xScale.ticks.length !== me.chart.data.labels.length) {
++ var perc = xScale.ticks.length / me.chart.data.labels.length;
++ fullBarWidth = fullBarWidth * perc;
++ }
++
++ var barWidth = fullBarWidth * xScale.options.barPercentage;
++ var barSpacing = fullBarWidth - (fullBarWidth * xScale.options.barPercentage);
++
++ return {
++ datasetCount: datasetCount,
++ tickWidth: tickWidth,
++ categoryWidth: categoryWidth,
++ categorySpacing: categorySpacing,
++ fullBarWidth: fullBarWidth,
++ barWidth: barWidth,
++ barSpacing: barSpacing
++ };
++ },
++
++ calculateBarWidth: function(ruler) {
++ var xScale = this.getScaleForId(this.getMeta().xAxisID);
++ if (xScale.options.barThickness) {
++ return xScale.options.barThickness;
++ }
++ return xScale.options.stacked ? ruler.categoryWidth : ruler.barWidth;
++ },
++
++ // Get bar index from the given dataset index accounting for the fact that not all bars are visible
++ getBarIndex: function(datasetIndex) {
++ var barIndex = 0;
++ var meta, j;
++
++ for (j = 0; j < datasetIndex; ++j) {
++ meta = this.chart.getDatasetMeta(j);
++ if (meta.bar && this.chart.isDatasetVisible(j)) {
++ ++barIndex;
++ }
++ }
++
++ return barIndex;
++ },
++
++ calculateBarX: function(index, datasetIndex, ruler) {
++ var me = this;
++ var meta = me.getMeta();
++ var xScale = me.getScaleForId(meta.xAxisID);
++ var barIndex = me.getBarIndex(datasetIndex);
++ var leftTick = xScale.getPixelForValue(null, index, datasetIndex, me.chart.isCombo);
++ leftTick -= me.chart.isCombo ? (ruler.tickWidth / 2) : 0;
++
++ if (xScale.options.stacked) {
++ return leftTick + (ruler.categoryWidth / 2) + ruler.categorySpacing;
++ }
++
++ return leftTick +
++ (ruler.barWidth / 2) +
++ ruler.categorySpacing +
++ (ruler.barWidth * barIndex) +
++ (ruler.barSpacing / 2) +
++ (ruler.barSpacing * barIndex);
++ },
++
++ calculateBarY: function(index, datasetIndex) {
++ var me = this;
++ var meta = me.getMeta();
++ var yScale = me.getScaleForId(meta.yAxisID);
++ var value = Number(me.getDataset().data[index]);
++
++ if (yScale.options.stacked) {
++
++ var sumPos = 0,
++ sumNeg = 0;
++
++ for (var i = 0; i < datasetIndex; i++) {
++ var ds = me.chart.data.datasets[i];
++ var dsMeta = me.chart.getDatasetMeta(i);
++ if (dsMeta.bar && dsMeta.yAxisID === yScale.id && me.chart.isDatasetVisible(i)) {
++ var stackedVal = Number(ds.data[index]);
++ if (stackedVal < 0) {
++ sumNeg += stackedVal || 0;
++ } else {
++ sumPos += stackedVal || 0;
++ }
++ }
++ }
++
++ if (value < 0) {
++ return yScale.getPixelForValue(sumNeg + value);
++ }
++ return yScale.getPixelForValue(sumPos + value);
++ }
++
++ return yScale.getPixelForValue(value);
++ },
++
++ draw: function(ease) {
++ var me = this;
++ var easingDecimal = ease || 1;
++ var metaData = me.getMeta().data;
++ var dataset = me.getDataset();
++ var i, len;
++
++ for (i = 0, len = metaData.length; i < len; ++i) {
++ var d = dataset.data[i];
++ if (d !== null && d !== undefined && !isNaN(d)) {
++ metaData[i].transition(easingDecimal).draw();
++ }
++ }
++ },
++
++ setHoverStyle: function(rectangle) {
++ var dataset = this.chart.data.datasets[rectangle._datasetIndex];
++ var index = rectangle._index;
++
++ var custom = rectangle.custom || {};
++ var model = rectangle._model;
++ model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
++ model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.getHoverColor(model.borderColor));
++ model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.hoverBorderWidth, index, model.borderWidth);
++ },
++
++ removeHoverStyle: function(rectangle) {
++ var dataset = this.chart.data.datasets[rectangle._datasetIndex];
++ var index = rectangle._index;
++ var custom = rectangle.custom || {};
++ var model = rectangle._model;
++ var rectangleElementOptions = this.chart.options.elements.rectangle;
++
++ model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor);
++ model.borderColor = custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor);
++ model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth);
++ }
++
++ });
++
++
++ // including horizontalBar in the bar file, instead of a file of its own
++ // it extends bar (like pie extends doughnut)
++ Chart.defaults.horizontalBar = {
++ hover: {
++ mode: 'label'
++ },
++
++ scales: {
++ xAxes: [{
++ type: 'linear',
++ position: 'bottom'
++ }],
++ yAxes: [{
++ position: 'left',
++ type: 'category',
++
++ // Specific to Horizontal Bar Controller
++ categoryPercentage: 0.8,
++ barPercentage: 0.9,
++
++ // grid line settings
++ gridLines: {
++ offsetGridLines: true
++ }
++ }]
++ },
++ elements: {
++ rectangle: {
++ borderSkipped: 'left'
++ }
++ },
++ tooltips: {
++ callbacks: {
++ title: function(tooltipItems, data) {
++ // Pick first xLabel for now
++ var title = '';
++
++ if (tooltipItems.length > 0) {
++ if (tooltipItems[0].yLabel) {
++ title = tooltipItems[0].yLabel;
++ } else if (data.labels.length > 0 && tooltipItems[0].index < data.labels.length) {
++ title = data.labels[tooltipItems[0].index];
++ }
++ }
++
++ return title;
++ },
++ label: function(tooltipItem, data) {
++ var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || '';
++ return datasetLabel + ': ' + tooltipItem.xLabel;
++ }
++ }
++ }
++ };
++
++ Chart.controllers.horizontalBar = Chart.controllers.bar.extend({
++ updateElement: function(rectangle, index, reset) {
++ var me = this;
++ var meta = me.getMeta();
++ var xScale = me.getScaleForId(meta.xAxisID);
++ var yScale = me.getScaleForId(meta.yAxisID);
++ var scaleBase = xScale.getBasePixel();
++ var custom = rectangle.custom || {};
++ var dataset = me.getDataset();
++ var rectangleElementOptions = me.chart.options.elements.rectangle;
++
++ rectangle._xScale = xScale;
++ rectangle._yScale = yScale;
++ rectangle._datasetIndex = me.index;
++ rectangle._index = index;
++
++ var ruler = me.getRuler(index);
++ rectangle._model = {
++ x: reset ? scaleBase : me.calculateBarX(index, me.index),
++ y: me.calculateBarY(index, me.index, ruler),
++
++ // Tooltip
++ label: me.chart.data.labels[index],
++ datasetLabel: dataset.label,
++
++ // Appearance
++ base: reset ? scaleBase : me.calculateBarBase(me.index, index),
++ height: me.calculateBarHeight(ruler),
++ backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor),
++ borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleElementOptions.borderSkipped,
++ borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor),
++ borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth)
++ };
++ rectangle.draw = function() {
++ var ctx = this._chart.ctx;
++ var vm = this._view;
++
++ var halfHeight = vm.height / 2,
++ topY = vm.y - halfHeight,
++ bottomY = vm.y + halfHeight,
++ right = vm.base - (vm.base - vm.x),
++ halfStroke = vm.borderWidth / 2;
++
++ // Canvas doesn't allow us to stroke inside the width so we can
++ // adjust the sizes to fit if we're setting a stroke on the line
++ if (vm.borderWidth) {
++ topY += halfStroke;
++ bottomY -= halfStroke;
++ right += halfStroke;
++ }
++
++ ctx.beginPath();
++
++ ctx.fillStyle = vm.backgroundColor;
++ ctx.strokeStyle = vm.borderColor;
++ ctx.lineWidth = vm.borderWidth;
++
++ // Corner points, from bottom-left to bottom-right clockwise
++ // | 1 2 |
++ // | 0 3 |
++ var corners = [
++ [vm.base, bottomY],
++ [vm.base, topY],
++ [right, topY],
++ [right, bottomY]
++ ];
++
++ // Find first (starting) corner with fallback to 'bottom'
++ var borders = ['bottom', 'left', 'top', 'right'];
++ var startCorner = borders.indexOf(vm.borderSkipped, 0);
++ if (startCorner === -1) {
++ startCorner = 0;
++ }
++
++ function cornerAt(cornerIndex) {
++ return corners[(startCorner + cornerIndex) % 4];
++ }
++
++ // Draw rectangle from 'startCorner'
++ ctx.moveTo.apply(ctx, cornerAt(0));
++ for (var i = 1; i < 4; i++) {
++ ctx.lineTo.apply(ctx, cornerAt(i));
++ }
++
++ ctx.fill();
++ if (vm.borderWidth) {
++ ctx.stroke();
++ }
++ };
++
++ rectangle.pivot();
++ },
++
++ calculateBarBase: function(datasetIndex, index) {
++ var me = this;
++ var meta = me.getMeta();
++ var xScale = me.getScaleForId(meta.xAxisID);
++ var base = 0;
++
++ if (xScale.options.stacked) {
++ var chart = me.chart;
++ var datasets = chart.data.datasets;
++ var value = Number(datasets[datasetIndex].data[index]);
++
++ for (var i = 0; i < datasetIndex; i++) {
++ var currentDs = datasets[i];
++ var currentDsMeta = chart.getDatasetMeta(i);
++ if (currentDsMeta.bar && currentDsMeta.xAxisID === xScale.id && chart.isDatasetVisible(i)) {
++ var currentVal = Number(currentDs.data[index]);
++ base += value < 0 ? Math.min(currentVal, 0) : Math.max(currentVal, 0);
++ }
++ }
++
++ return xScale.getPixelForValue(base);
++ }
++
++ return xScale.getBasePixel();
++ },
++
++ getRuler: function(index) {
++ var me = this;
++ var meta = me.getMeta();
++ var yScale = me.getScaleForId(meta.yAxisID);
++ var datasetCount = me.getBarCount();
++
++ var tickHeight;
++ if (yScale.options.type === 'category') {
++ tickHeight = yScale.getPixelForTick(index + 1) - yScale.getPixelForTick(index);
++ } else {
++ // Average width
++ tickHeight = yScale.width / yScale.ticks.length;
++ }
++ var categoryHeight = tickHeight * yScale.options.categoryPercentage;
++ var categorySpacing = (tickHeight - (tickHeight * yScale.options.categoryPercentage)) / 2;
++ var fullBarHeight = categoryHeight / datasetCount;
++
++ if (yScale.ticks.length !== me.chart.data.labels.length) {
++ var perc = yScale.ticks.length / me.chart.data.labels.length;
++ fullBarHeight = fullBarHeight * perc;
++ }
++
++ var barHeight = fullBarHeight * yScale.options.barPercentage;
++ var barSpacing = fullBarHeight - (fullBarHeight * yScale.options.barPercentage);
++
++ return {
++ datasetCount: datasetCount,
++ tickHeight: tickHeight,
++ categoryHeight: categoryHeight,
++ categorySpacing: categorySpacing,
++ fullBarHeight: fullBarHeight,
++ barHeight: barHeight,
++ barSpacing: barSpacing
++ };
++ },
++
++ calculateBarHeight: function(ruler) {
++ var me = this;
++ var yScale = me.getScaleForId(me.getMeta().yAxisID);
++ if (yScale.options.barThickness) {
++ return yScale.options.barThickness;
++ }
++ return yScale.options.stacked ? ruler.categoryHeight : ruler.barHeight;
++ },
++
++ calculateBarX: function(index, datasetIndex) {
++ var me = this;
++ var meta = me.getMeta();
++ var xScale = me.getScaleForId(meta.xAxisID);
++ var value = Number(me.getDataset().data[index]);
++
++ if (xScale.options.stacked) {
++
++ var sumPos = 0,
++ sumNeg = 0;
++
++ for (var i = 0; i < datasetIndex; i++) {
++ var ds = me.chart.data.datasets[i];
++ var dsMeta = me.chart.getDatasetMeta(i);
++ if (dsMeta.bar && dsMeta.xAxisID === xScale.id && me.chart.isDatasetVisible(i)) {
++ var stackedVal = Number(ds.data[index]);
++ if (stackedVal < 0) {
++ sumNeg += stackedVal || 0;
++ } else {
++ sumPos += stackedVal || 0;
++ }
++ }
++ }
++
++ if (value < 0) {
++ return xScale.getPixelForValue(sumNeg + value);
++ }
++ return xScale.getPixelForValue(sumPos + value);
++ }
++
++ return xScale.getPixelForValue(value);
++ },
++
++ calculateBarY: function(index, datasetIndex, ruler) {
++ var me = this;
++ var meta = me.getMeta();
++ var yScale = me.getScaleForId(meta.yAxisID);
++ var barIndex = me.getBarIndex(datasetIndex);
++ var topTick = yScale.getPixelForValue(null, index, datasetIndex, me.chart.isCombo);
++ topTick -= me.chart.isCombo ? (ruler.tickHeight / 2) : 0;
++
++ if (yScale.options.stacked) {
++ return topTick + (ruler.categoryHeight / 2) + ruler.categorySpacing;
++ }
++
++ return topTick +
++ (ruler.barHeight / 2) +
++ ruler.categorySpacing +
++ (ruler.barHeight * barIndex) +
++ (ruler.barSpacing / 2) +
++ (ruler.barSpacing * barIndex);
++ }
++ });
++};
++
++},{}],16:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++
++ var helpers = Chart.helpers;
++
++ Chart.defaults.bubble = {
++ hover: {
++ mode: 'single'
++ },
++
++ scales: {
++ xAxes: [{
++ type: 'linear', // bubble should probably use a linear scale by default
++ position: 'bottom',
++ id: 'x-axis-0' // need an ID so datasets can reference the scale
++ }],
++ yAxes: [{
++ type: 'linear',
++ position: 'left',
++ id: 'y-axis-0'
++ }]
++ },
++
++ tooltips: {
++ callbacks: {
++ title: function() {
++ // Title doesn't make sense for scatter since we format the data as a point
++ return '';
++ },
++ label: function(tooltipItem, data) {
++ var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || '';
++ var dataPoint = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
++ return datasetLabel + ': (' + tooltipItem.xLabel + ', ' + tooltipItem.yLabel + ', ' + dataPoint.r + ')';
++ }
++ }
++ }
++ };
++
++ Chart.controllers.bubble = Chart.DatasetController.extend({
++
++ dataElementType: Chart.elements.Point,
++
++ update: function(reset) {
++ var me = this;
++ var meta = me.getMeta();
++ var points = meta.data;
++
++ // Update Points
++ helpers.each(points, function(point, index) {
++ me.updateElement(point, index, reset);
++ });
++ },
++
++ updateElement: function(point, index, reset) {
++ var me = this;
++ var meta = me.getMeta();
++ var xScale = me.getScaleForId(meta.xAxisID);
++ var yScale = me.getScaleForId(meta.yAxisID);
++
++ var custom = point.custom || {};
++ var dataset = me.getDataset();
++ var data = dataset.data[index];
++ var pointElementOptions = me.chart.options.elements.point;
++ var dsIndex = me.index;
++
++ helpers.extend(point, {
++ // Utility
++ _xScale: xScale,
++ _yScale: yScale,
++ _datasetIndex: dsIndex,
++ _index: index,
++
++ // Desired view properties
++ _model: {
++ x: reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex, me.chart.isCombo),
++ y: reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex),
++ // Appearance
++ radius: reset ? 0 : custom.radius ? custom.radius : me.getRadius(data),
++
++ // Tooltip
++ hitRadius: custom.hitRadius ? custom.hitRadius : helpers.getValueAtIndexOrDefault(dataset.hitRadius, index, pointElementOptions.hitRadius)
++ }
++ });
++
++ // Trick to reset the styles of the point
++ Chart.DatasetController.prototype.removeHoverStyle.call(me, point, pointElementOptions);
++
++ var model = point._model;
++ model.skip = custom.skip ? custom.skip : (isNaN(model.x) || isNaN(model.y));
++
++ point.pivot();
++ },
++
++ getRadius: function(value) {
++ return value.r || this.chart.options.elements.point.radius;
++ },
++
++ setHoverStyle: function(point) {
++ var me = this;
++ Chart.DatasetController.prototype.setHoverStyle.call(me, point);
++
++ // Radius
++ var dataset = me.chart.data.datasets[point._datasetIndex];
++ var index = point._index;
++ var custom = point.custom || {};
++ var model = point._model;
++ model.radius = custom.hoverRadius ? custom.hoverRadius : (helpers.getValueAtIndexOrDefault(dataset.hoverRadius, index, me.chart.options.elements.point.hoverRadius)) + me.getRadius(dataset.data[index]);
++ },
++
++ removeHoverStyle: function(point) {
++ var me = this;
++ Chart.DatasetController.prototype.removeHoverStyle.call(me, point, me.chart.options.elements.point);
++
++ var dataVal = me.chart.data.datasets[point._datasetIndex].data[point._index];
++ var custom = point.custom || {};
++ var model = point._model;
++
++ model.radius = custom.radius ? custom.radius : me.getRadius(dataVal);
++ }
++ });
++};
++
++},{}],17:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++
++ var helpers = Chart.helpers,
++ defaults = Chart.defaults;
++
++ defaults.doughnut = {
++ animation: {
++ // Boolean - Whether we animate the rotation of the Doughnut
++ animateRotate: true,
++ // Boolean - Whether we animate scaling the Doughnut from the centre
++ animateScale: false
++ },
++ aspectRatio: 1,
++ hover: {
++ mode: 'single'
++ },
++ legendCallback: function(chart) {
++ var text = [];
++ text.push('<ul class="' + chart.id + '-legend">');
++
++ var data = chart.data;
++ var datasets = data.datasets;
++ var labels = data.labels;
++
++ if (datasets.length) {
++ for (var i = 0; i < datasets[0].data.length; ++i) {
++ text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>');
++ if (labels[i]) {
++ text.push(labels[i]);
++ }
++ text.push('</li>');
++ }
++ }
++
++ text.push('</ul>');
++ return text.join('');
++ },
++ legend: {
++ labels: {
++ generateLabels: function(chart) {
++ var data = chart.data;
++ if (data.labels.length && data.datasets.length) {
++ return data.labels.map(function(label, i) {
++ var meta = chart.getDatasetMeta(0);
++ var ds = data.datasets[0];
++ var arc = meta.data[i];
++ var custom = arc && arc.custom || {};
++ var getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault;
++ var arcOpts = chart.options.elements.arc;
++ var fill = custom.backgroundColor ? custom.backgroundColor : getValueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor);
++ var stroke = custom.borderColor ? custom.borderColor : getValueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor);
++ var bw = custom.borderWidth ? custom.borderWidth : getValueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth);
++
++ return {
++ text: label,
++ fillStyle: fill,
++ strokeStyle: stroke,
++ lineWidth: bw,
++ hidden: isNaN(ds.data[i]) || meta.data[i].hidden,
++
++ // Extra data used for toggling the correct item
++ index: i
++ };
++ });
++ }
++ return [];
++ }
++ },
++
++ onClick: function(e, legendItem) {
++ var index = legendItem.index;
++ var chart = this.chart;
++ var i, ilen, meta;
++
++ for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
++ meta = chart.getDatasetMeta(i);
++ // toggle visibility of index if exists
++ if (meta.data[index]) {
++ meta.data[index].hidden = !meta.data[index].hidden;
++ }
++ }
++
++ chart.update();
++ }
++ },
++
++ // The percentage of the chart that we cut out of the middle.
++ cutoutPercentage: 50,
++
++ // The rotation of the chart, where the first data arc begins.
++ rotation: Math.PI * -0.5,
++
++ // The total circumference of the chart.
++ circumference: Math.PI * 2.0,
++
++ // Need to override these to give a nice default
++ tooltips: {
++ callbacks: {
++ title: function() {
++ return '';
++ },
++ label: function(tooltipItem, data) {
++ var dataLabel = data.labels[tooltipItem.index];
++ var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
++
++ if (helpers.isArray(dataLabel)) {
++ // show value on first line of multiline label
++ // need to clone because we are changing the value
++ dataLabel = dataLabel.slice();
++ dataLabel[0] += value;
++ } else {
++ dataLabel += value;
++ }
++
++ return dataLabel;
++ }
++ }
++ }
++ };
++
++ defaults.pie = helpers.clone(defaults.doughnut);
++ helpers.extend(defaults.pie, {
++ cutoutPercentage: 0
++ });
++
++
++ Chart.controllers.doughnut = Chart.controllers.pie = Chart.DatasetController.extend({
++
++ dataElementType: Chart.elements.Arc,
++
++ linkScales: helpers.noop,
++
++ // Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly
++ getRingIndex: function(datasetIndex) {
++ var ringIndex = 0;
++
++ for (var j = 0; j < datasetIndex; ++j) {
++ if (this.chart.isDatasetVisible(j)) {
++ ++ringIndex;
++ }
++ }
++
++ return ringIndex;
++ },
++
++ update: function(reset) {
++ var me = this;
++ var chart = me.chart,
++ chartArea = chart.chartArea,
++ opts = chart.options,
++ arcOpts = opts.elements.arc,
++ availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth,
++ availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth,
++ minSize = Math.min(availableWidth, availableHeight),
++ offset = {
++ x: 0,
++ y: 0
++ },
++ meta = me.getMeta(),
++ cutoutPercentage = opts.cutoutPercentage,
++ circumference = opts.circumference;
++
++ // If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc
++ if (circumference < Math.PI * 2.0) {
++ var startAngle = opts.rotation % (Math.PI * 2.0);
++ startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0);
++ var endAngle = startAngle + circumference;
++ var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)};
++ var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)};
++ var contains0 = (startAngle <= 0 && 0 <= endAngle) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle);
++ var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle);
++ var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle);
++ var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle);
++ var cutout = cutoutPercentage / 100.0;
++ var min = {x: contains180 ? -1 : Math.min(start.x * (start.x < 0 ? 1 : cutout), end.x * (end.x < 0 ? 1 : cutout)), y: contains270 ? -1 : Math.min(start.y * (start.y < 0 ? 1 : cutout), end.y * (end.y < 0 ? 1 : cutout))};
++ var max = {x: contains0 ? 1 : Math.max(start.x * (start.x > 0 ? 1 : cutout), end.x * (end.x > 0 ? 1 : cutout)), y: contains90 ? 1 : Math.max(start.y * (start.y > 0 ? 1 : cutout), end.y * (end.y > 0 ? 1 : cutout))};
++ var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5};
++ minSize = Math.min(availableWidth / size.width, availableHeight / size.height);
++ offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5};
++ }
++
++ chart.borderWidth = me.getMaxBorderWidth(meta.data);
++ chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0);
++ chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 1, 0);
++ chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();
++ chart.offsetX = offset.x * chart.outerRadius;
++ chart.offsetY = offset.y * chart.outerRadius;
++
++ meta.total = me.calculateTotal();
++
++ me.outerRadius = chart.outerRadius - (chart.radiusLength * me.getRingIndex(me.index));
++ me.innerRadius = me.outerRadius - chart.radiusLength;
++
++ helpers.each(meta.data, function(arc, index) {
++ me.updateElement(arc, index, reset);
++ });
++ },
++
++ updateElement: function(arc, index, reset) {
++ var me = this;
++ var chart = me.chart,
++ chartArea = chart.chartArea,
++ opts = chart.options,
++ animationOpts = opts.animation,
++ centerX = (chartArea.left + chartArea.right) / 2,
++ centerY = (chartArea.top + chartArea.bottom) / 2,
++ startAngle = opts.rotation, // non reset case handled later
++ endAngle = opts.rotation, // non reset case handled later
++ dataset = me.getDataset(),
++ circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI)),
++ innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius,
++ outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius,
++ valueAtIndexOrDefault = helpers.getValueAtIndexOrDefault;
++
++ helpers.extend(arc, {
++ // Utility
++ _datasetIndex: me.index,
++ _index: index,
++
++ // Desired view properties
++ _model: {
++ x: centerX + chart.offsetX,
++ y: centerY + chart.offsetY,
++ startAngle: startAngle,
++ endAngle: endAngle,
++ circumference: circumference,
++ outerRadius: outerRadius,
++ innerRadius: innerRadius,
++ label: valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index])
++ }
++ });
++
++ var model = arc._model;
++ // Resets the visual styles
++ this.removeHoverStyle(arc);
++
++ // Set correct angles if not resetting
++ if (!reset || !animationOpts.animateRotate) {
++ if (index === 0) {
++ model.startAngle = opts.rotation;
++ } else {
++ model.startAngle = me.getMeta().data[index - 1]._model.endAngle;
++ }
++
++ model.endAngle = model.startAngle + model.circumference;
++ }
++
++ arc.pivot();
++ },
++
++ removeHoverStyle: function(arc) {
++ Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc);
++ },
++
++ calculateTotal: function() {
++ var dataset = this.getDataset();
++ var meta = this.getMeta();
++ var total = 0;
++ var value;
++
++ helpers.each(meta.data, function(element, index) {
++ value = dataset.data[index];
++ if (!isNaN(value) && !element.hidden) {
++ total += Math.abs(value);
++ }
++ });
++
++ /* if (total === 0) {
++ total = NaN;
++ }*/
++
++ return total;
++ },
++
++ calculateCircumference: function(value) {
++ var total = this.getMeta().total;
++ if (total > 0 && !isNaN(value)) {
++ return (Math.PI * 2.0) * (value / total);
++ }
++ return 0;
++ },
++
++ // gets the max border or hover width to properly scale pie charts
++ getMaxBorderWidth: function(elements) {
++ var max = 0,
++ index = this.index,
++ length = elements.length,
++ borderWidth,
++ hoverWidth;
++
++ for (var i = 0; i < length; i++) {
++ borderWidth = elements[i]._model ? elements[i]._model.borderWidth : 0;
++ hoverWidth = elements[i]._chart ? elements[i]._chart.config.data.datasets[index].hoverBorderWidth : 0;
++
++ max = borderWidth > max ? borderWidth : max;
++ max = hoverWidth > max ? hoverWidth : max;
++ }
++ return max;
++ }
++ });
++};
++
++},{}],18:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++
++ var helpers = Chart.helpers;
++
++ Chart.defaults.line = {
++ showLines: true,
++ spanGaps: false,
++
++ hover: {
++ mode: 'label'
++ },
++
++ scales: {
++ xAxes: [{
++ type: 'category',
++ id: 'x-axis-0'
++ }],
++ yAxes: [{
++ type: 'linear',
++ id: 'y-axis-0'
++ }]
++ }
++ };
++
++ function lineEnabled(dataset, options) {
++ return helpers.getValueOrDefault(dataset.showLine, options.showLines);
++ }
++
++ Chart.controllers.line = Chart.DatasetController.extend({
++
++ datasetElementType: Chart.elements.Line,
++
++ dataElementType: Chart.elements.Point,
++
++ update: function(reset) {
++ var me = this;
++ var meta = me.getMeta();
++ var line = meta.dataset;
++ var points = meta.data || [];
++ var options = me.chart.options;
++ var lineElementOptions = options.elements.line;
++ var scale = me.getScaleForId(meta.yAxisID);
++ var i, ilen, custom;
++ var dataset = me.getDataset();
++ var showLine = lineEnabled(dataset, options);
++
++ // Update Line
++ if (showLine) {
++ custom = line.custom || {};
++
++ // Compatibility: If the properties are defined with only the old name, use those values
++ if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) {
++ dataset.lineTension = dataset.tension;
++ }
++
++ // Utility
++ line._scale = scale;
++ line._datasetIndex = me.index;
++ // Data
++ line._children = points;
++ // Model
++ line._model = {
++ // Appearance
++ // The default behavior of lines is to break at null values, according
++ // to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158
++ // This option gives lines the ability to span gaps
++ spanGaps: dataset.spanGaps ? dataset.spanGaps : options.spanGaps,
++ tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.lineTension, lineElementOptions.tension),
++ backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor),
++ borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth),
++ borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor),
++ borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle),
++ borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash),
++ borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset),
++ borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle),
++ fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill),
++ steppedLine: custom.steppedLine ? custom.steppedLine : helpers.getValueOrDefault(dataset.steppedLine, lineElementOptions.stepped),
++ cubicInterpolationMode: custom.cubicInterpolationMode ? custom.cubicInterpolationMode : helpers.getValueOrDefault(dataset.cubicInterpolationMode, lineElementOptions.cubicInterpolationMode),
++ // Scale
++ scaleTop: scale.top,
++ scaleBottom: scale.bottom,
++ scaleZero: scale.getBasePixel()
++ };
++
++ line.pivot();
++ }
++
++ // Update Points
++ for (i=0, ilen=points.length; i<ilen; ++i) {
++ me.updateElement(points[i], i, reset);
++ }
++
++ if (showLine && line._model.tension !== 0) {
++ me.updateBezierControlPoints();
++ }
++
++ // Now pivot the point for animation
++ for (i=0, ilen=points.length; i<ilen; ++i) {
++ points[i].pivot();
++ }
++ },
++
++ getPointBackgroundColor: function(point, index) {
++ var backgroundColor = this.chart.options.elements.point.backgroundColor;
++ var dataset = this.getDataset();
++ var custom = point.custom || {};
++
++ if (custom.backgroundColor) {
++ backgroundColor = custom.backgroundColor;
++ } else if (dataset.pointBackgroundColor) {
++ backgroundColor = helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, backgroundColor);
++ } else if (dataset.backgroundColor) {
++ backgroundColor = dataset.backgroundColor;
++ }
++
++ return backgroundColor;
++ },
++
++ getPointBorderColor: function(point, index) {
++ var borderColor = this.chart.options.elements.point.borderColor;
++ var dataset = this.getDataset();
++ var custom = point.custom || {};
++
++ if (custom.borderColor) {
++ borderColor = custom.borderColor;
++ } else if (dataset.pointBorderColor) {
++ borderColor = helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, borderColor);
++ } else if (dataset.borderColor) {
++ borderColor = dataset.borderColor;
++ }
++
++ return borderColor;
++ },
++
++ getPointBorderWidth: function(point, index) {
++ var borderWidth = this.chart.options.elements.point.borderWidth;
++ var dataset = this.getDataset();
++ var custom = point.custom || {};
++
++ if (custom.borderWidth) {
++ borderWidth = custom.borderWidth;
++ } else if (dataset.pointBorderWidth) {
++ borderWidth = helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, borderWidth);
++ } else if (dataset.borderWidth) {
++ borderWidth = dataset.borderWidth;
++ }
++
++ return borderWidth;
++ },
++
++ updateElement: function(point, index, reset) {
++ var me = this;
++ var meta = me.getMeta();
++ var custom = point.custom || {};
++ var dataset = me.getDataset();
++ var datasetIndex = me.index;
++ var value = dataset.data[index];
++ var yScale = me.getScaleForId(meta.yAxisID);
++ var xScale = me.getScaleForId(meta.xAxisID);
++ var pointOptions = me.chart.options.elements.point;
++ var x, y;
++ var labels = me.chart.data.labels || [];
++ var includeOffset = (labels.length === 1 || dataset.data.length === 1) || me.chart.isCombo;
++
++ // Compatibility: If the properties are defined with only the old name, use those values
++ if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {
++ dataset.pointRadius = dataset.radius;
++ }
++ if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) {
++ dataset.pointHitRadius = dataset.hitRadius;
++ }
++
++ x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex, includeOffset);
++ y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex);
++
++ // Utility
++ point._xScale = xScale;
++ point._yScale = yScale;
++ point._datasetIndex = datasetIndex;
++ point._index = index;
++
++ // Desired view properties
++ point._model = {
++ x: x,
++ y: y,
++ skip: custom.skip || isNaN(x) || isNaN(y),
++ // Appearance
++ radius: custom.radius || helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, pointOptions.radius),
++ pointStyle: custom.pointStyle || helpers.getValueAtIndexOrDefault(dataset.pointStyle, index, pointOptions.pointStyle),
++ backgroundColor: me.getPointBackgroundColor(point, index),
++ borderColor: me.getPointBorderColor(point, index),
++ borderWidth: me.getPointBorderWidth(point, index),
++ tension: meta.dataset._model ? meta.dataset._model.tension : 0,
++ steppedLine: meta.dataset._model ? meta.dataset._model.steppedLine : false,
++ // Tooltip
++ hitRadius: custom.hitRadius || helpers.getValueAtIndexOrDefault(dataset.pointHitRadius, index, pointOptions.hitRadius)
++ };
++ },
++
++ calculatePointY: function(value, index, datasetIndex) {
++ var me = this;
++ var chart = me.chart;
++ var meta = me.getMeta();
++ var yScale = me.getScaleForId(meta.yAxisID);
++ var sumPos = 0;
++ var sumNeg = 0;
++ var i, ds, dsMeta;
++
++ if (yScale.options.stacked) {
++ for (i = 0; i < datasetIndex; i++) {
++ ds = chart.data.datasets[i];
++ dsMeta = chart.getDatasetMeta(i);
++ if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) {
++ var stackedRightValue = Number(yScale.getRightValue(ds.data[index]));
++ if (stackedRightValue < 0) {
++ sumNeg += stackedRightValue || 0;
++ } else {
++ sumPos += stackedRightValue || 0;
++ }
++ }
++ }
++
++ var rightValue = Number(yScale.getRightValue(value));
++ if (rightValue < 0) {
++ return yScale.getPixelForValue(sumNeg + rightValue);
++ }
++ return yScale.getPixelForValue(sumPos + rightValue);
++ }
++
++ return yScale.getPixelForValue(value);
++ },
++
++ updateBezierControlPoints: function() {
++ var me = this;
++ var meta = me.getMeta();
++ var area = me.chart.chartArea;
++ var points = (meta.data || []);
++ var i, ilen, point, model, controlPoints;
++
++ // Only consider points that are drawn in case the spanGaps option is used
++ if (meta.dataset._model.spanGaps) {
++ points = points.filter(function(pt) {
++ return !pt._model.skip;
++ });
++ }
++
++ function capControlPoint(pt, min, max) {
++ return Math.max(Math.min(pt, max), min);
++ }
++
++ if (meta.dataset._model.cubicInterpolationMode === 'monotone') {
++ helpers.splineCurveMonotone(points);
++ } else {
++ for (i = 0, ilen = points.length; i < ilen; ++i) {
++ point = points[i];
++ model = point._model;
++ controlPoints = helpers.splineCurve(
++ helpers.previousItem(points, i)._model,
++ model,
++ helpers.nextItem(points, i)._model,
++ meta.dataset._model.tension
++ );
++ model.controlPointPreviousX = controlPoints.previous.x;
++ model.controlPointPreviousY = controlPoints.previous.y;
++ model.controlPointNextX = controlPoints.next.x;
++ model.controlPointNextY = controlPoints.next.y;
++ }
++ }
++
++ if (me.chart.options.elements.line.capBezierPoints) {
++ for (i = 0, ilen = points.length; i < ilen; ++i) {
++ model = points[i]._model;
++ model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right);
++ model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom);
++ model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right);
++ model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom);
++ }
++ }
++ },
++
++ draw: function(ease) {
++ var me = this;
++ var meta = me.getMeta();
++ var points = meta.data || [];
++ var easingDecimal = ease || 1;
++ var i, ilen;
++
++ // Transition Point Locations
++ for (i=0, ilen=points.length; i<ilen; ++i) {
++ points[i].transition(easingDecimal);
++ }
++
++ // Transition and Draw the line
++ if (lineEnabled(me.getDataset(), me.chart.options)) {
++ meta.dataset.transition(easingDecimal).draw();
++ }
++
++ // Draw the points
++ for (i=0, ilen=points.length; i<ilen; ++i) {
++ points[i].draw();
++ }
++ },
++
++ setHoverStyle: function(point) {
++ // Point
++ var dataset = this.chart.data.datasets[point._datasetIndex];
++ var index = point._index;
++ var custom = point.custom || {};
++ var model = point._model;
++
++ model.radius = custom.hoverRadius || helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
++ model.backgroundColor = custom.hoverBackgroundColor || helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
++ model.borderColor = custom.hoverBorderColor || helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor));
++ model.borderWidth = custom.hoverBorderWidth || helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth);
++ },
++
++ removeHoverStyle: function(point) {
++ var me = this;
++ var dataset = me.chart.data.datasets[point._datasetIndex];
++ var index = point._index;
++ var custom = point.custom || {};
++ var model = point._model;
++
++ // Compatibility: If the properties are defined with only the old name, use those values
++ if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {
++ dataset.pointRadius = dataset.radius;
++ }
++
++ model.radius = custom.radius || helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, me.chart.options.elements.point.radius);
++ model.backgroundColor = me.getPointBackgroundColor(point, index);
++ model.borderColor = me.getPointBorderColor(point, index);
++ model.borderWidth = me.getPointBorderWidth(point, index);
++ }
++ });
++};
++
++},{}],19:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++
++ var helpers = Chart.helpers;
++
++ Chart.defaults.polarArea = {
++
++ scale: {
++ type: 'radialLinear',
++ lineArc: true, // so that lines are circular
++ ticks: {
++ beginAtZero: true
++ }
++ },
++
++ // Boolean - Whether to animate the rotation of the chart
++ animation: {
++ animateRotate: true,
++ animateScale: true
++ },
++
++ startAngle: -0.5 * Math.PI,
++ aspectRatio: 1,
++ legendCallback: function(chart) {
++ var text = [];
++ text.push('<ul class="' + chart.id + '-legend">');
++
++ var data = chart.data;
++ var datasets = data.datasets;
++ var labels = data.labels;
++
++ if (datasets.length) {
++ for (var i = 0; i < datasets[0].data.length; ++i) {
++ text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>');
++ if (labels[i]) {
++ text.push(labels[i]);
++ }
++ text.push('</li>');
++ }
++ }
++
++ text.push('</ul>');
++ return text.join('');
++ },
++ legend: {
++ labels: {
++ generateLabels: function(chart) {
++ var data = chart.data;
++ if (data.labels.length && data.datasets.length) {
++ return data.labels.map(function(label, i) {
++ var meta = chart.getDatasetMeta(0);
++ var ds = data.datasets[0];
++ var arc = meta.data[i];
++ var custom = arc.custom || {};
++ var getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault;
++ var arcOpts = chart.options.elements.arc;
++ var fill = custom.backgroundColor ? custom.backgroundColor : getValueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor);
++ var stroke = custom.borderColor ? custom.borderColor : getValueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor);
++ var bw = custom.borderWidth ? custom.borderWidth : getValueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth);
++
++ return {
++ text: label,
++ fillStyle: fill,
++ strokeStyle: stroke,
++ lineWidth: bw,
++ hidden: isNaN(ds.data[i]) || meta.data[i].hidden,
++
++ // Extra data used for toggling the correct item
++ index: i
++ };
++ });
++ }
++ return [];
++ }
++ },
++
++ onClick: function(e, legendItem) {
++ var index = legendItem.index;
++ var chart = this.chart;
++ var i, ilen, meta;
++
++ for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
++ meta = chart.getDatasetMeta(i);
++ meta.data[index].hidden = !meta.data[index].hidden;
++ }
++
++ chart.update();
++ }
++ },
++
++ // Need to override these to give a nice default
++ tooltips: {
++ callbacks: {
++ title: function() {
++ return '';
++ },
++ label: function(tooltipItem, data) {
++ return data.labels[tooltipItem.index] + ': ' + tooltipItem.yLabel;
++ }
++ }
++ }
++ };
++
++ Chart.controllers.polarArea = Chart.DatasetController.extend({
++
++ dataElementType: Chart.elements.Arc,
++
++ linkScales: helpers.noop,
++
++ update: function(reset) {
++ var me = this;
++ var chart = me.chart;
++ var chartArea = chart.chartArea;
++ var meta = me.getMeta();
++ var opts = chart.options;
++ var arcOpts = opts.elements.arc;
++ var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);
++ chart.outerRadius = Math.max((minSize - arcOpts.borderWidth / 2) / 2, 0);
++ chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0);
++ chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();
++
++ me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index);
++ me.innerRadius = me.outerRadius - chart.radiusLength;
++
++ meta.count = me.countVisibleElements();
++
++ helpers.each(meta.data, function(arc, index) {
++ me.updateElement(arc, index, reset);
++ });
++ },
++
++ updateElement: function(arc, index, reset) {
++ var me = this;
++ var chart = me.chart;
++ var dataset = me.getDataset();
++ var opts = chart.options;
++ var animationOpts = opts.animation;
++ var scale = chart.scale;
++ var getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault;
++ var labels = chart.data.labels;
++
++ var circumference = me.calculateCircumference(dataset.data[index]);
++ var centerX = scale.xCenter;
++ var centerY = scale.yCenter;
++
++ // If there is NaN data before us, we need to calculate the starting angle correctly.
++ // We could be way more efficient here, but its unlikely that the polar area chart will have a lot of data
++ var visibleCount = 0;
++ var meta = me.getMeta();
++ for (var i = 0; i < index; ++i) {
++ if (!isNaN(dataset.data[i]) && !meta.data[i].hidden) {
++ ++visibleCount;
++ }
++ }
++
++ // var negHalfPI = -0.5 * Math.PI;
++ var datasetStartAngle = opts.startAngle;
++ var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);
++ var startAngle = datasetStartAngle + (circumference * visibleCount);
++ var endAngle = startAngle + (arc.hidden ? 0 : circumference);
++
++ var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);
++
++ helpers.extend(arc, {
++ // Utility
++ _datasetIndex: me.index,
++ _index: index,
++ _scale: scale,
++
++ // Desired view properties
++ _model: {
++ x: centerX,
++ y: centerY,
++ innerRadius: 0,
++ outerRadius: reset ? resetRadius : distance,
++ startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle,
++ endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle,
++ label: getValueAtIndexOrDefault(labels, index, labels[index])
++ }
++ });
++
++ // Apply border and fill style
++ me.removeHoverStyle(arc);
++
++ arc.pivot();
++ },
++
++ removeHoverStyle: function(arc) {
++ Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc);
++ },
++
++ countVisibleElements: function() {
++ var dataset = this.getDataset();
++ var meta = this.getMeta();
++ var count = 0;
++
++ helpers.each(meta.data, function(element, index) {
++ if (!isNaN(dataset.data[index]) && !element.hidden) {
++ count++;
++ }
++ });
++
++ return count;
++ },
++
++ calculateCircumference: function(value) {
++ var count = this.getMeta().count;
++ if (count > 0 && !isNaN(value)) {
++ return (2 * Math.PI) / count;
++ }
++ return 0;
++ }
++ });
++};
++
++},{}],20:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++
++ var helpers = Chart.helpers;
++
++ Chart.defaults.radar = {
++ aspectRatio: 1,
++ scale: {
++ type: 'radialLinear'
++ },
++ elements: {
++ line: {
++ tension: 0 // no bezier in radar
++ }
++ }
++ };
++
++ Chart.controllers.radar = Chart.DatasetController.extend({
++
++ datasetElementType: Chart.elements.Line,
++
++ dataElementType: Chart.elements.Point,
++
++ linkScales: helpers.noop,
++
++ update: function(reset) {
++ var me = this;
++ var meta = me.getMeta();
++ var line = meta.dataset;
++ var points = meta.data;
++ var custom = line.custom || {};
++ var dataset = me.getDataset();
++ var lineElementOptions = me.chart.options.elements.line;
++ var scale = me.chart.scale;
++
++ // Compatibility: If the properties are defined with only the old name, use those values
++ if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) {
++ dataset.lineTension = dataset.tension;
++ }
++
++ helpers.extend(meta.dataset, {
++ // Utility
++ _datasetIndex: me.index,
++ // Data
++ _children: points,
++ _loop: true,
++ // Model
++ _model: {
++ // Appearance
++ tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.lineTension, lineElementOptions.tension),
++ backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor),
++ borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth),
++ borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor),
++ fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill),
++ borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle),
++ borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash),
++ borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset),
++ borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle),
++
++ // Scale
++ scaleTop: scale.top,
++ scaleBottom: scale.bottom,
++ scaleZero: scale.getBasePosition()
++ }
++ });
++
++ meta.dataset.pivot();
++
++ // Update Points
++ helpers.each(points, function(point, index) {
++ me.updateElement(point, index, reset);
++ }, me);
++
++ // Update bezier control points
++ me.updateBezierControlPoints();
++ },
++ updateElement: function(point, index, reset) {
++ var me = this;
++ var custom = point.custom || {};
++ var dataset = me.getDataset();
++ var scale = me.chart.scale;
++ var pointElementOptions = me.chart.options.elements.point;
++ var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]);
++
++ helpers.extend(point, {
++ // Utility
++ _datasetIndex: me.index,
++ _index: index,
++ _scale: scale,
++
++ // Desired view properties
++ _model: {
++ x: reset ? scale.xCenter : pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales
++ y: reset ? scale.yCenter : pointPosition.y,
++
++ // Appearance
++ tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.tension, me.chart.options.elements.line.tension),
++ radius: custom.radius ? custom.radius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius),
++ backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor),
++ borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor),
++ borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth),
++ pointStyle: custom.pointStyle ? custom.pointStyle : helpers.getValueAtIndexOrDefault(dataset.pointStyle, index, pointElementOptions.pointStyle),
++
++ // Tooltip
++ hitRadius: custom.hitRadius ? custom.hitRadius : helpers.getValueAtIndexOrDefault(dataset.hitRadius, index, pointElementOptions.hitRadius)
++ }
++ });
++
++ point._model.skip = custom.skip ? custom.skip : (isNaN(point._model.x) || isNaN(point._model.y));
++ },
++ updateBezierControlPoints: function() {
++ var chartArea = this.chart.chartArea;
++ var meta = this.getMeta();
++
++ helpers.each(meta.data, function(point, index) {
++ var model = point._model;
++ var controlPoints = helpers.splineCurve(
++ helpers.previousItem(meta.data, index, true)._model,
++ model,
++ helpers.nextItem(meta.data, index, true)._model,
++ model.tension
++ );
++
++ // Prevent the bezier going outside of the bounds of the graph
++ model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, chartArea.right), chartArea.left);
++ model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, chartArea.bottom), chartArea.top);
++
++ model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, chartArea.right), chartArea.left);
++ model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, chartArea.bottom), chartArea.top);
++
++ // Now pivot the point for animation
++ point.pivot();
++ });
++ },
++
++ draw: function(ease) {
++ var meta = this.getMeta();
++ var easingDecimal = ease || 1;
++
++ // Transition Point Locations
++ helpers.each(meta.data, function(point) {
++ point.transition(easingDecimal);
++ });
++
++ // Transition and Draw the line
++ meta.dataset.transition(easingDecimal).draw();
++
++ // Draw the points
++ helpers.each(meta.data, function(point) {
++ point.draw();
++ });
++ },
++
++ setHoverStyle: function(point) {
++ // Point
++ var dataset = this.chart.data.datasets[point._datasetIndex];
++ var custom = point.custom || {};
++ var index = point._index;
++ var model = point._model;
++
++ model.radius = custom.hoverRadius ? custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
++ model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
++ model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor));
++ model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth);
++ },
++
++ removeHoverStyle: function(point) {
++ var dataset = this.chart.data.datasets[point._datasetIndex];
++ var custom = point.custom || {};
++ var index = point._index;
++ var model = point._model;
++ var pointElementOptions = this.chart.options.elements.point;
++
++ model.radius = custom.radius ? custom.radius : helpers.getValueAtIndexOrDefault(dataset.radius, index, pointElementOptions.radius);
++ model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor);
++ model.borderColor = custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor);
++ model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth);
++ }
++ });
++};
++
++},{}],21:[function(require,module,exports){
++/* global window: false */
++'use strict';
++
++module.exports = function(Chart) {
++
++ var helpers = Chart.helpers;
++
++ Chart.defaults.global.animation = {
++ duration: 1000,
++ easing: 'easeOutQuart',
++ onProgress: helpers.noop,
++ onComplete: helpers.noop
++ };
++
++ Chart.Animation = Chart.Element.extend({
++ currentStep: null, // the current animation step
++ numSteps: 60, // default number of steps
++ easing: '', // the easing to use for this animation
++ render: null, // render function used by the animation service
++
++ onAnimationProgress: null, // user specified callback to fire on each step of the animation
++ onAnimationComplete: null // user specified callback to fire when the animation finishes
++ });
++
++ Chart.animationService = {
++ frameDuration: 17,
++ animations: [],
++ dropFrames: 0,
++ request: null,
++
++ /**
++ * @function Chart.animationService.addAnimation
++ * @param chartInstance {ChartController} the chart to animate
++ * @param animationObject {IAnimation} the animation that we will animate
++ * @param duration {Number} length of animation in ms
++ * @param lazy {Boolean} if true, the chart is not marked as animating to enable more responsive interactions
++ */
++ addAnimation: function(chartInstance, animationObject, duration, lazy) {
++ var me = this;
++
++ if (!lazy) {
++ chartInstance.animating = true;
++ }
++
++ for (var index = 0; index < me.animations.length; ++index) {
++ if (me.animations[index].chartInstance === chartInstance) {
++ // replacing an in progress animation
++ me.animations[index].animationObject = animationObject;
++ return;
++ }
++ }
++
++ me.animations.push({
++ chartInstance: chartInstance,
++ animationObject: animationObject
++ });
++
++ // If there are no animations queued, manually kickstart a digest, for lack of a better word
++ if (me.animations.length === 1) {
++ me.requestAnimationFrame();
++ }
++ },
++ // Cancel the animation for a given chart instance
++ cancelAnimation: function(chartInstance) {
++ var index = helpers.findIndex(this.animations, function(animationWrapper) {
++ return animationWrapper.chartInstance === chartInstance;
++ });
++
++ if (index !== -1) {
++ this.animations.splice(index, 1);
++ chartInstance.animating = false;
++ }
++ },
++ requestAnimationFrame: function() {
++ var me = this;
++ if (me.request === null) {
++ // Skip animation frame requests until the active one is executed.
++ // This can happen when processing mouse events, e.g. 'mousemove'
++ // and 'mouseout' events will trigger multiple renders.
++ me.request = helpers.requestAnimFrame.call(window, function() {
++ me.request = null;
++ me.startDigest();
++ });
++ }
++ },
++ startDigest: function() {
++ var me = this;
++
++ var startTime = Date.now();
++ var framesToDrop = 0;
++
++ if (me.dropFrames > 1) {
++ framesToDrop = Math.floor(me.dropFrames);
++ me.dropFrames = me.dropFrames % 1;
++ }
++
++ var i = 0;
++ while (i < me.animations.length) {
++ if (me.animations[i].animationObject.currentStep === null) {
++ me.animations[i].animationObject.currentStep = 0;
++ }
++
++ me.animations[i].animationObject.currentStep += 1 + framesToDrop;
++
++ if (me.animations[i].animationObject.currentStep > me.animations[i].animationObject.numSteps) {
++ me.animations[i].animationObject.currentStep = me.animations[i].animationObject.numSteps;
++ }
++
++ me.animations[i].animationObject.render(me.animations[i].chartInstance, me.animations[i].animationObject);
++ if (me.animations[i].animationObject.onAnimationProgress && me.animations[i].animationObject.onAnimationProgress.call) {
++ me.animations[i].animationObject.onAnimationProgress.call(me.animations[i].chartInstance, me.animations[i]);
++ }
++
++ if (me.animations[i].animationObject.currentStep === me.animations[i].animationObject.numSteps) {
++ if (me.animations[i].animationObject.onAnimationComplete && me.animations[i].animationObject.onAnimationComplete.call) {
++ me.animations[i].animationObject.onAnimationComplete.call(me.animations[i].chartInstance, me.animations[i]);
++ }
++
++ // executed the last frame. Remove the animation.
++ me.animations[i].chartInstance.animating = false;
++
++ me.animations.splice(i, 1);
++ } else {
++ ++i;
++ }
++ }
++
++ var endTime = Date.now();
++ var dropFrames = (endTime - startTime) / me.frameDuration;
++
++ me.dropFrames += dropFrames;
++
++ // Do we have more stuff to animate?
++ if (me.animations.length > 0) {
++ me.requestAnimationFrame();
++ }
++ }
++ };
++};
++
++},{}],22:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++ // Global Chart canvas helpers object for drawing items to canvas
++ var helpers = Chart.canvasHelpers = {};
++
++ helpers.drawPoint = function(ctx, pointStyle, radius, x, y) {
++ var type, edgeLength, xOffset, yOffset, height, size;
++
++ if (typeof pointStyle === 'object') {
++ type = pointStyle.toString();
++ if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') {
++ ctx.drawImage(pointStyle, x - pointStyle.width / 2, y - pointStyle.height / 2);
++ return;
++ }
++ }
++
++ if (isNaN(radius) || radius <= 0) {
++ return;
++ }
++
++ switch (pointStyle) {
++ // Default includes circle
++ default:
++ ctx.beginPath();
++ ctx.arc(x, y, radius, 0, Math.PI * 2);
++ ctx.closePath();
++ ctx.fill();
++ break;
++ case 'triangle':
++ ctx.beginPath();
++ edgeLength = 3 * radius / Math.sqrt(3);
++ height = edgeLength * Math.sqrt(3) / 2;
++ ctx.moveTo(x - edgeLength / 2, y + height / 3);
++ ctx.lineTo(x + edgeLength / 2, y + height / 3);
++ ctx.lineTo(x, y - 2 * height / 3);
++ ctx.closePath();
++ ctx.fill();
++ break;
++ case 'rect':
++ size = 1 / Math.SQRT2 * radius;
++ ctx.beginPath();
++ ctx.fillRect(x - size, y - size, 2 * size, 2 * size);
++ ctx.strokeRect(x - size, y - size, 2 * size, 2 * size);
++ break;
++ case 'rectRot':
++ size = 1 / Math.SQRT2 * radius;
++ ctx.beginPath();
++ ctx.moveTo(x - size, y);
++ ctx.lineTo(x, y + size);
++ ctx.lineTo(x + size, y);
++ ctx.lineTo(x, y - size);
++ ctx.closePath();
++ ctx.fill();
++ break;
++ case 'cross':
++ ctx.beginPath();
++ ctx.moveTo(x, y + radius);
++ ctx.lineTo(x, y - radius);
++ ctx.moveTo(x - radius, y);
++ ctx.lineTo(x + radius, y);
++ ctx.closePath();
++ break;
++ case 'crossRot':
++ ctx.beginPath();
++ xOffset = Math.cos(Math.PI / 4) * radius;
++ yOffset = Math.sin(Math.PI / 4) * radius;
++ ctx.moveTo(x - xOffset, y - yOffset);
++ ctx.lineTo(x + xOffset, y + yOffset);
++ ctx.moveTo(x - xOffset, y + yOffset);
++ ctx.lineTo(x + xOffset, y - yOffset);
++ ctx.closePath();
++ break;
++ case 'star':
++ ctx.beginPath();
++ ctx.moveTo(x, y + radius);
++ ctx.lineTo(x, y - radius);
++ ctx.moveTo(x - radius, y);
++ ctx.lineTo(x + radius, y);
++ xOffset = Math.cos(Math.PI / 4) * radius;
++ yOffset = Math.sin(Math.PI / 4) * radius;
++ ctx.moveTo(x - xOffset, y - yOffset);
++ ctx.lineTo(x + xOffset, y + yOffset);
++ ctx.moveTo(x - xOffset, y + yOffset);
++ ctx.lineTo(x + xOffset, y - yOffset);
++ ctx.closePath();
++ break;
++ case 'line':
++ ctx.beginPath();
++ ctx.moveTo(x - radius, y);
++ ctx.lineTo(x + radius, y);
++ ctx.closePath();
++ break;
++ case 'dash':
++ ctx.beginPath();
++ ctx.moveTo(x, y);
++ ctx.lineTo(x + radius, y);
++ ctx.closePath();
++ break;
++ }
++
++ ctx.stroke();
++ };
++};
++
++},{}],23:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++
++ var helpers = Chart.helpers;
++
++ // Create a dictionary of chart types, to allow for extension of existing types
++ Chart.types = {};
++
++ // Store a reference to each instance - allowing us to globally resize chart instances on window resize.
++ // Destroy method on the chart will remove the instance of the chart from this reference.
++ Chart.instances = {};
++
++ // Controllers available for dataset visualization eg. bar, line, slice, etc.
++ Chart.controllers = {};
++
++ /**
++ * The "used" size is the final value of a dimension property after all calculations have
++ * been performed. This method uses the computed style of `element` but returns undefined
++ * if the computed style is not expressed in pixels. That can happen in some cases where
++ * `element` has a size relative to its parent and this last one is not yet displayed,
++ * for example because of `display: none` on a parent node.
++ * TODO(SB) Move this method in the upcoming core.platform class.
++ * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value
++ * @returns {Number} Size in pixels or undefined if unknown.
++ */
++ function readUsedSize(element, property) {
++ var value = helpers.getStyle(element, property);
++ var matches = value && value.match(/(\d+)px/);
++ return matches? Number(matches[1]) : undefined;
++ }
++
++ /**
++ * Initializes the canvas style and render size without modifying the canvas display size,
++ * since responsiveness is handled by the controller.resize() method. The config is used
++ * to determine the aspect ratio to apply in case no explicit height has been specified.
++ * TODO(SB) Move this method in the upcoming core.platform class.
++ */
++ function initCanvas(canvas, config) {
++ var style = canvas.style;
++
++ // NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it
++ // returns null or '' if no explicit value has been set to the canvas attribute.
++ var renderHeight = canvas.getAttribute('height');
++ var renderWidth = canvas.getAttribute('width');
++
++ // Chart.js modifies some canvas values that we want to restore on destroy
++ canvas._chartjs = {
++ initial: {
++ height: renderHeight,
++ width: renderWidth,
++ style: {
++ display: style.display,
++ height: style.height,
++ width: style.width
++ }
++ }
++ };
++
++ // Force canvas to display as block to avoid extra space caused by inline
++ // elements, which would interfere with the responsive resize process.
++ // https://github.com/chartjs/Chart.js/issues/2538
++ style.display = style.display || 'block';
++
++ if (renderWidth === null || renderWidth === '') {
++ var displayWidth = readUsedSize(canvas, 'width');
++ if (displayWidth !== undefined) {
++ canvas.width = displayWidth;
++ }
++ }
++
++ if (renderHeight === null || renderHeight === '') {
++ if (canvas.style.height === '') {
++ // If no explicit render height and style height, let's apply the aspect ratio,
++ // which one can be specified by the user but also by charts as default option
++ // (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2.
++ canvas.height = canvas.width / (config.options.aspectRatio || 2);
++ } else {
++ var displayHeight = readUsedSize(canvas, 'height');
++ if (displayWidth !== undefined) {
++ canvas.height = displayHeight;
++ }
++ }
++ }
++
++ return canvas;
++ }
++
++ /**
++ * Restores the canvas initial state, such as render/display sizes and style.
++ * TODO(SB) Move this method in the upcoming core.platform class.
++ */
++ function releaseCanvas(canvas) {
++ if (!canvas._chartjs) {
++ return;
++ }
++
++ var initial = canvas._chartjs.initial;
++ ['height', 'width'].forEach(function(prop) {
++ var value = initial[prop];
++ if (value === undefined || value === null) {
++ canvas.removeAttribute(prop);
++ } else {
++ canvas.setAttribute(prop, value);
++ }
++ });
++
++ helpers.each(initial.style || {}, function(value, key) {
++ canvas.style[key] = value;
++ });
++
++ // The canvas render size might have been changed (and thus the state stack discarded),
++ // we can't use save() and restore() to restore the initial state. So make sure that at
++ // least the canvas context is reset to the default state by setting the canvas width.
++ // https://www.w3.org/TR/2011/WD-html5-20110525/the-canvas-element.html
++ canvas.width = canvas.width;
++
++ delete canvas._chartjs;
++ }
++
++ /**
++ * TODO(SB) Move this method in the upcoming core.platform class.
++ */
++ function acquireContext(item, config) {
++ if (typeof item === 'string') {
++ item = document.getElementById(item);
++ } else if (item.length) {
++ // Support for array based queries (such as jQuery)
++ item = item[0];
++ }
++
++ if (item && item.canvas) {
++ // Support for any object associated to a canvas (including a context2d)
++ item = item.canvas;
++ }
++
++ if (item instanceof HTMLCanvasElement) {
++ // To prevent canvas fingerprinting, some add-ons undefine the getContext
++ // method, for example: https://github.com/kkapsner/CanvasBlocker
++ // https://github.com/chartjs/Chart.js/issues/2807
++ var context = item.getContext && item.getContext('2d');
++ if (context instanceof CanvasRenderingContext2D) {
++ initCanvas(item, config);
++ return context;
++ }
++ }
++
++ return null;
++ }
++
++ /**
++ * Initializes the given config with global and chart default values.
++ */
++ function initConfig(config) {
++ config = config || {};
++
++ // Do NOT use configMerge() for the data object because this method merges arrays
++ // and so would change references to labels and datasets, preventing data updates.
++ var data = config.data = config.data || {};
++ data.datasets = data.datasets || [];
++ data.labels = data.labels || [];
++
++ config.options = helpers.configMerge(
++ Chart.defaults.global,
++ Chart.defaults[config.type],
++ config.options || {});
++
++ return config;
++ }
++
++ /**
++ * @class Chart.Controller
++ * The main controller of a chart.
++ */
++ Chart.Controller = function(item, config, instance) {
++ var me = this;
++
++ config = initConfig(config);
++
++ var context = acquireContext(item, config);
++ var canvas = context && context.canvas;
++ var height = canvas && canvas.height;
++ var width = canvas && canvas.width;
++
++ instance.ctx = context;
++ instance.canvas = canvas;
++ instance.config = config;
++ instance.width = width;
++ instance.height = height;
++ instance.aspectRatio = height? width / height : null;
++
++ me.id = helpers.uid();
++ me.chart = instance;
++ me.config = config;
++ me.options = config.options;
++ me._bufferedRender = false;
++
++ // Add the chart instance to the global namespace
++ Chart.instances[me.id] = me;
++
++ Object.defineProperty(me, 'data', {
++ get: function() {
++ return me.config.data;
++ }
++ });
++
++ if (!context || !canvas) {
++ // The given item is not a compatible context2d element, let's return before finalizing
++ // the chart initialization but after setting basic chart / controller properties that
++ // can help to figure out that the chart is not valid (e.g chart.canvas !== null);
++ // https://github.com/chartjs/Chart.js/issues/2807
++ console.error("Failed to create chart: can't acquire context from the given item");
++ return me;
++ }
++
++ helpers.retinaScale(instance);
++
++ // Responsiveness is currently based on the use of an iframe, however this method causes
++ // performance issues and could be troublesome when used with ad blockers. So make sure
++ // that the user is still able to create a chart without iframe when responsive is false.
++ // See https://github.com/chartjs/Chart.js/issues/2210
++ if (me.options.responsive) {
++ helpers.addResizeListener(canvas.parentNode, function() {
++ me.resize();
++ });
++
++ // Initial resize before chart draws (must be silent to preserve initial animations).
++ me.resize(true);
++ }
++
++ me.initialize();
++
++ return me;
++ };
++
++ helpers.extend(Chart.Controller.prototype, /** @lends Chart.Controller */ {
++ initialize: function() {
++ var me = this;
++
++ // Before init plugin notification
++ Chart.plugins.notify('beforeInit', [me]);
++
++ me.bindEvents();
++
++ // Make sure controllers are built first so that each dataset is bound to an axis before the scales
++ // are built
++ me.ensureScalesHaveIDs();
++ me.buildOrUpdateControllers();
++ me.buildScales();
++ me.updateLayout();
++ me.resetElements();
++ me.initToolTip();
++ me.update();
++
++ // After init plugin notification
++ Chart.plugins.notify('afterInit', [me]);
++
++ return me;
++ },
++
++ clear: function() {
++ helpers.clear(this.chart);
++ return this;
++ },
++
++ stop: function() {
++ // Stops any current animation loop occurring
++ Chart.animationService.cancelAnimation(this);
++ return this;
++ },
++
++ resize: function(silent) {
++ var me = this;
++ var chart = me.chart;
++ var options = me.options;
++ var canvas = chart.canvas;
++ var aspectRatio = (options.maintainAspectRatio && chart.aspectRatio) || null;
++
++ // the canvas render width and height will be casted to integers so make sure that
++ // the canvas display style uses the same integer values to avoid blurring effect.
++ var newWidth = Math.floor(helpers.getMaximumWidth(canvas));
++ var newHeight = Math.floor(aspectRatio? newWidth / aspectRatio : helpers.getMaximumHeight(canvas));
++
++ if (chart.width === newWidth && chart.height === newHeight) {
++ return;
++ }
++
++ canvas.width = chart.width = newWidth;
++ canvas.height = chart.height = newHeight;
++ canvas.style.width = newWidth + 'px';
++ canvas.style.height = newHeight + 'px';
++
++ helpers.retinaScale(chart);
++
++ // Notify any plugins about the resize
++ var newSize = {width: newWidth, height: newHeight};
++ Chart.plugins.notify('resize', [me, newSize]);
++
++ // Notify of resize
++ if (me.options.onResize) {
++ me.options.onResize(me, newSize);
++ }
++
++ if (!silent) {
++ me.stop();
++ me.update(me.options.responsiveAnimationDuration);
++ }
++ },
++
++ ensureScalesHaveIDs: function() {
++ var options = this.options;
++ var scalesOptions = options.scales || {};
++ var scaleOptions = options.scale;
++
++ helpers.each(scalesOptions.xAxes, function(xAxisOptions, index) {
++ xAxisOptions.id = xAxisOptions.id || ('x-axis-' + index);
++ });
++
++ helpers.each(scalesOptions.yAxes, function(yAxisOptions, index) {
++ yAxisOptions.id = yAxisOptions.id || ('y-axis-' + index);
++ });
++
++ if (scaleOptions) {
++ scaleOptions.id = scaleOptions.id || 'scale';
++ }
++ },
++
++ /**
++ * Builds a map of scale ID to scale object for future lookup.
++ */
++ buildScales: function() {
++ var me = this;
++ var options = me.options;
++ var scales = me.scales = {};
++ var items = [];
++
++ if (options.scales) {
++ items = items.concat(
++ (options.scales.xAxes || []).map(function(xAxisOptions) {
++ return {options: xAxisOptions, dtype: 'category'};
++ }),
++ (options.scales.yAxes || []).map(function(yAxisOptions) {
++ return {options: yAxisOptions, dtype: 'linear'};
++ })
++ );
++ }
++
++ if (options.scale) {
++ items.push({options: options.scale, dtype: 'radialLinear', isDefault: true});
++ }
++
++ helpers.each(items, function(item) {
++ var scaleOptions = item.options;
++ var scaleType = helpers.getValueOrDefault(scaleOptions.type, item.dtype);
++ var scaleClass = Chart.scaleService.getScaleConstructor(scaleType);
++ if (!scaleClass) {
++ return;
++ }
++
++ var scale = new scaleClass({
++ id: scaleOptions.id,
++ options: scaleOptions,
++ ctx: me.chart.ctx,
++ chart: me
++ });
++
++ scales[scale.id] = scale;
++
++ // TODO(SB): I think we should be able to remove this custom case (options.scale)
++ // and consider it as a regular scale part of the "scales"" map only! This would
++ // make the logic easier and remove some useless? custom code.
++ if (item.isDefault) {
++ me.scale = scale;
++ }
++ });
++
++ Chart.scaleService.addScalesToLayout(this);
++ },
++
++ updateLayout: function() {
++ Chart.layoutService.update(this, this.chart.width, this.chart.height);
++ },
++
++ buildOrUpdateControllers: function() {
++ var me = this;
++ var types = [];
++ var newControllers = [];
++
++ helpers.each(me.data.datasets, function(dataset, datasetIndex) {
++ var meta = me.getDatasetMeta(datasetIndex);
++ if (!meta.type) {
++ meta.type = dataset.type || me.config.type;
++ }
++
++ types.push(meta.type);
++
++ if (meta.controller) {
++ meta.controller.updateIndex(datasetIndex);
++ } else {
++ meta.controller = new Chart.controllers[meta.type](me, datasetIndex);
++ newControllers.push(meta.controller);
++ }
++ }, me);
++
++ if (types.length > 1) {
++ for (var i = 1; i < types.length; i++) {
++ if (types[i] !== types[i - 1]) {
++ me.isCombo = true;
++ break;
++ }
++ }
++ }
++
++ return newControllers;
++ },
++
++ /**
++ * Reset the elements of all datasets
++ * @method resetElements
++ * @private
++ */
++ resetElements: function() {
++ var me = this;
++ helpers.each(me.data.datasets, function(dataset, datasetIndex) {
++ me.getDatasetMeta(datasetIndex).controller.reset();
++ }, me);
++ },
++
++ /**
++ * Resets the chart back to it's state before the initial animation
++ * @method reset
++ */
++ reset: function() {
++ this.resetElements();
++ this.tooltip.initialize();
++ },
++
++ update: function(animationDuration, lazy) {
++ var me = this;
++ Chart.plugins.notify('beforeUpdate', [me]);
++
++ // In case the entire data object changed
++ me.tooltip._data = me.data;
++
++ // Make sure dataset controllers are updated and new controllers are reset
++ var newControllers = me.buildOrUpdateControllers();
++
++ // Make sure all dataset controllers have correct meta data counts
++ helpers.each(me.data.datasets, function(dataset, datasetIndex) {
++ me.getDatasetMeta(datasetIndex).controller.buildOrUpdateElements();
++ }, me);
++
++ Chart.layoutService.update(me, me.chart.width, me.chart.height);
++
++ // Apply changes to the datasets that require the scales to have been calculated i.e BorderColor changes
++ Chart.plugins.notify('afterScaleUpdate', [me]);
++
++ // Can only reset the new controllers after the scales have been updated
++ helpers.each(newControllers, function(controller) {
++ controller.reset();
++ });
++
++ me.updateDatasets();
++
++ // Do this before render so that any plugins that need final scale updates can use it
++ Chart.plugins.notify('afterUpdate', [me]);
++
++ if (me._bufferedRender) {
++ me._bufferedRequest = {
++ lazy: lazy,
++ duration: animationDuration
++ };
++ } else {
++ me.render(animationDuration, lazy);
++ }
++ },
++
++ /**
++ * @method beforeDatasetsUpdate
++ * @description Called before all datasets are updated. If a plugin returns false,
++ * the datasets update will be cancelled until another chart update is triggered.
++ * @param {Object} instance the chart instance being updated.
++ * @returns {Boolean} false to cancel the datasets update.
++ * @memberof Chart.PluginBase
++ * @since version 2.1.5
++ * @instance
++ */
++
++ /**
++ * @method afterDatasetsUpdate
++ * @description Called after all datasets have been updated. Note that this
++ * extension will not be called if the datasets update has been cancelled.
++ * @param {Object} instance the chart instance being updated.
++ * @memberof Chart.PluginBase
++ * @since version 2.1.5
++ * @instance
++ */
++
++ /**
++ * Updates all datasets unless a plugin returns false to the beforeDatasetsUpdate
++ * extension, in which case no datasets will be updated and the afterDatasetsUpdate
++ * notification will be skipped.
++ * @protected
++ * @instance
++ */
++ updateDatasets: function() {
++ var me = this;
++ var i, ilen;
++
++ if (Chart.plugins.notify('beforeDatasetsUpdate', [me])) {
++ for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
++ me.getDatasetMeta(i).controller.update();
++ }
++
++ Chart.plugins.notify('afterDatasetsUpdate', [me]);
++ }
++ },
++
++ render: function(duration, lazy) {
++ var me = this;
++ Chart.plugins.notify('beforeRender', [me]);
++
++ var animationOptions = me.options.animation;
++ if (animationOptions && ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration === 'undefined' && animationOptions.duration !== 0))) {
++ var animation = new Chart.Animation();
++ animation.numSteps = (duration || animationOptions.duration) / 16.66; // 60 fps
++ animation.easing = animationOptions.easing;
++
++ // render function
++ animation.render = function(chartInstance, animationObject) {
++ var easingFunction = helpers.easingEffects[animationObject.easing];
++ var stepDecimal = animationObject.currentStep / animationObject.numSteps;
++ var easeDecimal = easingFunction(stepDecimal);
++
++ chartInstance.draw(easeDecimal, stepDecimal, animationObject.currentStep);
++ };
++
++ // user events
++ animation.onAnimationProgress = animationOptions.onProgress;
++ animation.onAnimationComplete = animationOptions.onComplete;
++
++ Chart.animationService.addAnimation(me, animation, duration, lazy);
++ } else {
++ me.draw();
++ if (animationOptions && animationOptions.onComplete && animationOptions.onComplete.call) {
++ animationOptions.onComplete.call(me);
++ }
++ }
++ return me;
++ },
++
++ draw: function(ease) {
++ var me = this;
++ var easingDecimal = ease || 1;
++ me.clear();
++
++ Chart.plugins.notify('beforeDraw', [me, easingDecimal]);
++
++ // Draw all the scales
++ helpers.each(me.boxes, function(box) {
++ box.draw(me.chartArea);
++ }, me);
++ if (me.scale) {
++ me.scale.draw();
++ }
++
++ Chart.plugins.notify('beforeDatasetsDraw', [me, easingDecimal]);
++
++ // Draw each dataset via its respective controller (reversed to support proper line stacking)
++ helpers.each(me.data.datasets, function(dataset, datasetIndex) {
++ if (me.isDatasetVisible(datasetIndex)) {
++ me.getDatasetMeta(datasetIndex).controller.draw(ease);
++ }
++ }, me, true);
++
++ Chart.plugins.notify('afterDatasetsDraw', [me, easingDecimal]);
++
++ // Finally draw the tooltip
++ me.tooltip.transition(easingDecimal).draw();
++
++ Chart.plugins.notify('afterDraw', [me, easingDecimal]);
++ },
++
++ // Get the single element that was clicked on
++ // @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw
++ getElementAtEvent: function(e) {
++ return Chart.Interaction.modes.single(this, e);
++ },
++
++ getElementsAtEvent: function(e) {
++ return Chart.Interaction.modes.label(this, e, {intersect: true});
++ },
++
++ getElementsAtXAxis: function(e) {
++ return Chart.Interaction.modes['x-axis'](this, e, {intersect: true});
++ },
++
++ getElementsAtEventForMode: function(e, mode, options) {
++ var method = Chart.Interaction.modes[mode];
++ if (typeof method === 'function') {
++ return method(this, e, options);
++ }
++
++ return [];
++ },
++
++ getDatasetAtEvent: function(e) {
++ return Chart.Interaction.modes.dataset(this, e);
++ },
++
++ getDatasetMeta: function(datasetIndex) {
++ var me = this;
++ var dataset = me.data.datasets[datasetIndex];
++ if (!dataset._meta) {
++ dataset._meta = {};
++ }
++
++ var meta = dataset._meta[me.id];
++ if (!meta) {
++ meta = dataset._meta[me.id] = {
++ type: null,
++ data: [],
++ dataset: null,
++ controller: null,
++ hidden: null, // See isDatasetVisible() comment
++ xAxisID: null,
++ yAxisID: null
++ };
++ }
++
++ return meta;
++ },
++
++ getVisibleDatasetCount: function() {
++ var count = 0;
++ for (var i = 0, ilen = this.data.datasets.length; i<ilen; ++i) {
++ if (this.isDatasetVisible(i)) {
++ count++;
++ }
++ }
++ return count;
++ },
++
++ isDatasetVisible: function(datasetIndex) {
++ var meta = this.getDatasetMeta(datasetIndex);
++
++ // meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false,
++ // the dataset.hidden value is ignored, else if null, the dataset hidden state is returned.
++ return typeof meta.hidden === 'boolean'? !meta.hidden : !this.data.datasets[datasetIndex].hidden;
++ },
++
++ generateLegend: function() {
++ return this.options.legendCallback(this);
++ },
++
++ destroy: function() {
++ var me = this;
++ var canvas = me.chart.canvas;
++ var meta, i, ilen;
++
++ me.stop();
++
++ // dataset controllers need to cleanup associated data
++ for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
++ meta = me.getDatasetMeta(i);
++ if (meta.controller) {
++ meta.controller.destroy();
++ meta.controller = null;
++ }
++ }
++
++ if (canvas) {
++ helpers.unbindEvents(me, me.events);
++ helpers.removeResizeListener(canvas.parentNode);
++ helpers.clear(me.chart);
++ releaseCanvas(canvas);
++ me.chart.canvas = null;
++ me.chart.ctx = null;
++ }
++
++ Chart.plugins.notify('destroy', [me]);
++
++ delete Chart.instances[me.id];
++ },
++
++ toBase64Image: function() {
++ return this.chart.canvas.toDataURL.apply(this.chart.canvas, arguments);
++ },
++
++ initToolTip: function() {
++ var me = this;
++ me.tooltip = new Chart.Tooltip({
++ _chart: me.chart,
++ _chartInstance: me,
++ _data: me.data,
++ _options: me.options.tooltips
++ }, me);
++ me.tooltip.initialize();
++ },
++
++ bindEvents: function() {
++ var me = this;
++ helpers.bindEvents(me, me.options.events, function(evt) {
++ me.eventHandler(evt);
++ });
++ },
++
++ updateHoverStyle: function(elements, mode, enabled) {
++ var method = enabled? 'setHoverStyle' : 'removeHoverStyle';
++ var element, i, ilen;
++
++ for (i=0, ilen=elements.length; i<ilen; ++i) {
++ element = elements[i];
++ if (element) {
++ this.getDatasetMeta(element._datasetIndex).controller[method](element);
++ }
++ }
++ },
++
++ eventHandler: function(e) {
++ var me = this;
++ var legend = me.legend;
++ var tooltip = me.tooltip;
++ var hoverOptions = me.options.hover;
++
++ // Buffer any update calls so that renders do not occur
++ me._bufferedRender = true;
++ me._bufferedRequest = null;
++
++ var changed = me.handleEvent(e);
++ changed |= legend && legend.handleEvent(e);
++ changed |= tooltip && tooltip.handleEvent(e);
++
++ var bufferedRequest = me._bufferedRequest;
++ if (bufferedRequest) {
++ // If we have an update that was triggered, we need to do a normal render
++ me.render(bufferedRequest.duration, bufferedRequest.lazy);
++ } else if (changed && !me.animating) {
++ // If entering, leaving, or changing elements, animate the change via pivot
++ me.stop();
++
++ // We only need to render at this point. Updating will cause scales to be
++ // recomputed generating flicker & using more memory than necessary.
++ me.render(hoverOptions.animationDuration, true);
++ }
++
++ me._bufferedRender = false;
++ me._bufferedRequest = null;
++
++ return me;
++ },
++
++ /**
++ * Handle an event
++ * @private
++ * param e {Event} the event to handle
++ * @return {Boolean} true if the chart needs to re-render
++ */
++ handleEvent: function(e) {
++ var me = this;
++ var options = me.options || {};
++ var hoverOptions = options.hover;
++ var changed = false;
++
++ me.lastActive = me.lastActive || [];
++
++ // Find Active Elements for hover and tooltips
++ if (e.type === 'mouseout') {
++ me.active = [];
++ } else {
++ me.active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions);
++ }
++
++ // On Hover hook
++ if (hoverOptions.onHover) {
++ hoverOptions.onHover.call(me, me.active);
++ }
++
++ if (e.type === 'mouseup' || e.type === 'click') {
++ if (options.onClick) {
++ options.onClick.call(me, e, me.active);
++ }
++ }
++
++ // Remove styling for last active (even if it may still be active)
++ if (me.lastActive.length) {
++ me.updateHoverStyle(me.lastActive, hoverOptions.mode, false);
++ }
++
++ // Built in hover styling
++ if (me.active.length && hoverOptions.mode) {
++ me.updateHoverStyle(me.active, hoverOptions.mode, true);
++ }
++
++ changed = !helpers.arrayEquals(me.active, me.lastActive);
++
++ // Remember Last Actives
++ me.lastActive = me.active;
++
++ return changed;
++ }
++ });
++};
++
++},{}],24:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++
++ var helpers = Chart.helpers;
++
++ var arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift'];
++
++ /**
++ * Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice',
++ * 'unshift') and notify the listener AFTER the array has been altered. Listeners are
++ * called on the 'onData*' callbacks (e.g. onDataPush, etc.) with same arguments.
++ */
++ function listenArrayEvents(array, listener) {
++ if (array._chartjs) {
++ array._chartjs.listeners.push(listener);
++ return;
++ }
++
++ Object.defineProperty(array, '_chartjs', {
++ configurable: true,
++ enumerable: false,
++ value: {
++ listeners: [listener]
++ }
++ });
++
++ arrayEvents.forEach(function(key) {
++ var method = 'onData' + key.charAt(0).toUpperCase() + key.slice(1);
++ var base = array[key];
++
++ Object.defineProperty(array, key, {
++ configurable: true,
++ enumerable: false,
++ value: function() {
++ var args = Array.prototype.slice.call(arguments);
++ var res = base.apply(this, args);
++
++ helpers.each(array._chartjs.listeners, function(object) {
++ if (typeof object[method] === 'function') {
++ object[method].apply(object, args);
++ }
++ });
++
++ return res;
++ }
++ });
++ });
++ }
++
++ /**
++ * Removes the given array event listener and cleanup extra attached properties (such as
++ * the _chartjs stub and overridden methods) if array doesn't have any more listeners.
++ */
++ function unlistenArrayEvents(array, listener) {
++ var stub = array._chartjs;
++ if (!stub) {
++ return;
++ }
++
++ var listeners = stub.listeners;
++ var index = listeners.indexOf(listener);
++ if (index !== -1) {
++ listeners.splice(index, 1);
++ }
++
++ if (listeners.length > 0) {
++ return;
++ }
++
++ arrayEvents.forEach(function(key) {
++ delete array[key];
++ });
++
++ delete array._chartjs;
++ }
++
++ // Base class for all dataset controllers (line, bar, etc)
++ Chart.DatasetController = function(chart, datasetIndex) {
++ this.initialize(chart, datasetIndex);
++ };
++
++ helpers.extend(Chart.DatasetController.prototype, {
++
++ /**
++ * Element type used to generate a meta dataset (e.g. Chart.element.Line).
++ * @type {Chart.core.element}
++ */
++ datasetElementType: null,
++
++ /**
++ * Element type used to generate a meta data (e.g. Chart.element.Point).
++ * @type {Chart.core.element}
++ */
++ dataElementType: null,
++
++ initialize: function(chart, datasetIndex) {
++ var me = this;
++ me.chart = chart;
++ me.index = datasetIndex;
++ me.linkScales();
++ me.addElements();
++ },
++
++ updateIndex: function(datasetIndex) {
++ this.index = datasetIndex;
++ },
++
++ linkScales: function() {
++ var me = this;
++ var meta = me.getMeta();
++ var dataset = me.getDataset();
++
++ if (meta.xAxisID === null) {
++ meta.xAxisID = dataset.xAxisID || me.chart.options.scales.xAxes[0].id;
++ }
++ if (meta.yAxisID === null) {
++ meta.yAxisID = dataset.yAxisID || me.chart.options.scales.yAxes[0].id;
++ }
++ },
++
++ getDataset: function() {
++ return this.chart.data.datasets[this.index];
++ },
++
++ getMeta: function() {
++ return this.chart.getDatasetMeta(this.index);
++ },
++
++ getScaleForId: function(scaleID) {
++ return this.chart.scales[scaleID];
++ },
++
++ reset: function() {
++ this.update(true);
++ },
++
++ /**
++ * @private
++ */
++ destroy: function() {
++ if (this._data) {
++ unlistenArrayEvents(this._data, this);
++ }
++ },
++
++ createMetaDataset: function() {
++ var me = this;
++ var type = me.datasetElementType;
++ return type && new type({
++ _chart: me.chart.chart,
++ _datasetIndex: me.index
++ });
++ },
++
++ createMetaData: function(index) {
++ var me = this;
++ var type = me.dataElementType;
++ return type && new type({
++ _chart: me.chart.chart,
++ _datasetIndex: me.index,
++ _index: index
++ });
++ },
++
++ addElements: function() {
++ var me = this;
++ var meta = me.getMeta();
++ var data = me.getDataset().data || [];
++ var metaData = meta.data;
++ var i, ilen;
++
++ for (i=0, ilen=data.length; i<ilen; ++i) {
++ metaData[i] = metaData[i] || me.createMetaData(i);
++ }
++
++ meta.dataset = meta.dataset || me.createMetaDataset();
++ },
++
++ addElementAndReset: function(index) {
++ var element = this.createMetaData(index);
++ this.getMeta().data.splice(index, 0, element);
++ this.updateElement(element, index, true);
++ },
++
++ buildOrUpdateElements: function() {
++ var me = this;
++ var dataset = me.getDataset();
++ var data = dataset.data || (dataset.data = []);
++
++ // In order to correctly handle data addition/deletion animation (an thus simulate
++ // real-time charts), we need to monitor these data modifications and synchronize
++ // the internal meta data accordingly.
++ if (me._data !== data) {
++ if (me._data) {
++ // This case happens when the user replaced the data array instance.
++ unlistenArrayEvents(me._data, me);
++ }
++
++ listenArrayEvents(data, me);
++ me._data = data;
++ }
++
++ // Re-sync meta data in case the user replaced the data array or if we missed
++ // any updates and so make sure that we handle number of datapoints changing.
++ me.resyncElements();
++ },
++
++ update: helpers.noop,
++
++ draw: function(ease) {
++ var easingDecimal = ease || 1;
++ var i, len;
++ var metaData = this.getMeta().data;
++ for (i = 0, len = metaData.length; i < len; ++i) {
++ metaData[i].transition(easingDecimal).draw();
++ }
++ },
++
++ removeHoverStyle: function(element, elementOpts) {
++ var dataset = this.chart.data.datasets[element._datasetIndex],
++ index = element._index,
++ custom = element.custom || {},
++ valueOrDefault = helpers.getValueAtIndexOrDefault,
++ model = element._model;
++
++ model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor);
++ model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor);
++ model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth);
++ },
++
++ setHoverStyle: function(element) {
++ var dataset = this.chart.data.datasets[element._datasetIndex],
++ index = element._index,
++ custom = element.custom || {},
++ valueOrDefault = helpers.getValueAtIndexOrDefault,
++ getHoverColor = helpers.getHoverColor,
++ model = element._model;
++
++ model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : valueOrDefault(dataset.hoverBackgroundColor, index, getHoverColor(model.backgroundColor));
++ model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : valueOrDefault(dataset.hoverBorderColor, index, getHoverColor(model.borderColor));
++ model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : valueOrDefault(dataset.hoverBorderWidth, index, model.borderWidth);
++ },
++
++ /**
++ * @private
++ */
++ resyncElements: function() {
++ var me = this;
++ var meta = me.getMeta();
++ var data = me.getDataset().data;
++ var numMeta = meta.data.length;
++ var numData = data.length;
++
++ if (numData < numMeta) {
++ meta.data.splice(numData, numMeta - numData);
++ } else if (numData > numMeta) {
++ me.insertElements(numMeta, numData - numMeta);
++ }
++ },
++
++ /**
++ * @private
++ */
++ insertElements: function(start, count) {
++ for (var i=0; i<count; ++i) {
++ this.addElementAndReset(start + i);
++ }
++ },
++
++ /**
++ * @private
++ */
++ onDataPush: function() {
++ this.insertElements(this.getDataset().data.length-1, arguments.length);
++ },
++
++ /**
++ * @private
++ */
++ onDataPop: function() {
++ this.getMeta().data.pop();
++ },
++
++ /**
++ * @private
++ */
++ onDataShift: function() {
++ this.getMeta().data.shift();
++ },
++
++ /**
++ * @private
++ */
++ onDataSplice: function(start, count) {
++ this.getMeta().data.splice(start, count);
++ this.insertElements(start, arguments.length - 2);
++ },
++
++ /**
++ * @private
++ */
++ onDataUnshift: function() {
++ this.insertElements(0, arguments.length);
++ }
++ });
++
++ Chart.DatasetController.extend = helpers.inherits;
++};
++
++},{}],25:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++
++ var helpers = Chart.helpers;
++
++ Chart.elements = {};
++
++ Chart.Element = function(configuration) {
++ helpers.extend(this, configuration);
++ this.initialize.apply(this, arguments);
++ };
++
++ helpers.extend(Chart.Element.prototype, {
++
++ initialize: function() {
++ this.hidden = false;
++ },
++
++ pivot: function() {
++ var me = this;
++ if (!me._view) {
++ me._view = helpers.clone(me._model);
++ }
++ me._start = helpers.clone(me._view);
++ return me;
++ },
++
++ transition: function(ease) {
++ var me = this;
++
++ if (!me._view) {
++ me._view = helpers.clone(me._model);
++ }
++
++ // No animation -> No Transition
++ if (ease === 1) {
++ me._view = me._model;
++ me._start = null;
++ return me;
++ }
++
++ if (!me._start) {
++ me.pivot();
++ }
++
++ helpers.each(me._model, function(value, key) {
++
++ if (key[0] === '_') {
++ // Only non-underscored properties
++ // Init if doesn't exist
++ } else if (!me._view.hasOwnProperty(key)) {
++ if (typeof value === 'number' && !isNaN(me._view[key])) {
++ me._view[key] = value * ease;
++ } else {
++ me._view[key] = value;
++ }
++ // No unnecessary computations
++ } else if (value === me._view[key]) {
++ // It's the same! Woohoo!
++ // Color transitions if possible
++ } else if (typeof value === 'string') {
++ try {
++ var color = helpers.color(me._model[key]).mix(helpers.color(me._start[key]), ease);
++ me._view[key] = color.rgbString();
++ } catch (err) {
++ me._view[key] = value;
++ }
++ // Number transitions
++ } else if (typeof value === 'number') {
++ var startVal = me._start[key] !== undefined && isNaN(me._start[key]) === false ? me._start[key] : 0;
++ me._view[key] = ((me._model[key] - startVal) * ease) + startVal;
++ // Everything else
++ } else {
++ me._view[key] = value;
++ }
++ }, me);
++
++ return me;
++ },
++
++ tooltipPosition: function() {
++ return {
++ x: this._model.x,
++ y: this._model.y
++ };
++ },
++
++ hasValue: function() {
++ return helpers.isNumber(this._model.x) && helpers.isNumber(this._model.y);
++ }
++ });
++
++ Chart.Element.extend = helpers.inherits;
++
++};
++
++},{}],26:[function(require,module,exports){
++/* global window: false */
++/* global document: false */
++'use strict';
++
++var color = require(3);
++
++module.exports = function(Chart) {
++ // Global Chart helpers object for utility methods and classes
++ var helpers = Chart.helpers = {};
++
++ // -- Basic js utility methods
++ helpers.each = function(loopable, callback, self, reverse) {
++ // Check to see if null or undefined firstly.
++ var i, len;
++ if (helpers.isArray(loopable)) {
++ len = loopable.length;
++ if (reverse) {
++ for (i = len - 1; i >= 0; i--) {
++ callback.call(self, loopable[i], i);
++ }
++ } else {
++ for (i = 0; i < len; i++) {
++ callback.call(self, loopable[i], i);
++ }
++ }
++ } else if (typeof loopable === 'object') {
++ var keys = Object.keys(loopable);
++ len = keys.length;
++ for (i = 0; i < len; i++) {
++ callback.call(self, loopable[keys[i]], keys[i]);
++ }
++ }
++ };
++ helpers.clone = function(obj) {
++ var objClone = {};
++ helpers.each(obj, function(value, key) {
++ if (helpers.isArray(value)) {
++ objClone[key] = value.slice(0);
++ } else if (typeof value === 'object' && value !== null) {
++ objClone[key] = helpers.clone(value);
++ } else {
++ objClone[key] = value;
++ }
++ });
++ return objClone;
++ };
++ helpers.extend = function(base) {
++ var setFn = function(value, key) {
++ base[key] = value;
++ };
++ for (var i = 1, ilen = arguments.length; i < ilen; i++) {
++ helpers.each(arguments[i], setFn);
++ }
++ return base;
++ };
++ // Need a special merge function to chart configs since they are now grouped
++ helpers.configMerge = function(_base) {
++ var base = helpers.clone(_base);
++ helpers.each(Array.prototype.slice.call(arguments, 1), function(extension) {
++ helpers.each(extension, function(value, key) {
++ var baseHasProperty = base.hasOwnProperty(key);
++ var baseVal = baseHasProperty ? base[key] : {};
++
++ if (key === 'scales') {
++ // Scale config merging is complex. Add our own function here for that
++ base[key] = helpers.scaleMerge(baseVal, value);
++ } else if (key === 'scale') {
++ // Used in polar area & radar charts since there is only one scale
++ base[key] = helpers.configMerge(baseVal, Chart.scaleService.getScaleDefaults(value.type), value);
++ } else if (baseHasProperty
++ && typeof baseVal === 'object'
++ && !helpers.isArray(baseVal)
++ && baseVal !== null
++ && typeof value === 'object'
++ && !helpers.isArray(value)) {
++ // If we are overwriting an object with an object, do a merge of the properties.
++ base[key] = helpers.configMerge(baseVal, value);
++ } else {
++ // can just overwrite the value in this case
++ base[key] = value;
++ }
++ });
++ });
++
++ return base;
++ };
++ helpers.scaleMerge = function(_base, extension) {
++ var base = helpers.clone(_base);
++
++ helpers.each(extension, function(value, key) {
++ if (key === 'xAxes' || key === 'yAxes') {
++ // These properties are arrays of items
++ if (base.hasOwnProperty(key)) {
++ helpers.each(value, function(valueObj, index) {
++ var axisType = helpers.getValueOrDefault(valueObj.type, key === 'xAxes' ? 'category' : 'linear');
++ var axisDefaults = Chart.scaleService.getScaleDefaults(axisType);
++ if (index >= base[key].length || !base[key][index].type) {
++ base[key].push(helpers.configMerge(axisDefaults, valueObj));
++ } else if (valueObj.type && valueObj.type !== base[key][index].type) {
++ // Type changed. Bring in the new defaults before we bring in valueObj so that valueObj can override the correct scale defaults
++ base[key][index] = helpers.configMerge(base[key][index], axisDefaults, valueObj);
++ } else {
++ // Type is the same
++ base[key][index] = helpers.configMerge(base[key][index], valueObj);
++ }
++ });
++ } else {
++ base[key] = [];
++ helpers.each(value, function(valueObj) {
++ var axisType = helpers.getValueOrDefault(valueObj.type, key === 'xAxes' ? 'category' : 'linear');
++ base[key].push(helpers.configMerge(Chart.scaleService.getScaleDefaults(axisType), valueObj));
++ });
++ }
++ } else if (base.hasOwnProperty(key) && typeof base[key] === 'object' && base[key] !== null && typeof value === 'object') {
++ // If we are overwriting an object with an object, do a merge of the properties.
++ base[key] = helpers.configMerge(base[key], value);
++
++ } else {
++ // can just overwrite the value in this case
++ base[key] = value;
++ }
++ });
++
++ return base;
++ };
++ helpers.getValueAtIndexOrDefault = function(value, index, defaultValue) {
++ if (value === undefined || value === null) {
++ return defaultValue;
++ }
++
++ if (helpers.isArray(value)) {
++ return index < value.length ? value[index] : defaultValue;
++ }
++
++ return value;
++ };
++ helpers.getValueOrDefault = function(value, defaultValue) {
++ return value === undefined ? defaultValue : value;
++ };
++ helpers.indexOf = Array.prototype.indexOf?
++ function(array, item) {
++ return array.indexOf(item);
++ }:
++ function(array, item) {
++ for (var i = 0, ilen = array.length; i < ilen; ++i) {
++ if (array[i] === item) {
++ return i;
++ }
++ }
++ return -1;
++ };
++ helpers.where = function(collection, filterCallback) {
++ if (helpers.isArray(collection) && Array.prototype.filter) {
++ return collection.filter(filterCallback);
++ }
++ var filtered = [];
++
++ helpers.each(collection, function(item) {
++ if (filterCallback(item)) {
++ filtered.push(item);
++ }
++ });
++
++ return filtered;
++ };
++ helpers.findIndex = Array.prototype.findIndex?
++ function(array, callback, scope) {
++ return array.findIndex(callback, scope);
++ } :
++ function(array, callback, scope) {
++ scope = scope === undefined? array : scope;
++ for (var i = 0, ilen = array.length; i < ilen; ++i) {
++ if (callback.call(scope, array[i], i, array)) {
++ return i;
++ }
++ }
++ return -1;
++ };
++ helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex) {
++ // Default to start of the array
++ if (startIndex === undefined || startIndex === null) {
++ startIndex = -1;
++ }
++ for (var i = startIndex + 1; i < arrayToSearch.length; i++) {
++ var currentItem = arrayToSearch[i];
++ if (filterCallback(currentItem)) {
++ return currentItem;
++ }
++ }
++ };
++ helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) {
++ // Default to end of the array
++ if (startIndex === undefined || startIndex === null) {
++ startIndex = arrayToSearch.length;
++ }
++ for (var i = startIndex - 1; i >= 0; i--) {
++ var currentItem = arrayToSearch[i];
++ if (filterCallback(currentItem)) {
++ return currentItem;
++ }
++ }
++ };
++ helpers.inherits = function(extensions) {
++ // Basic javascript inheritance based on the model created in Backbone.js
++ var me = this;
++ var ChartElement = (extensions && extensions.hasOwnProperty('constructor')) ? extensions.constructor : function() {
++ return me.apply(this, arguments);
++ };
++
++ var Surrogate = function() {
++ this.constructor = ChartElement;
++ };
++ Surrogate.prototype = me.prototype;
++ ChartElement.prototype = new Surrogate();
++
++ ChartElement.extend = helpers.inherits;
++
++ if (extensions) {
++ helpers.extend(ChartElement.prototype, extensions);
++ }
++
++ ChartElement.__super__ = me.prototype;
++
++ return ChartElement;
++ };
++ helpers.noop = function() {};
++ helpers.uid = (function() {
++ var id = 0;
++ return function() {
++ return id++;
++ };
++ }());
++ // -- Math methods
++ helpers.isNumber = function(n) {
++ return !isNaN(parseFloat(n)) && isFinite(n);
++ };
++ helpers.almostEquals = function(x, y, epsilon) {
++ return Math.abs(x - y) < epsilon;
++ };
++ helpers.max = function(array) {
++ return array.reduce(function(max, value) {
++ if (!isNaN(value)) {
++ return Math.max(max, value);
++ }
++ return max;
++ }, Number.NEGATIVE_INFINITY);
++ };
++ helpers.min = function(array) {
++ return array.reduce(function(min, value) {
++ if (!isNaN(value)) {
++ return Math.min(min, value);
++ }
++ return min;
++ }, Number.POSITIVE_INFINITY);
++ };
++ helpers.sign = Math.sign?
++ function(x) {
++ return Math.sign(x);
++ } :
++ function(x) {
++ x = +x; // convert to a number
++ if (x === 0 || isNaN(x)) {
++ return x;
++ }
++ return x > 0 ? 1 : -1;
++ };
++ helpers.log10 = Math.log10?
++ function(x) {
++ return Math.log10(x);
++ } :
++ function(x) {
++ return Math.log(x) / Math.LN10;
++ };
++ helpers.toRadians = function(degrees) {
++ return degrees * (Math.PI / 180);
++ };
++ helpers.toDegrees = function(radians) {
++ return radians * (180 / Math.PI);
++ };
++ // Gets the angle from vertical upright to the point about a centre.
++ helpers.getAngleFromPoint = function(centrePoint, anglePoint) {
++ var distanceFromXCenter = anglePoint.x - centrePoint.x,
++ distanceFromYCenter = anglePoint.y - centrePoint.y,
++ radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);
++
++ var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter);
++
++ if (angle < (-0.5 * Math.PI)) {
++ angle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2]
++ }
++
++ return {
++ angle: angle,
++ distance: radialDistanceFromCenter
++ };
++ };
++ helpers.distanceBetweenPoints = function(pt1, pt2) {
++ return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2));
++ };
++ helpers.aliasPixel = function(pixelWidth) {
++ return (pixelWidth % 2 === 0) ? 0 : 0.5;
++ };
++ helpers.splineCurve = function(firstPoint, middlePoint, afterPoint, t) {
++ // Props to Rob Spencer at scaled innovation for his post on splining between points
++ // http://scaledinnovation.com/analytics/splines/aboutSplines.html
++
++ // This function must also respect "skipped" points
++
++ var previous = firstPoint.skip ? middlePoint : firstPoint,
++ current = middlePoint,
++ next = afterPoint.skip ? middlePoint : afterPoint;
++
++ var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2));
++ var d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2));
++
++ var s01 = d01 / (d01 + d12);
++ var s12 = d12 / (d01 + d12);
++
++ // If all points are the same, s01 & s02 will be inf
++ s01 = isNaN(s01) ? 0 : s01;
++ s12 = isNaN(s12) ? 0 : s12;
++
++ var fa = t * s01; // scaling factor for triangle Ta
++ var fb = t * s12;
++
++ return {
++ previous: {
++ x: current.x - fa * (next.x - previous.x),
++ y: current.y - fa * (next.y - previous.y)
++ },
++ next: {
++ x: current.x + fb * (next.x - previous.x),
++ y: current.y + fb * (next.y - previous.y)
++ }
++ };
++ };
++ helpers.EPSILON = Number.EPSILON || 1e-14;
++ helpers.splineCurveMonotone = function(points) {
++ // This function calculates Bézier control points in a similar way than |splineCurve|,
++ // but preserves monotonicity of the provided data and ensures no local extremums are added
++ // between the dataset discrete points due to the interpolation.
++ // See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation
++
++ var pointsWithTangents = (points || []).map(function(point) {
++ return {
++ model: point._model,
++ deltaK: 0,
++ mK: 0
++ };
++ });
++
++ // Calculate slopes (deltaK) and initialize tangents (mK)
++ var pointsLen = pointsWithTangents.length;
++ var i, pointBefore, pointCurrent, pointAfter;
++ for (i = 0; i < pointsLen; ++i) {
++ pointCurrent = pointsWithTangents[i];
++ if (pointCurrent.model.skip) {
++ continue;
++ }
++
++ pointBefore = i > 0 ? pointsWithTangents[i - 1] : null;
++ pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null;
++ if (pointAfter && !pointAfter.model.skip) {
++ pointCurrent.deltaK = (pointAfter.model.y - pointCurrent.model.y) / (pointAfter.model.x - pointCurrent.model.x);
++ }
++
++ if (!pointBefore || pointBefore.model.skip) {
++ pointCurrent.mK = pointCurrent.deltaK;
++ } else if (!pointAfter || pointAfter.model.skip) {
++ pointCurrent.mK = pointBefore.deltaK;
++ } else if (this.sign(pointBefore.deltaK) !== this.sign(pointCurrent.deltaK)) {
++ pointCurrent.mK = 0;
++ } else {
++ pointCurrent.mK = (pointBefore.deltaK + pointCurrent.deltaK) / 2;
++ }
++ }
++
++ // Adjust tangents to ensure monotonic properties
++ var alphaK, betaK, tauK, squaredMagnitude;
++ for (i = 0; i < pointsLen - 1; ++i) {
++ pointCurrent = pointsWithTangents[i];
++ pointAfter = pointsWithTangents[i + 1];
++ if (pointCurrent.model.skip || pointAfter.model.skip) {
++ continue;
++ }
++
++ if (helpers.almostEquals(pointCurrent.deltaK, 0, this.EPSILON)) {
++ pointCurrent.mK = pointAfter.mK = 0;
++ continue;
++ }
++
++ alphaK = pointCurrent.mK / pointCurrent.deltaK;
++ betaK = pointAfter.mK / pointCurrent.deltaK;
++ squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2);
++ if (squaredMagnitude <= 9) {
++ continue;
++ }
++
++ tauK = 3 / Math.sqrt(squaredMagnitude);
++ pointCurrent.mK = alphaK * tauK * pointCurrent.deltaK;
++ pointAfter.mK = betaK * tauK * pointCurrent.deltaK;
++ }
++
++ // Compute control points
++ var deltaX;
++ for (i = 0; i < pointsLen; ++i) {
++ pointCurrent = pointsWithTangents[i];
++ if (pointCurrent.model.skip) {
++ continue;
++ }
++
++ pointBefore = i > 0 ? pointsWithTangents[i - 1] : null;
++ pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null;
++ if (pointBefore && !pointBefore.model.skip) {
++ deltaX = (pointCurrent.model.x - pointBefore.model.x) / 3;
++ pointCurrent.model.controlPointPreviousX = pointCurrent.model.x - deltaX;
++ pointCurrent.model.controlPointPreviousY = pointCurrent.model.y - deltaX * pointCurrent.mK;
++ }
++ if (pointAfter && !pointAfter.model.skip) {
++ deltaX = (pointAfter.model.x - pointCurrent.model.x) / 3;
++ pointCurrent.model.controlPointNextX = pointCurrent.model.x + deltaX;
++ pointCurrent.model.controlPointNextY = pointCurrent.model.y + deltaX * pointCurrent.mK;
++ }
++ }
++ };
++ helpers.nextItem = function(collection, index, loop) {
++ if (loop) {
++ return index >= collection.length - 1 ? collection[0] : collection[index + 1];
++ }
++ return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1];
++ };
++ helpers.previousItem = function(collection, index, loop) {
++ if (loop) {
++ return index <= 0 ? collection[collection.length - 1] : collection[index - 1];
++ }
++ return index <= 0 ? collection[0] : collection[index - 1];
++ };
++ // Implementation of the nice number algorithm used in determining where axis labels will go
++ helpers.niceNum = function(range, round) {
++ var exponent = Math.floor(helpers.log10(range));
++ var fraction = range / Math.pow(10, exponent);
++ var niceFraction;
++
++ if (round) {
++ if (fraction < 1.5) {
++ niceFraction = 1;
++ } else if (fraction < 3) {
++ niceFraction = 2;
++ } else if (fraction < 7) {
++ niceFraction = 5;
++ } else {
++ niceFraction = 10;
++ }
++ } else if (fraction <= 1.0) {
++ niceFraction = 1;
++ } else if (fraction <= 2) {
++ niceFraction = 2;
++ } else if (fraction <= 5) {
++ niceFraction = 5;
++ } else {
++ niceFraction = 10;
++ }
++
++ return niceFraction * Math.pow(10, exponent);
++ };
++ // Easing functions adapted from Robert Penner's easing equations
++ // http://www.robertpenner.com/easing/
++ var easingEffects = helpers.easingEffects = {
++ linear: function(t) {
++ return t;
++ },
++ easeInQuad: function(t) {
++ return t * t;
++ },
++ easeOutQuad: function(t) {
++ return -1 * t * (t - 2);
++ },
++ easeInOutQuad: function(t) {
++ if ((t /= 1 / 2) < 1) {
++ return 1 / 2 * t * t;
++ }
++ return -1 / 2 * ((--t) * (t - 2) - 1);
++ },
++ easeInCubic: function(t) {
++ return t * t * t;
++ },
++ easeOutCubic: function(t) {
++ return 1 * ((t = t / 1 - 1) * t * t + 1);
++ },
++ easeInOutCubic: function(t) {
++ if ((t /= 1 / 2) < 1) {
++ return 1 / 2 * t * t * t;
++ }
++ return 1 / 2 * ((t -= 2) * t * t + 2);
++ },
++ easeInQuart: function(t) {
++ return t * t * t * t;
++ },
++ easeOutQuart: function(t) {
++ return -1 * ((t = t / 1 - 1) * t * t * t - 1);
++ },
++ easeInOutQuart: function(t) {
++ if ((t /= 1 / 2) < 1) {
++ return 1 / 2 * t * t * t * t;
++ }
++ return -1 / 2 * ((t -= 2) * t * t * t - 2);
++ },
++ easeInQuint: function(t) {
++ return 1 * (t /= 1) * t * t * t * t;
++ },
++ easeOutQuint: function(t) {
++ return 1 * ((t = t / 1 - 1) * t * t * t * t + 1);
++ },
++ easeInOutQuint: function(t) {
++ if ((t /= 1 / 2) < 1) {
++ return 1 / 2 * t * t * t * t * t;
++ }
++ return 1 / 2 * ((t -= 2) * t * t * t * t + 2);
++ },
++ easeInSine: function(t) {
++ return -1 * Math.cos(t / 1 * (Math.PI / 2)) + 1;
++ },
++ easeOutSine: function(t) {
++ return 1 * Math.sin(t / 1 * (Math.PI / 2));
++ },
++ easeInOutSine: function(t) {
++ return -1 / 2 * (Math.cos(Math.PI * t / 1) - 1);
++ },
++ easeInExpo: function(t) {
++ return (t === 0) ? 1 : 1 * Math.pow(2, 10 * (t / 1 - 1));
++ },
++ easeOutExpo: function(t) {
++ return (t === 1) ? 1 : 1 * (-Math.pow(2, -10 * t / 1) + 1);
++ },
++ easeInOutExpo: function(t) {
++ if (t === 0) {
++ return 0;
++ }
++ if (t === 1) {
++ return 1;
++ }
++ if ((t /= 1 / 2) < 1) {
++ return 1 / 2 * Math.pow(2, 10 * (t - 1));
++ }
++ return 1 / 2 * (-Math.pow(2, -10 * --t) + 2);
++ },
++ easeInCirc: function(t) {
++ if (t >= 1) {
++ return t;
++ }
++ return -1 * (Math.sqrt(1 - (t /= 1) * t) - 1);
++ },
++ easeOutCirc: function(t) {
++ return 1 * Math.sqrt(1 - (t = t / 1 - 1) * t);
++ },
++ easeInOutCirc: function(t) {
++ if ((t /= 1 / 2) < 1) {
++ return -1 / 2 * (Math.sqrt(1 - t * t) - 1);
++ }
++ return 1 / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1);
++ },
++ easeInElastic: function(t) {
++ var s = 1.70158;
++ var p = 0;
++ var a = 1;
++ if (t === 0) {
++ return 0;
++ }
++ if ((t /= 1) === 1) {
++ return 1;
++ }
++ if (!p) {
++ p = 1 * 0.3;
++ }
++ if (a < Math.abs(1)) {
++ a = 1;
++ s = p / 4;
++ } else {
++ s = p / (2 * Math.PI) * Math.asin(1 / a);
++ }
++ return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p));
++ },
++ easeOutElastic: function(t) {
++ var s = 1.70158;
++ var p = 0;
++ var a = 1;
++ if (t === 0) {
++ return 0;
++ }
++ if ((t /= 1) === 1) {
++ return 1;
++ }
++ if (!p) {
++ p = 1 * 0.3;
++ }
++ if (a < Math.abs(1)) {
++ a = 1;
++ s = p / 4;
++ } else {
++ s = p / (2 * Math.PI) * Math.asin(1 / a);
++ }
++ return a * Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) + 1;
++ },
++ easeInOutElastic: function(t) {
++ var s = 1.70158;
++ var p = 0;
++ var a = 1;
++ if (t === 0) {
++ return 0;
++ }
++ if ((t /= 1 / 2) === 2) {
++ return 1;
++ }
++ if (!p) {
++ p = 1 * (0.3 * 1.5);
++ }
++ if (a < Math.abs(1)) {
++ a = 1;
++ s = p / 4;
++ } else {
++ s = p / (2 * Math.PI) * Math.asin(1 / a);
++ }
++ if (t < 1) {
++ return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p));
++ }
++ return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) * 0.5 + 1;
++ },
++ easeInBack: function(t) {
++ var s = 1.70158;
++ return 1 * (t /= 1) * t * ((s + 1) * t - s);
++ },
++ easeOutBack: function(t) {
++ var s = 1.70158;
++ return 1 * ((t = t / 1 - 1) * t * ((s + 1) * t + s) + 1);
++ },
++ easeInOutBack: function(t) {
++ var s = 1.70158;
++ if ((t /= 1 / 2) < 1) {
++ return 1 / 2 * (t * t * (((s *= (1.525)) + 1) * t - s));
++ }
++ return 1 / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2);
++ },
++ easeInBounce: function(t) {
++ return 1 - easingEffects.easeOutBounce(1 - t);
++ },
++ easeOutBounce: function(t) {
++ if ((t /= 1) < (1 / 2.75)) {
++ return 1 * (7.5625 * t * t);
++ } else if (t < (2 / 2.75)) {
++ return 1 * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75);
++ } else if (t < (2.5 / 2.75)) {
++ return 1 * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375);
++ }
++ return 1 * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375);
++ },
++ easeInOutBounce: function(t) {
++ if (t < 1 / 2) {
++ return easingEffects.easeInBounce(t * 2) * 0.5;
++ }
++ return easingEffects.easeOutBounce(t * 2 - 1) * 0.5 + 1 * 0.5;
++ }
++ };
++ // Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
++ helpers.requestAnimFrame = (function() {
++ return window.requestAnimationFrame ||
++ window.webkitRequestAnimationFrame ||
++ window.mozRequestAnimationFrame ||
++ window.oRequestAnimationFrame ||
++ window.msRequestAnimationFrame ||
++ function(callback) {
++ return window.setTimeout(callback, 1000 / 60);
++ };
++ }());
++ helpers.cancelAnimFrame = (function() {
++ return window.cancelAnimationFrame ||
++ window.webkitCancelAnimationFrame ||
++ window.mozCancelAnimationFrame ||
++ window.oCancelAnimationFrame ||
++ window.msCancelAnimationFrame ||
++ function(callback) {
++ return window.clearTimeout(callback, 1000 / 60);
++ };
++ }());
++ // -- DOM methods
++ helpers.getRelativePosition = function(evt, chart) {
++ var mouseX, mouseY;
++ var e = evt.originalEvent || evt,
++ canvas = evt.currentTarget || evt.srcElement,
++ boundingRect = canvas.getBoundingClientRect();
++
++ var touches = e.touches;
++ if (touches && touches.length > 0) {
++ mouseX = touches[0].clientX;
++ mouseY = touches[0].clientY;
++
++ } else {
++ mouseX = e.clientX;
++ mouseY = e.clientY;
++ }
++
++ // Scale mouse coordinates into canvas coordinates
++ // by following the pattern laid out by 'jerryj' in the comments of
++ // http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/
++ var paddingLeft = parseFloat(helpers.getStyle(canvas, 'padding-left'));
++ var paddingTop = parseFloat(helpers.getStyle(canvas, 'padding-top'));
++ var paddingRight = parseFloat(helpers.getStyle(canvas, 'padding-right'));
++ var paddingBottom = parseFloat(helpers.getStyle(canvas, 'padding-bottom'));
++ var width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight;
++ var height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom;
++
++ // We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However
++ // the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here
++ mouseX = Math.round((mouseX - boundingRect.left - paddingLeft) / (width) * canvas.width / chart.currentDevicePixelRatio);
++ mouseY = Math.round((mouseY - boundingRect.top - paddingTop) / (height) * canvas.height / chart.currentDevicePixelRatio);
++
++ return {
++ x: mouseX,
++ y: mouseY
++ };
++
++ };
++ helpers.addEvent = function(node, eventType, method) {
++ if (node.addEventListener) {
++ node.addEventListener(eventType, method);
++ } else if (node.attachEvent) {
++ node.attachEvent('on' + eventType, method);
++ } else {
++ node['on' + eventType] = method;
++ }
++ };
++ helpers.removeEvent = function(node, eventType, handler) {
++ if (node.removeEventListener) {
++ node.removeEventListener(eventType, handler, false);
++ } else if (node.detachEvent) {
++ node.detachEvent('on' + eventType, handler);
++ } else {
++ node['on' + eventType] = helpers.noop;
++ }
++ };
++ helpers.bindEvents = function(chartInstance, arrayOfEvents, handler) {
++ // Create the events object if it's not already present
++ var events = chartInstance.events = chartInstance.events || {};
++
++ helpers.each(arrayOfEvents, function(eventName) {
++ events[eventName] = function() {
++ handler.apply(chartInstance, arguments);
++ };
++ helpers.addEvent(chartInstance.chart.canvas, eventName, events[eventName]);
++ });
++ };
++ helpers.unbindEvents = function(chartInstance, arrayOfEvents) {
++ var canvas = chartInstance.chart.canvas;
++ helpers.each(arrayOfEvents, function(handler, eventName) {
++ helpers.removeEvent(canvas, eventName, handler);
++ });
++ };
++
++ // Private helper function to convert max-width/max-height values that may be percentages into a number
++ function parseMaxStyle(styleValue, node, parentProperty) {
++ var valueInPixels;
++ if (typeof(styleValue) === 'string') {
++ valueInPixels = parseInt(styleValue, 10);
++
++ if (styleValue.indexOf('%') !== -1) {
++ // percentage * size in dimension
++ valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty];
++ }
++ } else {
++ valueInPixels = styleValue;
++ }
++
++ return valueInPixels;
++ }
++
++ /**
++ * Returns if the given value contains an effective constraint.
++ * @private
++ */
++ function isConstrainedValue(value) {
++ return value !== undefined && value !== null && value !== 'none';
++ }
++
++ // Private helper to get a constraint dimension
++ // @param domNode : the node to check the constraint on
++ // @param maxStyle : the style that defines the maximum for the direction we are using (maxWidth / maxHeight)
++ // @param percentageProperty : property of parent to use when calculating width as a percentage
++ // @see http://www.nathanaeljones.com/blog/2013/reading-max-width-cross-browser
++ function getConstraintDimension(domNode, maxStyle, percentageProperty) {
++ var view = document.defaultView;
++ var parentNode = domNode.parentNode;
++ var constrainedNode = view.getComputedStyle(domNode)[maxStyle];
++ var constrainedContainer = view.getComputedStyle(parentNode)[maxStyle];
++ var hasCNode = isConstrainedValue(constrainedNode);
++ var hasCContainer = isConstrainedValue(constrainedContainer);
++ var infinity = Number.POSITIVE_INFINITY;
++
++ if (hasCNode || hasCContainer) {
++ return Math.min(
++ hasCNode? parseMaxStyle(constrainedNode, domNode, percentageProperty) : infinity,
++ hasCContainer? parseMaxStyle(constrainedContainer, parentNode, percentageProperty) : infinity);
++ }
++
++ return 'none';
++ }
++ // returns Number or undefined if no constraint
++ helpers.getConstraintWidth = function(domNode) {
++ return getConstraintDimension(domNode, 'max-width', 'clientWidth');
++ };
++ // returns Number or undefined if no constraint
++ helpers.getConstraintHeight = function(domNode) {
++ return getConstraintDimension(domNode, 'max-height', 'clientHeight');
++ };
++ helpers.getMaximumWidth = function(domNode) {
++ var container = domNode.parentNode;
++ var paddingLeft = parseInt(helpers.getStyle(container, 'padding-left'), 10);
++ var paddingRight = parseInt(helpers.getStyle(container, 'padding-right'), 10);
++ var w = container.clientWidth - paddingLeft - paddingRight;
++ var cw = helpers.getConstraintWidth(domNode);
++ return isNaN(cw)? w : Math.min(w, cw);
++ };
++ helpers.getMaximumHeight = function(domNode) {
++ var container = domNode.parentNode;
++ var paddingTop = parseInt(helpers.getStyle(container, 'padding-top'), 10);
++ var paddingBottom = parseInt(helpers.getStyle(container, 'padding-bottom'), 10);
++ var h = container.clientHeight - paddingTop - paddingBottom;
++ var ch = helpers.getConstraintHeight(domNode);
++ return isNaN(ch)? h : Math.min(h, ch);
++ };
++ helpers.getStyle = function(el, property) {
++ return el.currentStyle ?
++ el.currentStyle[property] :
++ document.defaultView.getComputedStyle(el, null).getPropertyValue(property);
++ };
++ helpers.retinaScale = function(chart) {
++ var pixelRatio = chart.currentDevicePixelRatio = window.devicePixelRatio || 1;
++ if (pixelRatio === 1) {
++ return;
++ }
++
++ var canvas = chart.canvas;
++ var height = chart.height;
++ var width = chart.width;
++
++ canvas.height = height * pixelRatio;
++ canvas.width = width * pixelRatio;
++ chart.ctx.scale(pixelRatio, pixelRatio);
++
++ // If no style has been set on the canvas, the render size is used as display size,
++ // making the chart visually bigger, so let's enforce it to the "correct" values.
++ // See https://github.com/chartjs/Chart.js/issues/3575
++ canvas.style.height = height + 'px';
++ canvas.style.width = width + 'px';
++ };
++ // -- Canvas methods
++ helpers.clear = function(chart) {
++ chart.ctx.clearRect(0, 0, chart.width, chart.height);
++ };
++ helpers.fontString = function(pixelSize, fontStyle, fontFamily) {
++ return fontStyle + ' ' + pixelSize + 'px ' + fontFamily;
++ };
++ helpers.longestText = function(ctx, font, arrayOfThings, cache) {
++ cache = cache || {};
++ var data = cache.data = cache.data || {};
++ var gc = cache.garbageCollect = cache.garbageCollect || [];
++
++ if (cache.font !== font) {
++ data = cache.data = {};
++ gc = cache.garbageCollect = [];
++ cache.font = font;
++ }
++
++ ctx.font = font;
++ var longest = 0;
++ helpers.each(arrayOfThings, function(thing) {
++ // Undefined strings and arrays should not be measured
++ if (thing !== undefined && thing !== null && helpers.isArray(thing) !== true) {
++ longest = helpers.measureText(ctx, data, gc, longest, thing);
++ } else if (helpers.isArray(thing)) {
++ // if it is an array lets measure each element
++ // to do maybe simplify this function a bit so we can do this more recursively?
++ helpers.each(thing, function(nestedThing) {
++ // Undefined strings and arrays should not be measured
++ if (nestedThing !== undefined && nestedThing !== null && !helpers.isArray(nestedThing)) {
++ longest = helpers.measureText(ctx, data, gc, longest, nestedThing);
++ }
++ });
++ }
++ });
++
++ var gcLen = gc.length / 2;
++ if (gcLen > arrayOfThings.length) {
++ for (var i = 0; i < gcLen; i++) {
++ delete data[gc[i]];
++ }
++ gc.splice(0, gcLen);
++ }
++ return longest;
++ };
++ helpers.measureText = function(ctx, data, gc, longest, string) {
++ var textWidth = data[string];
++ if (!textWidth) {
++ textWidth = data[string] = ctx.measureText(string).width;
++ gc.push(string);
++ }
++ if (textWidth > longest) {
++ longest = textWidth;
++ }
++ return longest;
++ };
++ helpers.numberOfLabelLines = function(arrayOfThings) {
++ var numberOfLines = 1;
++ helpers.each(arrayOfThings, function(thing) {
++ if (helpers.isArray(thing)) {
++ if (thing.length > numberOfLines) {
++ numberOfLines = thing.length;
++ }
++ }
++ });
++ return numberOfLines;
++ };
++ helpers.drawRoundedRectangle = function(ctx, x, y, width, height, radius) {
++ ctx.beginPath();
++ ctx.moveTo(x + radius, y);
++ ctx.lineTo(x + width - radius, y);
++ ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
++ ctx.lineTo(x + width, y + height - radius);
++ ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
++ ctx.lineTo(x + radius, y + height);
++ ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
++ ctx.lineTo(x, y + radius);
++ ctx.quadraticCurveTo(x, y, x + radius, y);
++ ctx.closePath();
++ };
++ helpers.color = function(c) {
++ if (!color) {
++ console.error('Color.js not found!');
++ return c;
++ }
++
++ /* global CanvasGradient */
++ if (c instanceof CanvasGradient) {
++ return color(Chart.defaults.global.defaultColor);
++ }
++
++ return color(c);
++ };
++ helpers.addResizeListener = function(node, callback) {
++ var iframe = document.createElement('iframe');
++ iframe.className = 'chartjs-hidden-iframe';
++ iframe.style.cssText =
++ 'display:block;'+
++ 'overflow:hidden;'+
++ 'border:0;'+
++ 'margin:0;'+
++ 'top:0;'+
++ 'left:0;'+
++ 'bottom:0;'+
++ 'right:0;'+
++ 'height:100%;'+
++ 'width:100%;'+
++ 'position:absolute;'+
++ 'pointer-events:none;'+
++ 'z-index:-1;';
++
++ // Prevent the iframe to gain focus on tab.
++ // https://github.com/chartjs/Chart.js/issues/3090
++ iframe.tabIndex = -1;
++
++ // Let's keep track of this added iframe and thus avoid DOM query when removing it.
++ var stub = node._chartjs = {
++ resizer: iframe,
++ ticking: false
++ };
++
++ // Throttle the callback notification until the next animation frame.
++ var notify = function() {
++ if (!stub.ticking) {
++ stub.ticking = true;
++ helpers.requestAnimFrame.call(window, function() {
++ if (stub.resizer) {
++ stub.ticking = false;
++ return callback();
++ }
++ });
++ }
++ };
++
++ // If the iframe is re-attached to the DOM, the resize listener is removed because the
++ // content is reloaded, so make sure to install the handler after the iframe is loaded.
++ // https://github.com/chartjs/Chart.js/issues/3521
++ helpers.addEvent(iframe, 'load', function() {
++ helpers.addEvent(iframe.contentWindow || iframe, 'resize', notify);
++
++ // The iframe size might have changed while loading, which can also
++ // happen if the size has been changed while detached from the DOM.
++ notify();
++ });
++
++ node.insertBefore(iframe, node.firstChild);
++ };
++ helpers.removeResizeListener = function(node) {
++ if (!node || !node._chartjs) {
++ return;
++ }
++
++ var iframe = node._chartjs.resizer;
++ if (iframe) {
++ iframe.parentNode.removeChild(iframe);
++ node._chartjs.resizer = null;
++ }
++
++ delete node._chartjs;
++ };
++ helpers.isArray = Array.isArray?
++ function(obj) {
++ return Array.isArray(obj);
++ } :
++ function(obj) {
++ return Object.prototype.toString.call(obj) === '[object Array]';
++ };
++ // ! @see http://stackoverflow.com/a/14853974
++ helpers.arrayEquals = function(a0, a1) {
++ var i, ilen, v0, v1;
++
++ if (!a0 || !a1 || a0.length !== a1.length) {
++ return false;
++ }
++
++ for (i = 0, ilen=a0.length; i < ilen; ++i) {
++ v0 = a0[i];
++ v1 = a1[i];
++
++ if (v0 instanceof Array && v1 instanceof Array) {
++ if (!helpers.arrayEquals(v0, v1)) {
++ return false;
++ }
++ } else if (v0 !== v1) {
++ // NOTE: two different object instances will never be equal: {x:20} != {x:20}
++ return false;
++ }
++ }
++
++ return true;
++ };
++ helpers.callCallback = function(fn, args, _tArg) {
++ if (fn && typeof fn.call === 'function') {
++ fn.apply(_tArg, args);
++ }
++ };
++ helpers.getHoverColor = function(colorValue) {
++ /* global CanvasPattern */
++ return (colorValue instanceof CanvasPattern) ?
++ colorValue :
++ helpers.color(colorValue).saturate(0.5).darken(0.1).rgbString();
++ };
++};
++
++},{"3":3}],27:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++ var helpers = Chart.helpers;
++
++ /**
++ * Helper function to traverse all of the visible elements in the chart
++ * @param chart {chart} the chart
++ * @param handler {Function} the callback to execute for each visible item
++ */
++ function parseVisibleItems(chart, handler) {
++ var datasets = chart.data.datasets;
++ var meta, i, j, ilen, jlen;
++
++ for (i = 0, ilen = datasets.length; i < ilen; ++i) {
++ if (!chart.isDatasetVisible(i)) {
++ continue;
++ }
++
++ meta = chart.getDatasetMeta(i);
++ for (j = 0, jlen = meta.data.length; j < jlen; ++j) {
++ var element = meta.data[j];
++ if (!element._view.skip) {
++ handler(element);
++ }
++ }
++ }
++ }
++
++ /**
++ * Helper function to get the items that intersect the event position
++ * @param items {ChartElement[]} elements to filter
++ * @param position {Point} the point to be nearest to
++ * @return {ChartElement[]} the nearest items
++ */
++ function getIntersectItems(chart, position) {
++ var elements = [];
++
++ parseVisibleItems(chart, function(element) {
++ if (element.inRange(position.x, position.y)) {
++ elements.push(element);
++ }
++ });
++
++ return elements;
++ }
++
++ /**
++ * Helper function to get the items nearest to the event position considering all visible items in teh chart
++ * @param chart {Chart} the chart to look at elements from
++ * @param position {Point} the point to be nearest to
++ * @param intersect {Boolean} if true, only consider items that intersect the position
++ * @param distanceMetric {Function} Optional function to provide the distance between
++ * @return {ChartElement[]} the nearest items
++ */
++ function getNearestItems(chart, position, intersect, distanceMetric) {
++ var minDistance = Number.POSITIVE_INFINITY;
++ var nearestItems = [];
++
++ if (!distanceMetric) {
++ distanceMetric = helpers.distanceBetweenPoints;
++ }
++
++ parseVisibleItems(chart, function(element) {
++ if (intersect && !element.inRange(position.x, position.y)) {
++ return;
++ }
++
++ var center = element.getCenterPoint();
++ var distance = distanceMetric(position, center);
++
++ if (distance < minDistance) {
++ nearestItems = [element];
++ minDistance = distance;
++ } else if (distance === minDistance) {
++ // Can have multiple items at the same distance in which case we sort by size
++ nearestItems.push(element);
++ }
++ });
++
++ return nearestItems;
++ }
++
++ function indexMode(chart, e, options) {
++ var position = helpers.getRelativePosition(e, chart.chart);
++ var distanceMetric = function(pt1, pt2) {
++ return Math.abs(pt1.x - pt2.x);
++ };
++ var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric);
++ var elements = [];
++
++ if (!items.length) {
++ return [];
++ }
++
++ chart.data.datasets.forEach(function(dataset, datasetIndex) {
++ if (chart.isDatasetVisible(datasetIndex)) {
++ var meta = chart.getDatasetMeta(datasetIndex),
++ element = meta.data[items[0]._index];
++
++ // don't count items that are skipped (null data)
++ if (element && !element._view.skip) {
++ elements.push(element);
++ }
++ }
++ });
++
++ return elements;
++ }
++
++ /**
++ * @interface IInteractionOptions
++ */
++ /**
++ * If true, only consider items that intersect the point
++ * @name IInterfaceOptions#boolean
++ * @type Boolean
++ */
++
++ /**
++ * @namespace Chart.Interaction
++ * Contains interaction related functions
++ */
++ Chart.Interaction = {
++ // Helper function for different modes
++ modes: {
++ single: function(chart, e) {
++ var position = helpers.getRelativePosition(e, chart.chart);
++ var elements = [];
++
++ parseVisibleItems(chart, function(element) {
++ if (element.inRange(position.x, position.y)) {
++ elements.push(element);
++ return elements;
++ }
++ });
++
++ return elements.slice(0, 1);
++ },
++
++ /**
++ * @function Chart.Interaction.modes.label
++ * @deprecated since version 2.4.0
++ */
++ label: indexMode,
++
++ /**
++ * Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something
++ * If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item
++ * @function Chart.Interaction.modes.index
++ * @since v2.4.0
++ * @param chart {chart} the chart we are returning items from
++ * @param e {Event} the event we are find things at
++ * @param options {IInteractionOptions} options to use during interaction
++ * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
++ */
++ index: indexMode,
++
++ /**
++ * Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something
++ * If the options.intersect is false, we find the nearest item and return the items in that dataset
++ * @function Chart.Interaction.modes.dataset
++ * @param chart {chart} the chart we are returning items from
++ * @param e {Event} the event we are find things at
++ * @param options {IInteractionOptions} options to use during interaction
++ * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
++ */
++ dataset: function(chart, e, options) {
++ var position = helpers.getRelativePosition(e, chart.chart);
++ var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false);
++
++ if (items.length > 0) {
++ items = chart.getDatasetMeta(items[0]._datasetIndex).data;
++ }
++
++ return items;
++ },
++
++ /**
++ * @function Chart.Interaction.modes.x-axis
++ * @deprecated since version 2.4.0. Use index mode and intersect == true
++ */
++ 'x-axis': function(chart, e) {
++ return indexMode(chart, e, true);
++ },
++
++ /**
++ * Point mode returns all elements that hit test based on the event position
++ * of the event
++ * @function Chart.Interaction.modes.intersect
++ * @param chart {chart} the chart we are returning items from
++ * @param e {Event} the event we are find things at
++ * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
++ */
++ point: function(chart, e) {
++ var position = helpers.getRelativePosition(e, chart.chart);
++ return getIntersectItems(chart, position);
++ },
++
++ /**
++ * nearest mode returns the element closest to the point
++ * @function Chart.Interaction.modes.intersect
++ * @param chart {chart} the chart we are returning items from
++ * @param e {Event} the event we are find things at
++ * @param options {IInteractionOptions} options to use
++ * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
++ */
++ nearest: function(chart, e, options) {
++ var position = helpers.getRelativePosition(e, chart.chart);
++ var nearestItems = getNearestItems(chart, position, options.intersect);
++
++ // We have multiple items at the same distance from the event. Now sort by smallest
++ if (nearestItems.length > 1) {
++ nearestItems.sort(function(a, b) {
++ var sizeA = a.getArea();
++ var sizeB = b.getArea();
++ var ret = sizeA - sizeB;
++
++ if (ret === 0) {
++ // if equal sort by dataset index
++ ret = a._datasetIndex - b._datasetIndex;
++ }
++
++ return ret;
++ });
++ }
++
++ // Return only 1 item
++ return nearestItems.slice(0, 1);
++ },
++
++ /**
++ * x mode returns the elements that hit-test at the current x coordinate
++ * @function Chart.Interaction.modes.x
++ * @param chart {chart} the chart we are returning items from
++ * @param e {Event} the event we are find things at
++ * @param options {IInteractionOptions} options to use
++ * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
++ */
++ x: function(chart, e, options) {
++ var position = helpers.getRelativePosition(e, chart.chart);
++ var items = [];
++ var intersectsItem = false;
++
++ parseVisibleItems(chart, function(element) {
++ if (element.inXRange(position.x)) {
++ items.push(element);
++ }
++
++ if (element.inRange(position.x, position.y)) {
++ intersectsItem = true;
++ }
++ });
++
++ // If we want to trigger on an intersect and we don't have any items
++ // that intersect the position, return nothing
++ if (options.intersect && !intersectsItem) {
++ items = [];
++ }
++ return items;
++ },
++
++ /**
++ * y mode returns the elements that hit-test at the current y coordinate
++ * @function Chart.Interaction.modes.y
++ * @param chart {chart} the chart we are returning items from
++ * @param e {Event} the event we are find things at
++ * @param options {IInteractionOptions} options to use
++ * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
++ */
++ y: function(chart, e, options) {
++ var position = helpers.getRelativePosition(e, chart.chart);
++ var items = [];
++ var intersectsItem = false;
++
++ parseVisibleItems(chart, function(element) {
++ if (element.inYRange(position.y)) {
++ items.push(element);
++ }
++
++ if (element.inRange(position.x, position.y)) {
++ intersectsItem = true;
++ }
++ });
++
++ // If we want to trigger on an intersect and we don't have any items
++ // that intersect the position, return nothing
++ if (options.intersect && !intersectsItem) {
++ items = [];
++ }
++ return items;
++ }
++ }
++ };
++};
++
++},{}],28:[function(require,module,exports){
++'use strict';
++
++module.exports = function() {
++
++ // Occupy the global variable of Chart, and create a simple base class
++ var Chart = function(item, config) {
++ this.controller = new Chart.Controller(item, config, this);
++ return this.controller;
++ };
++
++ // Globally expose the defaults to allow for user updating/changing
++ Chart.defaults = {
++ global: {
++ responsive: true,
++ responsiveAnimationDuration: 0,
++ maintainAspectRatio: true,
++ events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'],
++ hover: {
++ onHover: null,
++ mode: 'nearest',
++ intersect: true,
++ animationDuration: 400
++ },
++ onClick: null,
++ defaultColor: 'rgba(0,0,0,0.1)',
++ defaultFontColor: '#666',
++ defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
++ defaultFontSize: 12,
++ defaultFontStyle: 'normal',
++ showLines: true,
++
++ // Element defaults defined in element extensions
++ elements: {},
++
++ // Legend callback string
++ legendCallback: function(chart) {
++ var text = [];
++ text.push('<ul class="' + chart.id + '-legend">');
++ for (var i = 0; i < chart.data.datasets.length; i++) {
++ text.push('<li><span style="background-color:' + chart.data.datasets[i].backgroundColor + '"></span>');
++ if (chart.data.datasets[i].label) {
++ text.push(chart.data.datasets[i].label);
++ }
++ text.push('</li>');
++ }
++ text.push('</ul>');
++
++ return text.join('');
++ }
++ }
++ };
++
++ Chart.Chart = Chart;
++
++ return Chart;
++};
++
++},{}],29:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++
++ var helpers = Chart.helpers;
++
++ // The layout service is very self explanatory. It's responsible for the layout within a chart.
++ // Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need
++ // It is this service's responsibility of carrying out that layout.
++ Chart.layoutService = {
++ defaults: {},
++
++ // Register a box to a chartInstance. A box is simply a reference to an object that requires layout. eg. Scales, Legend, Plugins.
++ addBox: function(chartInstance, box) {
++ if (!chartInstance.boxes) {
++ chartInstance.boxes = [];
++ }
++ chartInstance.boxes.push(box);
++ },
++
++ removeBox: function(chartInstance, box) {
++ if (!chartInstance.boxes) {
++ return;
++ }
++ chartInstance.boxes.splice(chartInstance.boxes.indexOf(box), 1);
++ },
++
++ // The most important function
++ update: function(chartInstance, width, height) {
++
++ if (!chartInstance) {
++ return;
++ }
++
++ var layoutOptions = chartInstance.options.layout;
++ var padding = layoutOptions ? layoutOptions.padding : null;
++
++ var leftPadding = 0;
++ var rightPadding = 0;
++ var topPadding = 0;
++ var bottomPadding = 0;
++
++ if (!isNaN(padding)) {
++ // options.layout.padding is a number. assign to all
++ leftPadding = padding;
++ rightPadding = padding;
++ topPadding = padding;
++ bottomPadding = padding;
++ } else {
++ leftPadding = padding.left || 0;
++ rightPadding = padding.right || 0;
++ topPadding = padding.top || 0;
++ bottomPadding = padding.bottom || 0;
++ }
++
++ var leftBoxes = helpers.where(chartInstance.boxes, function(box) {
++ return box.options.position === 'left';
++ });
++ var rightBoxes = helpers.where(chartInstance.boxes, function(box) {
++ return box.options.position === 'right';
++ });
++ var topBoxes = helpers.where(chartInstance.boxes, function(box) {
++ return box.options.position === 'top';
++ });
++ var bottomBoxes = helpers.where(chartInstance.boxes, function(box) {
++ return box.options.position === 'bottom';
++ });
++
++ // Boxes that overlay the chartarea such as the radialLinear scale
++ var chartAreaBoxes = helpers.where(chartInstance.boxes, function(box) {
++ return box.options.position === 'chartArea';
++ });
++
++ // Ensure that full width boxes are at the very top / bottom
++ topBoxes.sort(function(a, b) {
++ return (b.options.fullWidth ? 1 : 0) - (a.options.fullWidth ? 1 : 0);
++ });
++ bottomBoxes.sort(function(a, b) {
++ return (a.options.fullWidth ? 1 : 0) - (b.options.fullWidth ? 1 : 0);
++ });
++
++ // Essentially we now have any number of boxes on each of the 4 sides.
++ // Our canvas looks like the following.
++ // The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and
++ // B1 is the bottom axis
++ // There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays
++ // These locations are single-box locations only, when trying to register a chartArea location that is already taken,
++ // an error will be thrown.
++ //
++ // |----------------------------------------------------|
++ // | T1 (Full Width) |
++ // |----------------------------------------------------|
++ // | | | T2 | |
++ // | |----|-------------------------------------|----|
++ // | | | C1 | | C2 | |
++ // | | |----| |----| |
++ // | | | | |
++ // | L1 | L2 | ChartArea (C0) | R1 |
++ // | | | | |
++ // | | |----| |----| |
++ // | | | C3 | | C4 | |
++ // | |----|-------------------------------------|----|
++ // | | | B1 | |
++ // |----------------------------------------------------|
++ // | B2 (Full Width) |
++ // |----------------------------------------------------|
++ //
++ // What we do to find the best sizing, we do the following
++ // 1. Determine the minimum size of the chart area.
++ // 2. Split the remaining width equally between each vertical axis
++ // 3. Split the remaining height equally between each horizontal axis
++ // 4. Give each layout the maximum size it can be. The layout will return it's minimum size
++ // 5. Adjust the sizes of each axis based on it's minimum reported size.
++ // 6. Refit each axis
++ // 7. Position each axis in the final location
++ // 8. Tell the chart the final location of the chart area
++ // 9. Tell any axes that overlay the chart area the positions of the chart area
++
++ // Step 1
++ var chartWidth = width - leftPadding - rightPadding;
++ var chartHeight = height - topPadding - bottomPadding;
++ var chartAreaWidth = chartWidth / 2; // min 50%
++ var chartAreaHeight = chartHeight / 2; // min 50%
++
++ // Step 2
++ var verticalBoxWidth = (width - chartAreaWidth) / (leftBoxes.length + rightBoxes.length);
++
++ // Step 3
++ var horizontalBoxHeight = (height - chartAreaHeight) / (topBoxes.length + bottomBoxes.length);
++
++ // Step 4
++ var maxChartAreaWidth = chartWidth;
++ var maxChartAreaHeight = chartHeight;
++ var minBoxSizes = [];
++
++ function getMinimumBoxSize(box) {
++ var minSize;
++ var isHorizontal = box.isHorizontal();
++
++ if (isHorizontal) {
++ minSize = box.update(box.options.fullWidth ? chartWidth : maxChartAreaWidth, horizontalBoxHeight);
++ maxChartAreaHeight -= minSize.height;
++ } else {
++ minSize = box.update(verticalBoxWidth, chartAreaHeight);
++ maxChartAreaWidth -= minSize.width;
++ }
++
++ minBoxSizes.push({
++ horizontal: isHorizontal,
++ minSize: minSize,
++ box: box
++ });
++ }
++
++ helpers.each(leftBoxes.concat(rightBoxes, topBoxes, bottomBoxes), getMinimumBoxSize);
++
++ // At this point, maxChartAreaHeight and maxChartAreaWidth are the size the chart area could
++ // be if the axes are drawn at their minimum sizes.
++
++ // Steps 5 & 6
++ var totalLeftBoxesWidth = leftPadding;
++ var totalRightBoxesWidth = rightPadding;
++ var totalTopBoxesHeight = topPadding;
++ var totalBottomBoxesHeight = bottomPadding;
++
++ // Function to fit a box
++ function fitBox(box) {
++ var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBox) {
++ return minBox.box === box;
++ });
++
++ if (minBoxSize) {
++ if (box.isHorizontal()) {
++ var scaleMargin = {
++ left: totalLeftBoxesWidth,
++ right: totalRightBoxesWidth,
++ top: 0,
++ bottom: 0
++ };
++
++ // Don't use min size here because of label rotation. When the labels are rotated, their rotation highly depends
++ // on the margin. Sometimes they need to increase in size slightly
++ box.update(box.options.fullWidth ? chartWidth : maxChartAreaWidth, chartHeight / 2, scaleMargin);
++ } else {
++ box.update(minBoxSize.minSize.width, maxChartAreaHeight);
++ }
++ }
++ }
++
++ // Update, and calculate the left and right margins for the horizontal boxes
++ helpers.each(leftBoxes.concat(rightBoxes), fitBox);
++
++ helpers.each(leftBoxes, function(box) {
++ totalLeftBoxesWidth += box.width;
++ });
++
++ helpers.each(rightBoxes, function(box) {
++ totalRightBoxesWidth += box.width;
++ });
++
++ // Set the Left and Right margins for the horizontal boxes
++ helpers.each(topBoxes.concat(bottomBoxes), fitBox);
++
++ // Figure out how much margin is on the top and bottom of the vertical boxes
++ helpers.each(topBoxes, function(box) {
++ totalTopBoxesHeight += box.height;
++ });
++
++ helpers.each(bottomBoxes, function(box) {
++ totalBottomBoxesHeight += box.height;
++ });
++
++ function finalFitVerticalBox(box) {
++ var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minSize) {
++ return minSize.box === box;
++ });
++
++ var scaleMargin = {
++ left: 0,
++ right: 0,
++ top: totalTopBoxesHeight,
++ bottom: totalBottomBoxesHeight
++ };
++
++ if (minBoxSize) {
++ box.update(minBoxSize.minSize.width, maxChartAreaHeight, scaleMargin);
++ }
++ }
++
++ // Let the left layout know the final margin
++ helpers.each(leftBoxes.concat(rightBoxes), finalFitVerticalBox);
++
++ // Recalculate because the size of each layout might have changed slightly due to the margins (label rotation for instance)
++ totalLeftBoxesWidth = leftPadding;
++ totalRightBoxesWidth = rightPadding;
++ totalTopBoxesHeight = topPadding;
++ totalBottomBoxesHeight = bottomPadding;
++
++ helpers.each(leftBoxes, function(box) {
++ totalLeftBoxesWidth += box.width;
++ });
++
++ helpers.each(rightBoxes, function(box) {
++ totalRightBoxesWidth += box.width;
++ });
++
++ helpers.each(topBoxes, function(box) {
++ totalTopBoxesHeight += box.height;
++ });
++ helpers.each(bottomBoxes, function(box) {
++ totalBottomBoxesHeight += box.height;
++ });
++
++ // Figure out if our chart area changed. This would occur if the dataset layout label rotation
++ // changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do
++ // without calling `fit` again
++ var newMaxChartAreaHeight = height - totalTopBoxesHeight - totalBottomBoxesHeight;
++ var newMaxChartAreaWidth = width - totalLeftBoxesWidth - totalRightBoxesWidth;
++
++ if (newMaxChartAreaWidth !== maxChartAreaWidth || newMaxChartAreaHeight !== maxChartAreaHeight) {
++ helpers.each(leftBoxes, function(box) {
++ box.height = newMaxChartAreaHeight;
++ });
++
++ helpers.each(rightBoxes, function(box) {
++ box.height = newMaxChartAreaHeight;
++ });
++
++ helpers.each(topBoxes, function(box) {
++ if (!box.options.fullWidth) {
++ box.width = newMaxChartAreaWidth;
++ }
++ });
++
++ helpers.each(bottomBoxes, function(box) {
++ if (!box.options.fullWidth) {
++ box.width = newMaxChartAreaWidth;
++ }
++ });
++
++ maxChartAreaHeight = newMaxChartAreaHeight;
++ maxChartAreaWidth = newMaxChartAreaWidth;
++ }
++
++ // Step 7 - Position the boxes
++ var left = leftPadding;
++ var top = topPadding;
++
++ function placeBox(box) {
++ if (box.isHorizontal()) {
++ box.left = box.options.fullWidth ? leftPadding : totalLeftBoxesWidth;
++ box.right = box.options.fullWidth ? width - rightPadding : totalLeftBoxesWidth + maxChartAreaWidth;
++ box.top = top;
++ box.bottom = top + box.height;
++
++ // Move to next point
++ top = box.bottom;
++
++ } else {
++
++ box.left = left;
++ box.right = left + box.width;
++ box.top = totalTopBoxesHeight;
++ box.bottom = totalTopBoxesHeight + maxChartAreaHeight;
++
++ // Move to next point
++ left = box.right;
++ }
++ }
++
++ helpers.each(leftBoxes.concat(topBoxes), placeBox);
++
++ // Account for chart width and height
++ left += maxChartAreaWidth;
++ top += maxChartAreaHeight;
++
++ helpers.each(rightBoxes, placeBox);
++ helpers.each(bottomBoxes, placeBox);
++
++ // Step 8
++ chartInstance.chartArea = {
++ left: totalLeftBoxesWidth,
++ top: totalTopBoxesHeight,
++ right: totalLeftBoxesWidth + maxChartAreaWidth,
++ bottom: totalTopBoxesHeight + maxChartAreaHeight
++ };
++
++ // Step 9
++ helpers.each(chartAreaBoxes, function(box) {
++ box.left = chartInstance.chartArea.left;
++ box.top = chartInstance.chartArea.top;
++ box.right = chartInstance.chartArea.right;
++ box.bottom = chartInstance.chartArea.bottom;
++
++ box.update(maxChartAreaWidth, maxChartAreaHeight);
++ });
++ }
++ };
++};
++
++},{}],30:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++
++ var helpers = Chart.helpers;
++ var noop = helpers.noop;
++
++ Chart.defaults.global.legend = {
++
++ display: true,
++ position: 'top',
++ fullWidth: true, // marks that this box should take the full width of the canvas (pushing down other boxes)
++ reverse: false,
++
++ // a callback that will handle
++ onClick: function(e, legendItem) {
++ var index = legendItem.datasetIndex;
++ var ci = this.chart;
++ var meta = ci.getDatasetMeta(index);
++
++ // See controller.isDatasetVisible comment
++ meta.hidden = meta.hidden === null? !ci.data.datasets[index].hidden : null;
++
++ // We hid a dataset ... rerender the chart
++ ci.update();
++ },
++
++ onHover: null,
++
++ labels: {
++ boxWidth: 40,
++ padding: 10,
++ // Generates labels shown in the legend
++ // Valid properties to return:
++ // text : text to display
++ // fillStyle : fill of coloured box
++ // strokeStyle: stroke of coloured box
++ // hidden : if this legend item refers to a hidden item
++ // lineCap : cap style for line
++ // lineDash
++ // lineDashOffset :
++ // lineJoin :
++ // lineWidth :
++ generateLabels: function(chart) {
++ var data = chart.data;
++ return helpers.isArray(data.datasets) ? data.datasets.map(function(dataset, i) {
++ return {
++ text: dataset.label,
++ fillStyle: (!helpers.isArray(dataset.backgroundColor) ? dataset.backgroundColor : dataset.backgroundColor[0]),
++ hidden: !chart.isDatasetVisible(i),
++ lineCap: dataset.borderCapStyle,
++ lineDash: dataset.borderDash,
++ lineDashOffset: dataset.borderDashOffset,
++ lineJoin: dataset.borderJoinStyle,
++ lineWidth: dataset.borderWidth,
++ strokeStyle: dataset.borderColor,
++ pointStyle: dataset.pointStyle,
++
++ // Below is extra data used for toggling the datasets
++ datasetIndex: i
++ };
++ }, this) : [];
++ }
++ }
++ };
++
++ /**
++ * Helper function to get the box width based on the usePointStyle option
++ * @param labelopts {Object} the label options on the legend
++ * @param fontSize {Number} the label font size
++ * @return {Number} width of the color box area
++ */
++ function getBoxWidth(labelOpts, fontSize) {
++ return labelOpts.usePointStyle ?
++ fontSize * Math.SQRT2 :
++ labelOpts.boxWidth;
++ }
++
++ Chart.Legend = Chart.Element.extend({
++
++ initialize: function(config) {
++ helpers.extend(this, config);
++
++ // Contains hit boxes for each dataset (in dataset order)
++ this.legendHitBoxes = [];
++
++ // Are we in doughnut mode which has a different data type
++ this.doughnutMode = false;
++ },
++
++ // These methods are ordered by lifecycle. Utilities then follow.
++ // Any function defined here is inherited by all legend types.
++ // Any function can be extended by the legend type
++
++ beforeUpdate: noop,
++ update: function(maxWidth, maxHeight, margins) {
++ var me = this;
++
++ // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
++ me.beforeUpdate();
++
++ // Absorb the master measurements
++ me.maxWidth = maxWidth;
++ me.maxHeight = maxHeight;
++ me.margins = margins;
++
++ // Dimensions
++ me.beforeSetDimensions();
++ me.setDimensions();
++ me.afterSetDimensions();
++ // Labels
++ me.beforeBuildLabels();
++ me.buildLabels();
++ me.afterBuildLabels();
++
++ // Fit
++ me.beforeFit();
++ me.fit();
++ me.afterFit();
++ //
++ me.afterUpdate();
++
++ return me.minSize;
++ },
++ afterUpdate: noop,
++
++ //
++
++ beforeSetDimensions: noop,
++ setDimensions: function() {
++ var me = this;
++ // Set the unconstrained dimension before label rotation
++ if (me.isHorizontal()) {
++ // Reset position before calculating rotation
++ me.width = me.maxWidth;
++ me.left = 0;
++ me.right = me.width;
++ } else {
++ me.height = me.maxHeight;
++
++ // Reset position before calculating rotation
++ me.top = 0;
++ me.bottom = me.height;
++ }
++
++ // Reset padding
++ me.paddingLeft = 0;
++ me.paddingTop = 0;
++ me.paddingRight = 0;
++ me.paddingBottom = 0;
++
++ // Reset minSize
++ me.minSize = {
++ width: 0,
++ height: 0
++ };
++ },
++ afterSetDimensions: noop,
++
++ //
++
++ beforeBuildLabels: noop,
++ buildLabels: function() {
++ var me = this;
++ me.legendItems = me.options.labels.generateLabels.call(me, me.chart);
++ if (me.options.reverse) {
++ me.legendItems.reverse();
++ }
++ },
++ afterBuildLabels: noop,
++
++ //
++
++ beforeFit: noop,
++ fit: function() {
++ var me = this;
++ var opts = me.options;
++ var labelOpts = opts.labels;
++ var display = opts.display;
++
++ var ctx = me.ctx;
++
++ var globalDefault = Chart.defaults.global,
++ itemOrDefault = helpers.getValueOrDefault,
++ fontSize = itemOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize),
++ fontStyle = itemOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle),
++ fontFamily = itemOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily),
++ labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);
++
++ // Reset hit boxes
++ var hitboxes = me.legendHitBoxes = [];
++
++ var minSize = me.minSize;
++ var isHorizontal = me.isHorizontal();
++
++ if (isHorizontal) {
++ minSize.width = me.maxWidth; // fill all the width
++ minSize.height = display ? 10 : 0;
++ } else {
++ minSize.width = display ? 10 : 0;
++ minSize.height = me.maxHeight; // fill all the height
++ }
++
++ // Increase sizes here
++ if (display) {
++ ctx.font = labelFont;
++
++ if (isHorizontal) {
++ // Labels
++
++ // Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one
++ var lineWidths = me.lineWidths = [0];
++ var totalHeight = me.legendItems.length ? fontSize + (labelOpts.padding) : 0;
++
++ ctx.textAlign = 'left';
++ ctx.textBaseline = 'top';
++
++ helpers.each(me.legendItems, function(legendItem, i) {
++ var boxWidth = getBoxWidth(labelOpts, fontSize);
++ var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
++
++ if (lineWidths[lineWidths.length - 1] + width + labelOpts.padding >= me.width) {
++ totalHeight += fontSize + (labelOpts.padding);
++ lineWidths[lineWidths.length] = me.left;
++ }
++
++ // Store the hitbox width and height here. Final position will be updated in `draw`
++ hitboxes[i] = {
++ left: 0,
++ top: 0,
++ width: width,
++ height: fontSize
++ };
++
++ lineWidths[lineWidths.length - 1] += width + labelOpts.padding;
++ });
++
++ minSize.height += totalHeight;
++
++ } else {
++ var vPadding = labelOpts.padding;
++ var columnWidths = me.columnWidths = [];
++ var totalWidth = labelOpts.padding;
++ var currentColWidth = 0;
++ var currentColHeight = 0;
++ var itemHeight = fontSize + vPadding;
++
++ helpers.each(me.legendItems, function(legendItem, i) {
++ var boxWidth = getBoxWidth(labelOpts, fontSize);
++ var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
++
++ // If too tall, go to new column
++ if (currentColHeight + itemHeight > minSize.height) {
++ totalWidth += currentColWidth + labelOpts.padding;
++ columnWidths.push(currentColWidth); // previous column width
++
++ currentColWidth = 0;
++ currentColHeight = 0;
++ }
++
++ // Get max width
++ currentColWidth = Math.max(currentColWidth, itemWidth);
++ currentColHeight += itemHeight;
++
++ // Store the hitbox width and height here. Final position will be updated in `draw`
++ hitboxes[i] = {
++ left: 0,
++ top: 0,
++ width: itemWidth,
++ height: fontSize
++ };
++ });
++
++ totalWidth += currentColWidth;
++ columnWidths.push(currentColWidth);
++ minSize.width += totalWidth;
++ }
++ }
++
++ me.width = minSize.width;
++ me.height = minSize.height;
++ },
++ afterFit: noop,
++
++ // Shared Methods
++ isHorizontal: function() {
++ return this.options.position === 'top' || this.options.position === 'bottom';
++ },
++
++ // Actually draw the legend on the canvas
++ draw: function() {
++ var me = this;
++ var opts = me.options;
++ var labelOpts = opts.labels;
++ var globalDefault = Chart.defaults.global,
++ lineDefault = globalDefault.elements.line,
++ legendWidth = me.width,
++ lineWidths = me.lineWidths;
++
++ if (opts.display) {
++ var ctx = me.ctx,
++ cursor,
++ itemOrDefault = helpers.getValueOrDefault,
++ fontColor = itemOrDefault(labelOpts.fontColor, globalDefault.defaultFontColor),
++ fontSize = itemOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize),
++ fontStyle = itemOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle),
++ fontFamily = itemOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily),
++ labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);
++
++ // Canvas setup
++ ctx.textAlign = 'left';
++ ctx.textBaseline = 'top';
++ ctx.lineWidth = 0.5;
++ ctx.strokeStyle = fontColor; // for strikethrough effect
++ ctx.fillStyle = fontColor; // render in correct colour
++ ctx.font = labelFont;
++
++ var boxWidth = getBoxWidth(labelOpts, fontSize),
++ hitboxes = me.legendHitBoxes;
++
++ // current position
++ var drawLegendBox = function(x, y, legendItem) {
++ if (isNaN(boxWidth) || boxWidth <= 0) {
++ return;
++ }
++
++ // Set the ctx for the box
++ ctx.save();
++
++ ctx.fillStyle = itemOrDefault(legendItem.fillStyle, globalDefault.defaultColor);
++ ctx.lineCap = itemOrDefault(legendItem.lineCap, lineDefault.borderCapStyle);
++ ctx.lineDashOffset = itemOrDefault(legendItem.lineDashOffset, lineDefault.borderDashOffset);
++ ctx.lineJoin = itemOrDefault(legendItem.lineJoin, lineDefault.borderJoinStyle);
++ ctx.lineWidth = itemOrDefault(legendItem.lineWidth, lineDefault.borderWidth);
++ ctx.strokeStyle = itemOrDefault(legendItem.strokeStyle, globalDefault.defaultColor);
++ var isLineWidthZero = (itemOrDefault(legendItem.lineWidth, lineDefault.borderWidth) === 0);
++
++ if (ctx.setLineDash) {
++ // IE 9 and 10 do not support line dash
++ ctx.setLineDash(itemOrDefault(legendItem.lineDash, lineDefault.borderDash));
++ }
++
++ if (opts.labels && opts.labels.usePointStyle) {
++ // Recalculate x and y for drawPoint() because its expecting
++ // x and y to be center of figure (instead of top left)
++ var radius = fontSize * Math.SQRT2 / 2;
++ var offSet = radius / Math.SQRT2;
++ var centerX = x + offSet;
++ var centerY = y + offSet;
++
++ // Draw pointStyle as legend symbol
++ Chart.canvasHelpers.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY);
++ } else {
++ // Draw box as legend symbol
++ if (!isLineWidthZero) {
++ ctx.strokeRect(x, y, boxWidth, fontSize);
++ }
++ ctx.fillRect(x, y, boxWidth, fontSize);
++ }
++
++ ctx.restore();
++ };
++ var fillText = function(x, y, legendItem, textWidth) {
++ ctx.fillText(legendItem.text, boxWidth + (fontSize / 2) + x, y);
++
++ if (legendItem.hidden) {
++ // Strikethrough the text if hidden
++ ctx.beginPath();
++ ctx.lineWidth = 2;
++ ctx.moveTo(boxWidth + (fontSize / 2) + x, y + (fontSize / 2));
++ ctx.lineTo(boxWidth + (fontSize / 2) + x + textWidth, y + (fontSize / 2));
++ ctx.stroke();
++ }
++ };
++
++ // Horizontal
++ var isHorizontal = me.isHorizontal();
++ if (isHorizontal) {
++ cursor = {
++ x: me.left + ((legendWidth - lineWidths[0]) / 2),
++ y: me.top + labelOpts.padding,
++ line: 0
++ };
++ } else {
++ cursor = {
++ x: me.left + labelOpts.padding,
++ y: me.top + labelOpts.padding,
++ line: 0
++ };
++ }
++
++ var itemHeight = fontSize + labelOpts.padding;
++ helpers.each(me.legendItems, function(legendItem, i) {
++ var textWidth = ctx.measureText(legendItem.text).width,
++ width = boxWidth + (fontSize / 2) + textWidth,
++ x = cursor.x,
++ y = cursor.y;
++
++ if (isHorizontal) {
++ if (x + width >= legendWidth) {
++ y = cursor.y += itemHeight;
++ cursor.line++;
++ x = cursor.x = me.left + ((legendWidth - lineWidths[cursor.line]) / 2);
++ }
++ } else if (y + itemHeight > me.bottom) {
++ x = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding;
++ y = cursor.y = me.top;
++ cursor.line++;
++ }
++
++ drawLegendBox(x, y, legendItem);
++
++ hitboxes[i].left = x;
++ hitboxes[i].top = y;
++
++ // Fill the actual label
++ fillText(x, y, legendItem, textWidth);
++
++ if (isHorizontal) {
++ cursor.x += width + (labelOpts.padding);
++ } else {
++ cursor.y += itemHeight;
++ }
++
++ });
++ }
++ },
++
++ /**
++ * Handle an event
++ * @private
++ * @param e {Event} the event to handle
++ * @return {Boolean} true if a change occured
++ */
++ handleEvent: function(e) {
++ var me = this;
++ var opts = me.options;
++ var type = e.type === 'mouseup' ? 'click' : e.type;
++ var changed = false;
++
++ if (type === 'mousemove') {
++ if (!opts.onHover) {
++ return;
++ }
++ } else if (type === 'click') {
++ if (!opts.onClick) {
++ return;
++ }
++ } else {
++ return;
++ }
++
++ var position = helpers.getRelativePosition(e, me.chart.chart),
++ x = position.x,
++ y = position.y;
++
++ if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) {
++ // See if we are touching one of the dataset boxes
++ var lh = me.legendHitBoxes;
++ for (var i = 0; i < lh.length; ++i) {
++ var hitBox = lh[i];
++
++ if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) {
++ // Touching an element
++ if (type === 'click') {
++ opts.onClick.call(me, e, me.legendItems[i]);
++ changed = true;
++ break;
++ } else if (type === 'mousemove') {
++ opts.onHover.call(me, e, me.legendItems[i]);
++ changed = true;
++ break;
++ }
++ }
++ }
++ }
++
++ return changed;
++ }
++ });
++
++ // Register the legend plugin
++ Chart.plugins.register({
++ beforeInit: function(chartInstance) {
++ var opts = chartInstance.options;
++ var legendOpts = opts.legend;
++
++ if (legendOpts) {
++ chartInstance.legend = new Chart.Legend({
++ ctx: chartInstance.chart.ctx,
++ options: legendOpts,
++ chart: chartInstance
++ });
++
++ Chart.layoutService.addBox(chartInstance, chartInstance.legend);
++ }
++ }
++ });
++};
++
++},{}],31:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++
++ var noop = Chart.helpers.noop;
++
++ /**
++ * The plugin service singleton
++ * @namespace Chart.plugins
++ * @since 2.1.0
++ */
++ Chart.plugins = {
++ _plugins: [],
++
++ /**
++ * Registers the given plugin(s) if not already registered.
++ * @param {Array|Object} plugins plugin instance(s).
++ */
++ register: function(plugins) {
++ var p = this._plugins;
++ ([]).concat(plugins).forEach(function(plugin) {
++ if (p.indexOf(plugin) === -1) {
++ p.push(plugin);
++ }
++ });
++ },
++
++ /**
++ * Unregisters the given plugin(s) only if registered.
++ * @param {Array|Object} plugins plugin instance(s).
++ */
++ unregister: function(plugins) {
++ var p = this._plugins;
++ ([]).concat(plugins).forEach(function(plugin) {
++ var idx = p.indexOf(plugin);
++ if (idx !== -1) {
++ p.splice(idx, 1);
++ }
++ });
++ },
++
++ /**
++ * Remove all registered plugins.
++ * @since 2.1.5
++ */
++ clear: function() {
++ this._plugins = [];
++ },
++
++ /**
++ * Returns the number of registered plugins?
++ * @returns {Number}
++ * @since 2.1.5
++ */
++ count: function() {
++ return this._plugins.length;
++ },
++
++ /**
++ * Returns all registered plugin instances.
++ * @returns {Array} array of plugin objects.
++ * @since 2.1.5
++ */
++ getAll: function() {
++ return this._plugins;
++ },
++
++ /**
++ * Calls registered plugins on the specified extension, with the given args. This
++ * method immediately returns as soon as a plugin explicitly returns false. The
++ * returned value can be used, for instance, to interrupt the current action.
++ * @param {String} extension the name of the plugin method to call (e.g. 'beforeUpdate').
++ * @param {Array} [args] extra arguments to apply to the extension call.
++ * @returns {Boolean} false if any of the plugins return false, else returns true.
++ */
++ notify: function(extension, args) {
++ var plugins = this._plugins;
++ var ilen = plugins.length;
++ var i, plugin;
++
++ for (i=0; i<ilen; ++i) {
++ plugin = plugins[i];
++ if (typeof plugin[extension] === 'function') {
++ if (plugin[extension].apply(plugin, args || []) === false) {
++ return false;
++ }
++ }
++ }
++
++ return true;
++ }
++ };
++
++ /**
++ * Plugin extension methods.
++ * @interface Chart.PluginBase
++ * @since 2.1.0
++ */
++ Chart.PluginBase = Chart.Element.extend({
++ // Called at start of chart init
++ beforeInit: noop,
++
++ // Called at end of chart init
++ afterInit: noop,
++
++ // Called at start of update
++ beforeUpdate: noop,
++
++ // Called at end of update
++ afterUpdate: noop,
++
++ // Called at start of draw
++ beforeDraw: noop,
++
++ // Called at end of draw
++ afterDraw: noop,
++
++ // Called during destroy
++ destroy: noop
++ });
++
++ /**
++ * Provided for backward compatibility, use Chart.plugins instead
++ * @namespace Chart.pluginService
++ * @deprecated since version 2.1.5
++ * @todo remove me at version 3
++ */
++ Chart.pluginService = Chart.plugins;
++};
++
++},{}],32:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++
++ var helpers = Chart.helpers;
++
++ Chart.defaults.scale = {
++ display: true,
++ position: 'left',
++
++ // grid line settings
++ gridLines: {
++ display: true,
++ color: 'rgba(0, 0, 0, 0.1)',
++ lineWidth: 1,
++ drawBorder: true,
++ drawOnChartArea: true,
++ drawTicks: true,
++ tickMarkLength: 10,
++ zeroLineWidth: 1,
++ zeroLineColor: 'rgba(0,0,0,0.25)',
++ offsetGridLines: false,
++ borderDash: [],
++ borderDashOffset: 0.0
++ },
++
++ // scale label
++ scaleLabel: {
++ // actual label
++ labelString: '',
++
++ // display property
++ display: false
++ },
++
++ // label settings
++ ticks: {
++ beginAtZero: false,
++ minRotation: 0,
++ maxRotation: 50,
++ mirror: false,
++ padding: 10,
++ reverse: false,
++ display: true,
++ autoSkip: true,
++ autoSkipPadding: 0,
++ labelOffset: 0,
++ // We pass through arrays to be rendered as multiline labels, we convert Others to strings here.
++ callback: Chart.Ticks.formatters.values
++ }
++ };
++
++ Chart.Scale = Chart.Element.extend({
++
++ // These methods are ordered by lifecycle. Utilities then follow.
++ // Any function defined here is inherited by all scale types.
++ // Any function can be extended by the scale type
++
++ beforeUpdate: function() {
++ helpers.callCallback(this.options.beforeUpdate, [this]);
++ },
++ update: function(maxWidth, maxHeight, margins) {
++ var me = this;
++
++ // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
++ me.beforeUpdate();
++
++ // Absorb the master measurements
++ me.maxWidth = maxWidth;
++ me.maxHeight = maxHeight;
++ me.margins = helpers.extend({
++ left: 0,
++ right: 0,
++ top: 0,
++ bottom: 0
++ }, margins);
++
++ // Dimensions
++ me.beforeSetDimensions();
++ me.setDimensions();
++ me.afterSetDimensions();
++
++ // Data min/max
++ me.beforeDataLimits();
++ me.determineDataLimits();
++ me.afterDataLimits();
++
++ // Ticks
++ me.beforeBuildTicks();
++ me.buildTicks();
++ me.afterBuildTicks();
++
++ me.beforeTickToLabelConversion();
++ me.convertTicksToLabels();
++ me.afterTickToLabelConversion();
++
++ // Tick Rotation
++ me.beforeCalculateTickRotation();
++ me.calculateTickRotation();
++ me.afterCalculateTickRotation();
++ // Fit
++ me.beforeFit();
++ me.fit();
++ me.afterFit();
++ //
++ me.afterUpdate();
++
++ return me.minSize;
++
++ },
++ afterUpdate: function() {
++ helpers.callCallback(this.options.afterUpdate, [this]);
++ },
++
++ //
++
++ beforeSetDimensions: function() {
++ helpers.callCallback(this.options.beforeSetDimensions, [this]);
++ },
++ setDimensions: function() {
++ var me = this;
++ // Set the unconstrained dimension before label rotation
++ if (me.isHorizontal()) {
++ // Reset position before calculating rotation
++ me.width = me.maxWidth;
++ me.left = 0;
++ me.right = me.width;
++ } else {
++ me.height = me.maxHeight;
++
++ // Reset position before calculating rotation
++ me.top = 0;
++ me.bottom = me.height;
++ }
++
++ // Reset padding
++ me.paddingLeft = 0;
++ me.paddingTop = 0;
++ me.paddingRight = 0;
++ me.paddingBottom = 0;
++ },
++ afterSetDimensions: function() {
++ helpers.callCallback(this.options.afterSetDimensions, [this]);
++ },
++
++ // Data limits
++ beforeDataLimits: function() {
++ helpers.callCallback(this.options.beforeDataLimits, [this]);
++ },
++ determineDataLimits: helpers.noop,
++ afterDataLimits: function() {
++ helpers.callCallback(this.options.afterDataLimits, [this]);
++ },
++
++ //
++ beforeBuildTicks: function() {
++ helpers.callCallback(this.options.beforeBuildTicks, [this]);
++ },
++ buildTicks: helpers.noop,
++ afterBuildTicks: function() {
++ helpers.callCallback(this.options.afterBuildTicks, [this]);
++ },
++
++ beforeTickToLabelConversion: function() {
++ helpers.callCallback(this.options.beforeTickToLabelConversion, [this]);
++ },
++ convertTicksToLabels: function() {
++ var me = this;
++ // Convert ticks to strings
++ var tickOpts = me.options.ticks;
++ me.ticks = me.ticks.map(tickOpts.userCallback || tickOpts.callback);
++ },
++ afterTickToLabelConversion: function() {
++ helpers.callCallback(this.options.afterTickToLabelConversion, [this]);
++ },
++
++ //
++
++ beforeCalculateTickRotation: function() {
++ helpers.callCallback(this.options.beforeCalculateTickRotation, [this]);
++ },
++ calculateTickRotation: function() {
++ var me = this;
++ var context = me.ctx;
++ var globalDefaults = Chart.defaults.global;
++ var optionTicks = me.options.ticks;
++
++ // Get the width of each grid by calculating the difference
++ // between x offsets between 0 and 1.
++ var tickFontSize = helpers.getValueOrDefault(optionTicks.fontSize, globalDefaults.defaultFontSize);
++ var tickFontStyle = helpers.getValueOrDefault(optionTicks.fontStyle, globalDefaults.defaultFontStyle);
++ var tickFontFamily = helpers.getValueOrDefault(optionTicks.fontFamily, globalDefaults.defaultFontFamily);
++ var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);
++ context.font = tickLabelFont;
++
++ var firstWidth = context.measureText(me.ticks[0]).width;
++ var lastWidth = context.measureText(me.ticks[me.ticks.length - 1]).width;
++ var firstRotated;
++
++ me.labelRotation = optionTicks.minRotation || 0;
++ me.paddingRight = 0;
++ me.paddingLeft = 0;
++
++ if (me.options.display) {
++ if (me.isHorizontal()) {
++ me.paddingRight = lastWidth / 2 + 3;
++ me.paddingLeft = firstWidth / 2 + 3;
++
++ if (!me.longestTextCache) {
++ me.longestTextCache = {};
++ }
++ var originalLabelWidth = helpers.longestText(context, tickLabelFont, me.ticks, me.longestTextCache);
++ var labelWidth = originalLabelWidth;
++ var cosRotation;
++ var sinRotation;
++
++ // Allow 3 pixels x2 padding either side for label readability
++ // only the index matters for a dataset scale, but we want a consistent interface between scales
++ var tickWidth = me.getPixelForTick(1) - me.getPixelForTick(0) - 6;
++
++ // Max label rotation can be set or default to 90 - also act as a loop counter
++ while (labelWidth > tickWidth && me.labelRotation < optionTicks.maxRotation) {
++ cosRotation = Math.cos(helpers.toRadians(me.labelRotation));
++ sinRotation = Math.sin(helpers.toRadians(me.labelRotation));
++
++ firstRotated = cosRotation * firstWidth;
++
++ // We're right aligning the text now.
++ if (firstRotated + tickFontSize / 2 > me.yLabelWidth) {
++ me.paddingLeft = firstRotated + tickFontSize / 2;
++ }
++
++ me.paddingRight = tickFontSize / 2;
++
++ if (sinRotation * originalLabelWidth > me.maxHeight) {
++ // go back one step
++ me.labelRotation--;
++ break;
++ }
++
++ me.labelRotation++;
++ labelWidth = cosRotation * originalLabelWidth;
++ }
++ }
++ }
++
++ if (me.margins) {
++ me.paddingLeft = Math.max(me.paddingLeft - me.margins.left, 0);
++ me.paddingRight = Math.max(me.paddingRight - me.margins.right, 0);
++ }
++ },
++ afterCalculateTickRotation: function() {
++ helpers.callCallback(this.options.afterCalculateTickRotation, [this]);
++ },
++
++ //
++
++ beforeFit: function() {
++ helpers.callCallback(this.options.beforeFit, [this]);
++ },
++ fit: function() {
++ var me = this;
++ // Reset
++ var minSize = me.minSize = {
++ width: 0,
++ height: 0
++ };
++
++ var opts = me.options;
++ var globalDefaults = Chart.defaults.global;
++ var tickOpts = opts.ticks;
++ var scaleLabelOpts = opts.scaleLabel;
++ var gridLineOpts = opts.gridLines;
++ var display = opts.display;
++ var isHorizontal = me.isHorizontal();
++
++ var tickFontSize = helpers.getValueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
++ var tickFontStyle = helpers.getValueOrDefault(tickOpts.fontStyle, globalDefaults.defaultFontStyle);
++ var tickFontFamily = helpers.getValueOrDefault(tickOpts.fontFamily, globalDefaults.defaultFontFamily);
++ var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);
++
++ var scaleLabelFontSize = helpers.getValueOrDefault(scaleLabelOpts.fontSize, globalDefaults.defaultFontSize);
++
++ var tickMarkLength = opts.gridLines.tickMarkLength;
++
++ // Width
++ if (isHorizontal) {
++ // subtract the margins to line up with the chartArea if we are a full width scale
++ minSize.width = me.isFullWidth() ? me.maxWidth - me.margins.left - me.margins.right : me.maxWidth;
++ } else {
++ minSize.width = display && gridLineOpts.drawTicks ? tickMarkLength : 0;
++ }
++
++ // height
++ if (isHorizontal) {
++ minSize.height = display && gridLineOpts.drawTicks ? tickMarkLength : 0;
++ } else {
++ minSize.height = me.maxHeight; // fill all the height
++ }
++
++ // Are we showing a title for the scale?
++ if (scaleLabelOpts.display && display) {
++ if (isHorizontal) {
++ minSize.height += (scaleLabelFontSize * 1.5);
++ } else {
++ minSize.width += (scaleLabelFontSize * 1.5);
++ }
++ }
++
++ if (tickOpts.display && display) {
++ // Don't bother fitting the ticks if we are not showing them
++ if (!me.longestTextCache) {
++ me.longestTextCache = {};
++ }
++
++ var largestTextWidth = helpers.longestText(me.ctx, tickLabelFont, me.ticks, me.longestTextCache);
++ var tallestLabelHeightInLines = helpers.numberOfLabelLines(me.ticks);
++ var lineSpace = tickFontSize * 0.5;
++
++ if (isHorizontal) {
++ // A horizontal axis is more constrained by the height.
++ me.longestLabelWidth = largestTextWidth;
++
++ // TODO - improve this calculation
++ var labelHeight = (Math.sin(helpers.toRadians(me.labelRotation)) * me.longestLabelWidth) + (tickFontSize * tallestLabelHeightInLines) + (lineSpace * tallestLabelHeightInLines);
++
++ minSize.height = Math.min(me.maxHeight, minSize.height + labelHeight);
++ me.ctx.font = tickLabelFont;
++
++ var firstLabelWidth = me.ctx.measureText(me.ticks[0]).width;
++ var lastLabelWidth = me.ctx.measureText(me.ticks[me.ticks.length - 1]).width;
++
++ // Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned which means that the right padding is dominated
++ // by the font height
++ var cosRotation = Math.cos(helpers.toRadians(me.labelRotation));
++ var sinRotation = Math.sin(helpers.toRadians(me.labelRotation));
++ me.paddingLeft = me.labelRotation !== 0 ? (cosRotation * firstLabelWidth) + 3 : firstLabelWidth / 2 + 3; // add 3 px to move away from canvas edges
++ me.paddingRight = me.labelRotation !== 0 ? (sinRotation * (tickFontSize / 2)) + 3 : lastLabelWidth / 2 + 3; // when rotated
++ } else {
++ // A vertical axis is more constrained by the width. Labels are the dominant factor here, so get that length first
++ var maxLabelWidth = me.maxWidth - minSize.width;
++
++ // Account for padding
++ var mirror = tickOpts.mirror;
++ if (!mirror) {
++ largestTextWidth += me.options.ticks.padding;
++ } else {
++ // If mirrored text is on the inside so don't expand
++ largestTextWidth = 0;
++ }
++
++ if (largestTextWidth < maxLabelWidth) {
++ // We don't need all the room
++ minSize.width += largestTextWidth;
++ } else {
++ // Expand to max size
++ minSize.width = me.maxWidth;
++ }
++
++ me.paddingTop = tickFontSize / 2;
++ me.paddingBottom = tickFontSize / 2;
++ }
++ }
++
++ if (me.margins) {
++ me.paddingLeft = Math.max(me.paddingLeft - me.margins.left, 0);
++ me.paddingTop = Math.max(me.paddingTop - me.margins.top, 0);
++ me.paddingRight = Math.max(me.paddingRight - me.margins.right, 0);
++ me.paddingBottom = Math.max(me.paddingBottom - me.margins.bottom, 0);
++ }
++
++ me.width = minSize.width;
++ me.height = minSize.height;
++
++ },
++ afterFit: function() {
++ helpers.callCallback(this.options.afterFit, [this]);
++ },
++
++ // Shared Methods
++ isHorizontal: function() {
++ return this.options.position === 'top' || this.options.position === 'bottom';
++ },
++ isFullWidth: function() {
++ return (this.options.fullWidth);
++ },
++
++ // Get the correct value. NaN bad inputs, If the value type is object get the x or y based on whether we are horizontal or not
++ getRightValue: function(rawValue) {
++ // Null and undefined values first
++ if (rawValue === null || typeof(rawValue) === 'undefined') {
++ return NaN;
++ }
++ // isNaN(object) returns true, so make sure NaN is checking for a number; Discard Infinite values
++ if (typeof(rawValue) === 'number' && !isFinite(rawValue)) {
++ return NaN;
++ }
++ // If it is in fact an object, dive in one more level
++ if (typeof(rawValue) === 'object') {
++ if ((rawValue instanceof Date) || (rawValue.isValid)) {
++ return rawValue;
++ }
++ return this.getRightValue(this.isHorizontal() ? rawValue.x : rawValue.y);
++ }
++
++ // Value is good, return it
++ return rawValue;
++ },
++
++ // Used to get the value to display in the tooltip for the data at the given index
++ // function getLabelForIndex(index, datasetIndex)
++ getLabelForIndex: helpers.noop,
++
++ // Used to get data value locations. Value can either be an index or a numerical value
++ getPixelForValue: helpers.noop,
++
++ // Used to get the data value from a given pixel. This is the inverse of getPixelForValue
++ getValueForPixel: helpers.noop,
++
++ // Used for tick location, should
++ getPixelForTick: function(index, includeOffset) {
++ var me = this;
++ if (me.isHorizontal()) {
++ var innerWidth = me.width - (me.paddingLeft + me.paddingRight);
++ var tickWidth = innerWidth / Math.max((me.ticks.length - ((me.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
++ var pixel = (tickWidth * index) + me.paddingLeft;
++
++ if (includeOffset) {
++ pixel += tickWidth / 2;
++ }
++
++ var finalVal = me.left + Math.round(pixel);
++ finalVal += me.isFullWidth() ? me.margins.left : 0;
++ return finalVal;
++ }
++ var innerHeight = me.height - (me.paddingTop + me.paddingBottom);
++ return me.top + (index * (innerHeight / (me.ticks.length - 1)));
++ },
++
++ // Utility for getting the pixel location of a percentage of scale
++ getPixelForDecimal: function(decimal /* , includeOffset*/) {
++ var me = this;
++ if (me.isHorizontal()) {
++ var innerWidth = me.width - (me.paddingLeft + me.paddingRight);
++ var valueOffset = (innerWidth * decimal) + me.paddingLeft;
++
++ var finalVal = me.left + Math.round(valueOffset);
++ finalVal += me.isFullWidth() ? me.margins.left : 0;
++ return finalVal;
++ }
++ return me.top + (decimal * me.height);
++ },
++
++ getBasePixel: function() {
++ var me = this;
++ var min = me.min;
++ var max = me.max;
++
++ return me.getPixelForValue(
++ me.beginAtZero? 0:
++ min < 0 && max < 0? max :
++ min > 0 && max > 0? min :
++ 0);
++ },
++
++ // Actually draw the scale on the canvas
++ // @param {rectangle} chartArea : the area of the chart to draw full grid lines on
++ draw: function(chartArea) {
++ var me = this;
++ var options = me.options;
++ if (!options.display) {
++ return;
++ }
++
++ var context = me.ctx;
++ var globalDefaults = Chart.defaults.global;
++ var optionTicks = options.ticks;
++ var gridLines = options.gridLines;
++ var scaleLabel = options.scaleLabel;
++
++ var isRotated = me.labelRotation !== 0;
++ var skipRatio;
++ var useAutoskipper = optionTicks.autoSkip;
++ var isHorizontal = me.isHorizontal();
++
++ // figure out the maximum number of gridlines to show
++ var maxTicks;
++ if (optionTicks.maxTicksLimit) {
++ maxTicks = optionTicks.maxTicksLimit;
++ }
++
++ var tickFontColor = helpers.getValueOrDefault(optionTicks.fontColor, globalDefaults.defaultFontColor);
++ var tickFontSize = helpers.getValueOrDefault(optionTicks.fontSize, globalDefaults.defaultFontSize);
++ var tickFontStyle = helpers.getValueOrDefault(optionTicks.fontStyle, globalDefaults.defaultFontStyle);
++ var tickFontFamily = helpers.getValueOrDefault(optionTicks.fontFamily, globalDefaults.defaultFontFamily);
++ var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);
++ var tl = gridLines.tickMarkLength;
++ var borderDash = helpers.getValueOrDefault(gridLines.borderDash, globalDefaults.borderDash);
++ var borderDashOffset = helpers.getValueOrDefault(gridLines.borderDashOffset, globalDefaults.borderDashOffset);
++
++ var scaleLabelFontColor = helpers.getValueOrDefault(scaleLabel.fontColor, globalDefaults.defaultFontColor);
++ var scaleLabelFontSize = helpers.getValueOrDefault(scaleLabel.fontSize, globalDefaults.defaultFontSize);
++ var scaleLabelFontStyle = helpers.getValueOrDefault(scaleLabel.fontStyle, globalDefaults.defaultFontStyle);
++ var scaleLabelFontFamily = helpers.getValueOrDefault(scaleLabel.fontFamily, globalDefaults.defaultFontFamily);
++ var scaleLabelFont = helpers.fontString(scaleLabelFontSize, scaleLabelFontStyle, scaleLabelFontFamily);
++
++ var labelRotationRadians = helpers.toRadians(me.labelRotation);
++ var cosRotation = Math.cos(labelRotationRadians);
++ var longestRotatedLabel = me.longestLabelWidth * cosRotation;
++
++ // Make sure we draw text in the correct color and font
++ context.fillStyle = tickFontColor;
++
++ var itemsToDraw = [];
++
++ if (isHorizontal) {
++ skipRatio = false;
++
++ // Only calculate the skip ratio with the half width of longestRotateLabel if we got an actual rotation
++ // See #2584
++ if (isRotated) {
++ longestRotatedLabel /= 2;
++ }
++
++ if ((longestRotatedLabel + optionTicks.autoSkipPadding) * me.ticks.length > (me.width - (me.paddingLeft + me.paddingRight))) {
++ skipRatio = 1 + Math.floor(((longestRotatedLabel + optionTicks.autoSkipPadding) * me.ticks.length) / (me.width - (me.paddingLeft + me.paddingRight)));
++ }
++
++ // if they defined a max number of optionTicks,
++ // increase skipRatio until that number is met
++ if (maxTicks && me.ticks.length > maxTicks) {
++ while (!skipRatio || me.ticks.length / (skipRatio || 1) > maxTicks) {
++ if (!skipRatio) {
++ skipRatio = 1;
++ }
++ skipRatio += 1;
++ }
++ }
++
++ if (!useAutoskipper) {
++ skipRatio = false;
++ }
++ }
++
++
++ var xTickStart = options.position === 'right' ? me.left : me.right - tl;
++ var xTickEnd = options.position === 'right' ? me.left + tl : me.right;
++ var yTickStart = options.position === 'bottom' ? me.top : me.bottom - tl;
++ var yTickEnd = options.position === 'bottom' ? me.top + tl : me.bottom;
++
++ helpers.each(me.ticks, function(label, index) {
++ // If the callback returned a null or undefined value, do not draw this line
++ if (label === undefined || label === null) {
++ return;
++ }
++
++ var isLastTick = me.ticks.length === index + 1;
++
++ // Since we always show the last tick,we need may need to hide the last shown one before
++ var shouldSkip = (skipRatio > 1 && index % skipRatio > 0) || (index % skipRatio === 0 && index + skipRatio >= me.ticks.length);
++ if (shouldSkip && !isLastTick || (label === undefined || label === null)) {
++ return;
++ }
++
++ var lineWidth, lineColor;
++ if (index === (typeof me.zeroLineIndex !== 'undefined' ? me.zeroLineIndex : 0)) {
++ // Draw the first index specially
++ lineWidth = gridLines.zeroLineWidth;
++ lineColor = gridLines.zeroLineColor;
++ } else {
++ lineWidth = helpers.getValueAtIndexOrDefault(gridLines.lineWidth, index);
++ lineColor = helpers.getValueAtIndexOrDefault(gridLines.color, index);
++ }
++
++ // Common properties
++ var tx1, ty1, tx2, ty2, x1, y1, x2, y2, labelX, labelY;
++ var textAlign = 'middle';
++ var textBaseline = 'middle';
++
++ if (isHorizontal) {
++ if (!isRotated) {
++ textBaseline = options.position === 'top' ? 'bottom' : 'top';
++ }
++
++ textAlign = isRotated ? 'right' : 'center';
++
++ var xLineValue = me.getPixelForTick(index) + helpers.aliasPixel(lineWidth); // xvalues for grid lines
++ labelX = me.getPixelForTick(index, gridLines.offsetGridLines) + optionTicks.labelOffset; // x values for optionTicks (need to consider offsetLabel option)
++ labelY = (isRotated) ? me.top + 12 : options.position === 'top' ? me.bottom - tl : me.top + tl;
++
++ tx1 = tx2 = x1 = x2 = xLineValue;
++ ty1 = yTickStart;
++ ty2 = yTickEnd;
++ y1 = chartArea.top;
++ y2 = chartArea.bottom;
++ } else {
++ if (options.position === 'left') {
++ if (optionTicks.mirror) {
++ labelX = me.right + optionTicks.padding;
++ textAlign = 'left';
++ } else {
++ labelX = me.right - optionTicks.padding;
++ textAlign = 'right';
++ }
++ // right side
++ } else if (optionTicks.mirror) {
++ labelX = me.left - optionTicks.padding;
++ textAlign = 'right';
++ } else {
++ labelX = me.left + optionTicks.padding;
++ textAlign = 'left';
++ }
++
++ var yLineValue = me.getPixelForTick(index); // xvalues for grid lines
++ yLineValue += helpers.aliasPixel(lineWidth);
++ labelY = me.getPixelForTick(index, gridLines.offsetGridLines);
++
++ tx1 = xTickStart;
++ tx2 = xTickEnd;
++ x1 = chartArea.left;
++ x2 = chartArea.right;
++ ty1 = ty2 = y1 = y2 = yLineValue;
++ }
++
++ itemsToDraw.push({
++ tx1: tx1,
++ ty1: ty1,
++ tx2: tx2,
++ ty2: ty2,
++ x1: x1,
++ y1: y1,
++ x2: x2,
++ y2: y2,
++ labelX: labelX,
++ labelY: labelY,
++ glWidth: lineWidth,
++ glColor: lineColor,
++ glBorderDash: borderDash,
++ glBorderDashOffset: borderDashOffset,
++ rotation: -1 * labelRotationRadians,
++ label: label,
++ textBaseline: textBaseline,
++ textAlign: textAlign
++ });
++ });
++
++ // Draw all of the tick labels, tick marks, and grid lines at the correct places
++ helpers.each(itemsToDraw, function(itemToDraw) {
++ if (gridLines.display) {
++ context.save();
++ context.lineWidth = itemToDraw.glWidth;
++ context.strokeStyle = itemToDraw.glColor;
++ if (context.setLineDash) {
++ context.setLineDash(itemToDraw.glBorderDash);
++ context.lineDashOffset = itemToDraw.glBorderDashOffset;
++ }
++
++ context.beginPath();
++
++ if (gridLines.drawTicks) {
++ context.moveTo(itemToDraw.tx1, itemToDraw.ty1);
++ context.lineTo(itemToDraw.tx2, itemToDraw.ty2);
++ }
++
++ if (gridLines.drawOnChartArea) {
++ context.moveTo(itemToDraw.x1, itemToDraw.y1);
++ context.lineTo(itemToDraw.x2, itemToDraw.y2);
++ }
++
++ context.stroke();
++ context.restore();
++ }
++
++ if (optionTicks.display) {
++ context.save();
++ context.translate(itemToDraw.labelX, itemToDraw.labelY);
++ context.rotate(itemToDraw.rotation);
++ context.font = tickLabelFont;
++ context.textBaseline = itemToDraw.textBaseline;
++ context.textAlign = itemToDraw.textAlign;
++
++ var label = itemToDraw.label;
++ if (helpers.isArray(label)) {
++ for (var i = 0, y = -(label.length - 1)*tickFontSize*0.75; i < label.length; ++i) {
++ // We just make sure the multiline element is a string here..
++ context.fillText('' + label[i], 0, y);
++ // apply same lineSpacing as calculated @ L#320
++ y += (tickFontSize * 1.5);
++ }
++ } else {
++ context.fillText(label, 0, 0);
++ }
++ context.restore();
++ }
++ });
++
++ if (scaleLabel.display) {
++ // Draw the scale label
++ var scaleLabelX;
++ var scaleLabelY;
++ var rotation = 0;
++
++ if (isHorizontal) {
++ scaleLabelX = me.left + ((me.right - me.left) / 2); // midpoint of the width
++ scaleLabelY = options.position === 'bottom' ? me.bottom - (scaleLabelFontSize / 2) : me.top + (scaleLabelFontSize / 2);
++ } else {
++ var isLeft = options.position === 'left';
++ scaleLabelX = isLeft ? me.left + (scaleLabelFontSize / 2) : me.right - (scaleLabelFontSize / 2);
++ scaleLabelY = me.top + ((me.bottom - me.top) / 2);
++ rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI;
++ }
++
++ context.save();
++ context.translate(scaleLabelX, scaleLabelY);
++ context.rotate(rotation);
++ context.textAlign = 'center';
++ context.textBaseline = 'middle';
++ context.fillStyle = scaleLabelFontColor; // render in correct colour
++ context.font = scaleLabelFont;
++ context.fillText(scaleLabel.labelString, 0, 0);
++ context.restore();
++ }
++
++ if (gridLines.drawBorder) {
++ // Draw the line at the edge of the axis
++ context.lineWidth = helpers.getValueAtIndexOrDefault(gridLines.lineWidth, 0);
++ context.strokeStyle = helpers.getValueAtIndexOrDefault(gridLines.color, 0);
++ var x1 = me.left,
++ x2 = me.right,
++ y1 = me.top,
++ y2 = me.bottom;
++
++ var aliasPixel = helpers.aliasPixel(context.lineWidth);
++ if (isHorizontal) {
++ y1 = y2 = options.position === 'top' ? me.bottom : me.top;
++ y1 += aliasPixel;
++ y2 += aliasPixel;
++ } else {
++ x1 = x2 = options.position === 'left' ? me.right : me.left;
++ x1 += aliasPixel;
++ x2 += aliasPixel;
++ }
++
++ context.beginPath();
++ context.moveTo(x1, y1);
++ context.lineTo(x2, y2);
++ context.stroke();
++ }
++ }
++ });
++};
++
++},{}],33:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++
++ var helpers = Chart.helpers;
++
++ Chart.scaleService = {
++ // Scale registration object. Extensions can register new scale types (such as log or DB scales) and then
++ // use the new chart options to grab the correct scale
++ constructors: {},
++ // Use a registration function so that we can move to an ES6 map when we no longer need to support
++ // old browsers
++
++ // Scale config defaults
++ defaults: {},
++ registerScaleType: function(type, scaleConstructor, defaults) {
++ this.constructors[type] = scaleConstructor;
++ this.defaults[type] = helpers.clone(defaults);
++ },
++ getScaleConstructor: function(type) {
++ return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined;
++ },
++ getScaleDefaults: function(type) {
++ // Return the scale defaults merged with the global settings so that we always use the latest ones
++ return this.defaults.hasOwnProperty(type) ? helpers.scaleMerge(Chart.defaults.scale, this.defaults[type]) : {};
++ },
++ updateScaleDefaults: function(type, additions) {
++ var defaults = this.defaults;
++ if (defaults.hasOwnProperty(type)) {
++ defaults[type] = helpers.extend(defaults[type], additions);
++ }
++ },
++ addScalesToLayout: function(chartInstance) {
++ // Adds each scale to the chart.boxes array to be sized accordingly
++ helpers.each(chartInstance.scales, function(scale) {
++ Chart.layoutService.addBox(chartInstance, scale);
++ });
++ }
++ };
++};
++
++},{}],34:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++
++ var helpers = Chart.helpers;
++
++ /**
++ * Namespace to hold static tick generation functions
++ * @namespace Chart.Ticks
++ */
++ Chart.Ticks = {
++ /**
++ * Namespace to hold generators for different types of ticks
++ * @namespace Chart.Ticks.generators
++ */
++ generators: {
++ /**
++ * Interface for the options provided to the numeric tick generator
++ * @interface INumericTickGenerationOptions
++ */
++ /**
++ * The maximum number of ticks to display
++ * @name INumericTickGenerationOptions#maxTicks
++ * @type Number
++ */
++ /**
++ * The distance between each tick.
++ * @name INumericTickGenerationOptions#stepSize
++ * @type Number
++ * @optional
++ */
++ /**
++ * Forced minimum for the ticks. If not specified, the minimum of the data range is used to calculate the tick minimum
++ * @name INumericTickGenerationOptions#min
++ * @type Number
++ * @optional
++ */
++ /**
++ * The maximum value of the ticks. If not specified, the maximum of the data range is used to calculate the tick maximum
++ * @name INumericTickGenerationOptions#max
++ * @type Number
++ * @optional
++ */
++
++ /**
++ * Generate a set of linear ticks
++ * @method Chart.Ticks.generators.linear
++ * @param generationOptions {INumericTickGenerationOptions} the options used to generate the ticks
++ * @param dataRange {IRange} the range of the data
++ * @returns {Array<Number>} array of tick values
++ */
++ linear: function(generationOptions, dataRange) {
++ var ticks = [];
++ // To get a "nice" value for the tick spacing, we will use the appropriately named
++ // "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
++ // for details.
++
++ var spacing;
++ if (generationOptions.stepSize && generationOptions.stepSize > 0) {
++ spacing = generationOptions.stepSize;
++ } else {
++ var niceRange = helpers.niceNum(dataRange.max - dataRange.min, false);
++ spacing = helpers.niceNum(niceRange / (generationOptions.maxTicks - 1), true);
++ }
++ var niceMin = Math.floor(dataRange.min / spacing) * spacing;
++ var niceMax = Math.ceil(dataRange.max / spacing) * spacing;
++
++ // If min, max and stepSize is set and they make an evenly spaced scale use it.
++ if (generationOptions.min && generationOptions.max && generationOptions.stepSize) {
++ var minMaxDeltaDivisibleByStepSize = ((generationOptions.max - generationOptions.min) % generationOptions.stepSize) === 0;
++ if (minMaxDeltaDivisibleByStepSize) {
++ niceMin = generationOptions.min;
++ niceMax = generationOptions.max;
++ }
++ }
++
++ var numSpaces = (niceMax - niceMin) / spacing;
++ // If very close to our rounded value, use it.
++ if (helpers.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) {
++ numSpaces = Math.round(numSpaces);
++ } else {
++ numSpaces = Math.ceil(numSpaces);
++ }
++
++ // Put the values into the ticks array
++ ticks.push(generationOptions.min !== undefined ? generationOptions.min : niceMin);
++ for (var j = 1; j < numSpaces; ++j) {
++ ticks.push(niceMin + (j * spacing));
++ }
++ ticks.push(generationOptions.max !== undefined ? generationOptions.max : niceMax);
++
++ return ticks;
++ },
++
++ /**
++ * Generate a set of logarithmic ticks
++ * @method Chart.Ticks.generators.logarithmic
++ * @param generationOptions {INumericTickGenerationOptions} the options used to generate the ticks
++ * @param dataRange {IRange} the range of the data
++ * @returns {Array<Number>} array of tick values
++ */
++ logarithmic: function(generationOptions, dataRange) {
++ var ticks = [];
++ var getValueOrDefault = helpers.getValueOrDefault;
++
++ // Figure out what the max number of ticks we can support it is based on the size of
++ // the axis area. For now, we say that the minimum tick spacing in pixels must be 50
++ // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
++ // the graph
++ var tickVal = getValueOrDefault(generationOptions.min, Math.pow(10, Math.floor(helpers.log10(dataRange.min))));
++
++ while (tickVal < dataRange.max) {
++ ticks.push(tickVal);
++
++ var exp;
++ var significand;
++
++ if (tickVal === 0) {
++ exp = Math.floor(helpers.log10(dataRange.minNotZero));
++ significand = Math.round(dataRange.minNotZero / Math.pow(10, exp));
++ } else {
++ exp = Math.floor(helpers.log10(tickVal));
++ significand = Math.floor(tickVal / Math.pow(10, exp)) + 1;
++ }
++
++ if (significand === 10) {
++ significand = 1;
++ ++exp;
++ }
++
++ tickVal = significand * Math.pow(10, exp);
++ }
++
++ var lastTick = getValueOrDefault(generationOptions.max, tickVal);
++ ticks.push(lastTick);
++
++ return ticks;
++ }
++ },
++
++ /**
++ * Namespace to hold formatters for different types of ticks
++ * @namespace Chart.Ticks.formatters
++ */
++ formatters: {
++ /**
++ * Formatter for value labels
++ * @method Chart.Ticks.formatters.values
++ * @param value the value to display
++ * @return {String|Array} the label to display
++ */
++ values: function(value) {
++ return helpers.isArray(value) ? value : '' + value;
++ },
++
++ /**
++ * Formatter for linear numeric ticks
++ * @method Chart.Ticks.formatters.linear
++ * @param tickValue {Number} the value to be formatted
++ * @param index {Number} the position of the tickValue parameter in the ticks array
++ * @param ticks {Array<Number>} the list of ticks being converted
++ * @return {String} string representation of the tickValue parameter
++ */
++ linear: function(tickValue, index, ticks) {
++ // If we have lots of ticks, don't use the ones
++ var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0];
++
++ // If we have a number like 2.5 as the delta, figure out how many decimal places we need
++ if (Math.abs(delta) > 1) {
++ if (tickValue !== Math.floor(tickValue)) {
++ // not an integer
++ delta = tickValue - Math.floor(tickValue);
++ }
++ }
++
++ var logDelta = helpers.log10(Math.abs(delta));
++ var tickString = '';
++
++ if (tickValue !== 0) {
++ var numDecimal = -1 * Math.floor(logDelta);
++ numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places
++ tickString = tickValue.toFixed(numDecimal);
++ } else {
++ tickString = '0'; // never show decimal places for 0
++ }
++
++ return tickString;
++ },
++
++ logarithmic: function(tickValue, index, ticks) {
++ var remain = tickValue / (Math.pow(10, Math.floor(helpers.log10(tickValue))));
++
++ if (tickValue === 0) {
++ return '0';
++ } else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === ticks.length - 1) {
++ return tickValue.toExponential();
++ }
++ return '';
++ }
++ }
++ };
++};
++
++},{}],35:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++
++ var helpers = Chart.helpers;
++
++ Chart.defaults.global.title = {
++ display: false,
++ position: 'top',
++ fullWidth: true, // marks that this box should take the full width of the canvas (pushing down other boxes)
++
++ fontStyle: 'bold',
++ padding: 10,
++
++ // actual title
++ text: ''
++ };
++
++ var noop = helpers.noop;
++ Chart.Title = Chart.Element.extend({
++
++ initialize: function(config) {
++ var me = this;
++ helpers.extend(me, config);
++ me.options = helpers.configMerge(Chart.defaults.global.title, config.options);
++
++ // Contains hit boxes for each dataset (in dataset order)
++ me.legendHitBoxes = [];
++ },
++
++ // These methods are ordered by lifecycle. Utilities then follow.
++
++ beforeUpdate: function() {
++ var chartOpts = this.chart.options;
++ if (chartOpts && chartOpts.title) {
++ this.options = helpers.configMerge(Chart.defaults.global.title, chartOpts.title);
++ }
++ },
++ update: function(maxWidth, maxHeight, margins) {
++ var me = this;
++
++ // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
++ me.beforeUpdate();
++
++ // Absorb the master measurements
++ me.maxWidth = maxWidth;
++ me.maxHeight = maxHeight;
++ me.margins = margins;
++
++ // Dimensions
++ me.beforeSetDimensions();
++ me.setDimensions();
++ me.afterSetDimensions();
++ // Labels
++ me.beforeBuildLabels();
++ me.buildLabels();
++ me.afterBuildLabels();
++
++ // Fit
++ me.beforeFit();
++ me.fit();
++ me.afterFit();
++ //
++ me.afterUpdate();
++
++ return me.minSize;
++
++ },
++ afterUpdate: noop,
++
++ //
++
++ beforeSetDimensions: noop,
++ setDimensions: function() {
++ var me = this;
++ // Set the unconstrained dimension before label rotation
++ if (me.isHorizontal()) {
++ // Reset position before calculating rotation
++ me.width = me.maxWidth;
++ me.left = 0;
++ me.right = me.width;
++ } else {
++ me.height = me.maxHeight;
++
++ // Reset position before calculating rotation
++ me.top = 0;
++ me.bottom = me.height;
++ }
++
++ // Reset padding
++ me.paddingLeft = 0;
++ me.paddingTop = 0;
++ me.paddingRight = 0;
++ me.paddingBottom = 0;
++
++ // Reset minSize
++ me.minSize = {
++ width: 0,
++ height: 0
++ };
++ },
++ afterSetDimensions: noop,
++
++ //
++
++ beforeBuildLabels: noop,
++ buildLabels: noop,
++ afterBuildLabels: noop,
++
++ //
++
++ beforeFit: noop,
++ fit: function() {
++ var me = this,
++ valueOrDefault = helpers.getValueOrDefault,
++ opts = me.options,
++ globalDefaults = Chart.defaults.global,
++ display = opts.display,
++ fontSize = valueOrDefault(opts.fontSize, globalDefaults.defaultFontSize),
++ minSize = me.minSize;
++
++ if (me.isHorizontal()) {
++ minSize.width = me.maxWidth; // fill all the width
++ minSize.height = display ? fontSize + (opts.padding * 2) : 0;
++ } else {
++ minSize.width = display ? fontSize + (opts.padding * 2) : 0;
++ minSize.height = me.maxHeight; // fill all the height
++ }
++
++ me.width = minSize.width;
++ me.height = minSize.height;
++
++ },
++ afterFit: noop,
++
++ // Shared Methods
++ isHorizontal: function() {
++ var pos = this.options.position;
++ return pos === 'top' || pos === 'bottom';
++ },
++
++ // Actually draw the title block on the canvas
++ draw: function() {
++ var me = this,
++ ctx = me.ctx,
++ valueOrDefault = helpers.getValueOrDefault,
++ opts = me.options,
++ globalDefaults = Chart.defaults.global;
++
++ if (opts.display) {
++ var fontSize = valueOrDefault(opts.fontSize, globalDefaults.defaultFontSize),
++ fontStyle = valueOrDefault(opts.fontStyle, globalDefaults.defaultFontStyle),
++ fontFamily = valueOrDefault(opts.fontFamily, globalDefaults.defaultFontFamily),
++ titleFont = helpers.fontString(fontSize, fontStyle, fontFamily),
++ rotation = 0,
++ titleX,
++ titleY,
++ top = me.top,
++ left = me.left,
++ bottom = me.bottom,
++ right = me.right,
++ maxWidth;
++
++ ctx.fillStyle = valueOrDefault(opts.fontColor, globalDefaults.defaultFontColor); // render in correct colour
++ ctx.font = titleFont;
++
++ // Horizontal
++ if (me.isHorizontal()) {
++ titleX = left + ((right - left) / 2); // midpoint of the width
++ titleY = top + ((bottom - top) / 2); // midpoint of the height
++ maxWidth = right - left;
++ } else {
++ titleX = opts.position === 'left' ? left + (fontSize / 2) : right - (fontSize / 2);
++ titleY = top + ((bottom - top) / 2);
++ maxWidth = bottom - top;
++ rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5);
++ }
++
++ ctx.save();
++ ctx.translate(titleX, titleY);
++ ctx.rotate(rotation);
++ ctx.textAlign = 'center';
++ ctx.textBaseline = 'middle';
++ ctx.fillText(opts.text, 0, 0, maxWidth);
++ ctx.restore();
++ }
++ }
++ });
++
++ // Register the title plugin
++ Chart.plugins.register({
++ beforeInit: function(chartInstance) {
++ var opts = chartInstance.options;
++ var titleOpts = opts.title;
++
++ if (titleOpts) {
++ chartInstance.titleBlock = new Chart.Title({
++ ctx: chartInstance.chart.ctx,
++ options: titleOpts,
++ chart: chartInstance
++ });
++
++ Chart.layoutService.addBox(chartInstance, chartInstance.titleBlock);
++ }
++ }
++ });
++};
++
++},{}],36:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++
++ var helpers = Chart.helpers;
++
++ /**
++ * Helper method to merge the opacity into a color
++ */
++ function mergeOpacity(colorString, opacity) {
++ var color = helpers.color(colorString);
++ return color.alpha(opacity * color.alpha()).rgbaString();
++ }
++
++ Chart.defaults.global.tooltips = {
++ enabled: true,
++ custom: null,
++ mode: 'nearest',
++ position: 'average',
++ intersect: true,
++ backgroundColor: 'rgba(0,0,0,0.8)',
++ titleFontStyle: 'bold',
++ titleSpacing: 2,
++ titleMarginBottom: 6,
++ titleFontColor: '#fff',
++ titleAlign: 'left',
++ bodySpacing: 2,
++ bodyFontColor: '#fff',
++ bodyAlign: 'left',
++ footerFontStyle: 'bold',
++ footerSpacing: 2,
++ footerMarginTop: 6,
++ footerFontColor: '#fff',
++ footerAlign: 'left',
++ yPadding: 6,
++ xPadding: 6,
++ caretSize: 5,
++ cornerRadius: 6,
++ multiKeyBackground: '#fff',
++ displayColors: true,
++ callbacks: {
++ // Args are: (tooltipItems, data)
++ beforeTitle: helpers.noop,
++ title: function(tooltipItems, data) {
++ // Pick first xLabel for now
++ var title = '';
++ var labels = data.labels;
++ var labelCount = labels ? labels.length : 0;
++
++ if (tooltipItems.length > 0) {
++ var item = tooltipItems[0];
++
++ if (item.xLabel) {
++ title = item.xLabel;
++ } else if (labelCount > 0 && item.index < labelCount) {
++ title = labels[item.index];
++ }
++ }
++
++ return title;
++ },
++ afterTitle: helpers.noop,
++
++ // Args are: (tooltipItems, data)
++ beforeBody: helpers.noop,
++
++ // Args are: (tooltipItem, data)
++ beforeLabel: helpers.noop,
++ label: function(tooltipItem, data) {
++ var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || '';
++ return datasetLabel + ': ' + tooltipItem.yLabel;
++ },
++ labelColor: function(tooltipItem, chartInstance) {
++ var meta = chartInstance.getDatasetMeta(tooltipItem.datasetIndex);
++ var activeElement = meta.data[tooltipItem.index];
++ var view = activeElement._view;
++ return {
++ borderColor: view.borderColor,
++ backgroundColor: view.backgroundColor
++ };
++ },
++ afterLabel: helpers.noop,
++
++ // Args are: (tooltipItems, data)
++ afterBody: helpers.noop,
++
++ // Args are: (tooltipItems, data)
++ beforeFooter: helpers.noop,
++ footer: helpers.noop,
++ afterFooter: helpers.noop
++ }
++ };
++
++ // Helper to push or concat based on if the 2nd parameter is an array or not
++ function pushOrConcat(base, toPush) {
++ if (toPush) {
++ if (helpers.isArray(toPush)) {
++ // base = base.concat(toPush);
++ Array.prototype.push.apply(base, toPush);
++ } else {
++ base.push(toPush);
++ }
++ }
++
++ return base;
++ }
++
++ // Private helper to create a tooltip item model
++ // @param element : the chart element (point, arc, bar) to create the tooltip item for
++ // @return : new tooltip item
++ function createTooltipItem(element) {
++ var xScale = element._xScale;
++ var yScale = element._yScale || element._scale; // handle radar || polarArea charts
++ var index = element._index,
++ datasetIndex = element._datasetIndex;
++
++ return {
++ xLabel: xScale ? xScale.getLabelForIndex(index, datasetIndex) : '',
++ yLabel: yScale ? yScale.getLabelForIndex(index, datasetIndex) : '',
++ index: index,
++ datasetIndex: datasetIndex,
++ x: element._model.x,
++ y: element._model.y
++ };
++ }
++
++ /**
++ * Helper to get the reset model for the tooltip
++ * @param tooltipOpts {Object} the tooltip options
++ */
++ function getBaseModel(tooltipOpts) {
++ var globalDefaults = Chart.defaults.global;
++ var getValueOrDefault = helpers.getValueOrDefault;
++
++ return {
++ // Positioning
++ xPadding: tooltipOpts.xPadding,
++ yPadding: tooltipOpts.yPadding,
++ xAlign: tooltipOpts.xAlign,
++ yAlign: tooltipOpts.yAlign,
++
++ // Body
++ bodyFontColor: tooltipOpts.bodyFontColor,
++ _bodyFontFamily: getValueOrDefault(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily),
++ _bodyFontStyle: getValueOrDefault(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle),
++ _bodyAlign: tooltipOpts.bodyAlign,
++ bodyFontSize: getValueOrDefault(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize),
++ bodySpacing: tooltipOpts.bodySpacing,
++
++ // Title
++ titleFontColor: tooltipOpts.titleFontColor,
++ _titleFontFamily: getValueOrDefault(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily),
++ _titleFontStyle: getValueOrDefault(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle),
++ titleFontSize: getValueOrDefault(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize),
++ _titleAlign: tooltipOpts.titleAlign,
++ titleSpacing: tooltipOpts.titleSpacing,
++ titleMarginBottom: tooltipOpts.titleMarginBottom,
++
++ // Footer
++ footerFontColor: tooltipOpts.footerFontColor,
++ _footerFontFamily: getValueOrDefault(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily),
++ _footerFontStyle: getValueOrDefault(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle),
++ footerFontSize: getValueOrDefault(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize),
++ _footerAlign: tooltipOpts.footerAlign,
++ footerSpacing: tooltipOpts.footerSpacing,
++ footerMarginTop: tooltipOpts.footerMarginTop,
++
++ // Appearance
++ caretSize: tooltipOpts.caretSize,
++ cornerRadius: tooltipOpts.cornerRadius,
++ backgroundColor: tooltipOpts.backgroundColor,
++ opacity: 0,
++ legendColorBackground: tooltipOpts.multiKeyBackground,
++ displayColors: tooltipOpts.displayColors
++ };
++ }
++
++ /**
++ * Get the size of the tooltip
++ */
++ function getTooltipSize(tooltip, model) {
++ var ctx = tooltip._chart.ctx;
++
++ var height = model.yPadding * 2; // Tooltip Padding
++ var width = 0;
++
++ // Count of all lines in the body
++ var body = model.body;
++ var combinedBodyLength = body.reduce(function(count, bodyItem) {
++ return count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length;
++ }, 0);
++ combinedBodyLength += model.beforeBody.length + model.afterBody.length;
++
++ var titleLineCount = model.title.length;
++ var footerLineCount = model.footer.length;
++ var titleFontSize = model.titleFontSize,
++ bodyFontSize = model.bodyFontSize,
++ footerFontSize = model.footerFontSize;
++
++ height += titleLineCount * titleFontSize; // Title Lines
++ height += titleLineCount ? (titleLineCount - 1) * model.titleSpacing : 0; // Title Line Spacing
++ height += titleLineCount ? model.titleMarginBottom : 0; // Title's bottom Margin
++ height += combinedBodyLength * bodyFontSize; // Body Lines
++ height += combinedBodyLength ? (combinedBodyLength - 1) * model.bodySpacing : 0; // Body Line Spacing
++ height += footerLineCount ? model.footerMarginTop : 0; // Footer Margin
++ height += footerLineCount * (footerFontSize); // Footer Lines
++ height += footerLineCount ? (footerLineCount - 1) * model.footerSpacing : 0; // Footer Line Spacing
++
++ // Title width
++ var widthPadding = 0;
++ var maxLineWidth = function(line) {
++ width = Math.max(width, ctx.measureText(line).width + widthPadding);
++ };
++
++ ctx.font = helpers.fontString(titleFontSize, model._titleFontStyle, model._titleFontFamily);
++ helpers.each(model.title, maxLineWidth);
++
++ // Body width
++ ctx.font = helpers.fontString(bodyFontSize, model._bodyFontStyle, model._bodyFontFamily);
++ helpers.each(model.beforeBody.concat(model.afterBody), maxLineWidth);
++
++ // Body lines may include some extra width due to the color box
++ widthPadding = model.displayColors ? (bodyFontSize + 2) : 0;
++ helpers.each(body, function(bodyItem) {
++ helpers.each(bodyItem.before, maxLineWidth);
++ helpers.each(bodyItem.lines, maxLineWidth);
++ helpers.each(bodyItem.after, maxLineWidth);
++ });
++
++ // Reset back to 0
++ widthPadding = 0;
++
++ // Footer width
++ ctx.font = helpers.fontString(footerFontSize, model._footerFontStyle, model._footerFontFamily);
++ helpers.each(model.footer, maxLineWidth);
++
++ // Add padding
++ width += 2 * model.xPadding;
++
++ return {
++ width: width,
++ height: height
++ };
++ }
++
++ /**
++ * Helper to get the alignment of a tooltip given the size
++ */
++ function determineAlignment(tooltip, size) {
++ var model = tooltip._model;
++ var chart = tooltip._chart;
++ var chartArea = tooltip._chartInstance.chartArea;
++ var xAlign = 'center';
++ var yAlign = 'center';
++
++ if (model.y < size.height) {
++ yAlign = 'top';
++ } else if (model.y > (chart.height - size.height)) {
++ yAlign = 'bottom';
++ }
++
++ var lf, rf; // functions to determine left, right alignment
++ var olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart
++ var yf; // function to get the y alignment if the tooltip goes outside of the left or right edges
++ var midX = (chartArea.left + chartArea.right) / 2;
++ var midY = (chartArea.top + chartArea.bottom) / 2;
++
++ if (yAlign === 'center') {
++ lf = function(x) {
++ return x <= midX;
++ };
++ rf = function(x) {
++ return x > midX;
++ };
++ } else {
++ lf = function(x) {
++ return x <= (size.width / 2);
++ };
++ rf = function(x) {
++ return x >= (chart.width - (size.width / 2));
++ };
++ }
++
++ olf = function(x) {
++ return x + size.width > chart.width;
++ };
++ orf = function(x) {
++ return x - size.width < 0;
++ };
++ yf = function(y) {
++ return y <= midY ? 'top' : 'bottom';
++ };
++
++ if (lf(model.x)) {
++ xAlign = 'left';
++
++ // Is tooltip too wide and goes over the right side of the chart.?
++ if (olf(model.x)) {
++ xAlign = 'center';
++ yAlign = yf(model.y);
++ }
++ } else if (rf(model.x)) {
++ xAlign = 'right';
++
++ // Is tooltip too wide and goes outside left edge of canvas?
++ if (orf(model.x)) {
++ xAlign = 'center';
++ yAlign = yf(model.y);
++ }
++ }
++
++ var opts = tooltip._options;
++ return {
++ xAlign: opts.xAlign ? opts.xAlign : xAlign,
++ yAlign: opts.yAlign ? opts.yAlign : yAlign
++ };
++ }
++
++ /**
++ * @Helper to get the location a tooltip needs to be placed at given the initial position (via the vm) and the size and alignment
++ */
++ function getBackgroundPoint(vm, size, alignment) {
++ // Background Position
++ var x = vm.x;
++ var y = vm.y;
++
++ var caretSize = vm.caretSize,
++ caretPadding = vm.caretPadding,
++ cornerRadius = vm.cornerRadius,
++ xAlign = alignment.xAlign,
++ yAlign = alignment.yAlign,
++ paddingAndSize = caretSize + caretPadding,
++ radiusAndPadding = cornerRadius + caretPadding;
++
++ if (xAlign === 'right') {
++ x -= size.width;
++ } else if (xAlign === 'center') {
++ x -= (size.width / 2);
++ }
++
++ if (yAlign === 'top') {
++ y += paddingAndSize;
++ } else if (yAlign === 'bottom') {
++ y -= size.height + paddingAndSize;
++ } else {
++ y -= (size.height / 2);
++ }
++
++ if (yAlign === 'center') {
++ if (xAlign === 'left') {
++ x += paddingAndSize;
++ } else if (xAlign === 'right') {
++ x -= paddingAndSize;
++ }
++ } else if (xAlign === 'left') {
++ x -= radiusAndPadding;
++ } else if (xAlign === 'right') {
++ x += radiusAndPadding;
++ }
++
++ return {
++ x: x,
++ y: y
++ };
++ }
++
++ Chart.Tooltip = Chart.Element.extend({
++ initialize: function() {
++ this._model = getBaseModel(this._options);
++ },
++
++ // Get the title
++ // Args are: (tooltipItem, data)
++ getTitle: function() {
++ var me = this;
++ var opts = me._options;
++ var callbacks = opts.callbacks;
++
++ var beforeTitle = callbacks.beforeTitle.apply(me, arguments),
++ title = callbacks.title.apply(me, arguments),
++ afterTitle = callbacks.afterTitle.apply(me, arguments);
++
++ var lines = [];
++ lines = pushOrConcat(lines, beforeTitle);
++ lines = pushOrConcat(lines, title);
++ lines = pushOrConcat(lines, afterTitle);
++
++ return lines;
++ },
++
++ // Args are: (tooltipItem, data)
++ getBeforeBody: function() {
++ var lines = this._options.callbacks.beforeBody.apply(this, arguments);
++ return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : [];
++ },
++
++ // Args are: (tooltipItem, data)
++ getBody: function(tooltipItems, data) {
++ var me = this;
++ var callbacks = me._options.callbacks;
++ var bodyItems = [];
++
++ helpers.each(tooltipItems, function(tooltipItem) {
++ var bodyItem = {
++ before: [],
++ lines: [],
++ after: []
++ };
++ pushOrConcat(bodyItem.before, callbacks.beforeLabel.call(me, tooltipItem, data));
++ pushOrConcat(bodyItem.lines, callbacks.label.call(me, tooltipItem, data));
++ pushOrConcat(bodyItem.after, callbacks.afterLabel.call(me, tooltipItem, data));
++
++ bodyItems.push(bodyItem);
++ });
++
++ return bodyItems;
++ },
++
++ // Args are: (tooltipItem, data)
++ getAfterBody: function() {
++ var lines = this._options.callbacks.afterBody.apply(this, arguments);
++ return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : [];
++ },
++
++ // Get the footer and beforeFooter and afterFooter lines
++ // Args are: (tooltipItem, data)
++ getFooter: function() {
++ var me = this;
++ var callbacks = me._options.callbacks;
++
++ var beforeFooter = callbacks.beforeFooter.apply(me, arguments);
++ var footer = callbacks.footer.apply(me, arguments);
++ var afterFooter = callbacks.afterFooter.apply(me, arguments);
++
++ var lines = [];
++ lines = pushOrConcat(lines, beforeFooter);
++ lines = pushOrConcat(lines, footer);
++ lines = pushOrConcat(lines, afterFooter);
++
++ return lines;
++ },
++
++ update: function(changed) {
++ var me = this;
++ var opts = me._options;
++
++ // Need to regenerate the model because its faster than using extend and it is necessary due to the optimization in Chart.Element.transition
++ // that does _view = _model if ease === 1. This causes the 2nd tooltip update to set properties in both the view and model at the same time
++ // which breaks any animations.
++ var existingModel = me._model;
++ var model = me._model = getBaseModel(opts);
++ var active = me._active;
++
++ var data = me._data;
++ var chartInstance = me._chartInstance;
++
++ // In the case where active.length === 0 we need to keep these at existing values for good animations
++ var alignment = {
++ xAlign: existingModel.xAlign,
++ yAlign: existingModel.yAlign
++ };
++ var backgroundPoint = {
++ x: existingModel.x,
++ y: existingModel.y
++ };
++ var tooltipSize = {
++ width: existingModel.width,
++ height: existingModel.height
++ };
++ var tooltipPosition = {
++ x: existingModel.caretX,
++ y: existingModel.caretY
++ };
++
++ var i, len;
++
++ if (active.length) {
++ model.opacity = 1;
++
++ var labelColors = [];
++ tooltipPosition = Chart.Tooltip.positioners[opts.position](active, me._eventPosition);
++
++ var tooltipItems = [];
++ for (i = 0, len = active.length; i < len; ++i) {
++ tooltipItems.push(createTooltipItem(active[i]));
++ }
++
++ // If the user provided a filter function, use it to modify the tooltip items
++ if (opts.filter) {
++ tooltipItems = tooltipItems.filter(function(a) {
++ return opts.filter(a, data);
++ });
++ }
++
++ // If the user provided a sorting function, use it to modify the tooltip items
++ if (opts.itemSort) {
++ tooltipItems = tooltipItems.sort(function(a, b) {
++ return opts.itemSort(a, b, data);
++ });
++ }
++
++ // Determine colors for boxes
++ helpers.each(tooltipItems, function(tooltipItem) {
++ labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, chartInstance));
++ });
++
++ // Build the Text Lines
++ model.title = me.getTitle(tooltipItems, data);
++ model.beforeBody = me.getBeforeBody(tooltipItems, data);
++ model.body = me.getBody(tooltipItems, data);
++ model.afterBody = me.getAfterBody(tooltipItems, data);
++ model.footer = me.getFooter(tooltipItems, data);
++
++ // Initial positioning and colors
++ model.x = Math.round(tooltipPosition.x);
++ model.y = Math.round(tooltipPosition.y);
++ model.caretPadding = helpers.getValueOrDefault(tooltipPosition.padding, 2);
++ model.labelColors = labelColors;
++
++ // data points
++ model.dataPoints = tooltipItems;
++
++ // We need to determine alignment of the tooltip
++ tooltipSize = getTooltipSize(this, model);
++ alignment = determineAlignment(this, tooltipSize);
++ // Final Size and Position
++ backgroundPoint = getBackgroundPoint(model, tooltipSize, alignment);
++ } else {
++ model.opacity = 0;
++ }
++
++ model.xAlign = alignment.xAlign;
++ model.yAlign = alignment.yAlign;
++ model.x = backgroundPoint.x;
++ model.y = backgroundPoint.y;
++ model.width = tooltipSize.width;
++ model.height = tooltipSize.height;
++
++ // Point where the caret on the tooltip points to
++ model.caretX = tooltipPosition.x;
++ model.caretY = tooltipPosition.y;
++
++ me._model = model;
++
++ if (changed && opts.custom) {
++ opts.custom.call(me, model);
++ }
++
++ return me;
++ },
++ drawCaret: function(tooltipPoint, size, opacity) {
++ var vm = this._view;
++ var ctx = this._chart.ctx;
++ var x1, x2, x3;
++ var y1, y2, y3;
++ var caretSize = vm.caretSize;
++ var cornerRadius = vm.cornerRadius;
++ var xAlign = vm.xAlign,
++ yAlign = vm.yAlign;
++ var ptX = tooltipPoint.x,
++ ptY = tooltipPoint.y;
++ var width = size.width,
++ height = size.height;
++
++ if (yAlign === 'center') {
++ // Left or right side
++ if (xAlign === 'left') {
++ x1 = ptX;
++ x2 = x1 - caretSize;
++ x3 = x1;
++ } else {
++ x1 = ptX + width;
++ x2 = x1 + caretSize;
++ x3 = x1;
++ }
++
++ y2 = ptY + (height / 2);
++ y1 = y2 - caretSize;
++ y3 = y2 + caretSize;
++ } else {
++ if (xAlign === 'left') {
++ x1 = ptX + cornerRadius;
++ x2 = x1 + caretSize;
++ x3 = x2 + caretSize;
++ } else if (xAlign === 'right') {
++ x1 = ptX + width - cornerRadius;
++ x2 = x1 - caretSize;
++ x3 = x2 - caretSize;
++ } else {
++ x2 = ptX + (width / 2);
++ x1 = x2 - caretSize;
++ x3 = x2 + caretSize;
++ }
++
++ if (yAlign === 'top') {
++ y1 = ptY;
++ y2 = y1 - caretSize;
++ y3 = y1;
++ } else {
++ y1 = ptY + height;
++ y2 = y1 + caretSize;
++ y3 = y1;
++ }
++ }
++
++ ctx.fillStyle = mergeOpacity(vm.backgroundColor, opacity);
++ ctx.beginPath();
++ ctx.moveTo(x1, y1);
++ ctx.lineTo(x2, y2);
++ ctx.lineTo(x3, y3);
++ ctx.closePath();
++ ctx.fill();
++ },
++ drawTitle: function(pt, vm, ctx, opacity) {
++ var title = vm.title;
++
++ if (title.length) {
++ ctx.textAlign = vm._titleAlign;
++ ctx.textBaseline = 'top';
++
++ var titleFontSize = vm.titleFontSize,
++ titleSpacing = vm.titleSpacing;
++
++ ctx.fillStyle = mergeOpacity(vm.titleFontColor, opacity);
++ ctx.font = helpers.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily);
++
++ var i, len;
++ for (i = 0, len = title.length; i < len; ++i) {
++ ctx.fillText(title[i], pt.x, pt.y);
++ pt.y += titleFontSize + titleSpacing; // Line Height and spacing
++
++ if (i + 1 === title.length) {
++ pt.y += vm.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing
++ }
++ }
++ }
++ },
++ drawBody: function(pt, vm, ctx, opacity) {
++ var bodyFontSize = vm.bodyFontSize;
++ var bodySpacing = vm.bodySpacing;
++ var body = vm.body;
++
++ ctx.textAlign = vm._bodyAlign;
++ ctx.textBaseline = 'top';
++
++ var textColor = mergeOpacity(vm.bodyFontColor, opacity);
++ ctx.fillStyle = textColor;
++ ctx.font = helpers.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);
++
++ // Before Body
++ var xLinePadding = 0;
++ var fillLineOfText = function(line) {
++ ctx.fillText(line, pt.x + xLinePadding, pt.y);
++ pt.y += bodyFontSize + bodySpacing;
++ };
++
++ // Before body lines
++ helpers.each(vm.beforeBody, fillLineOfText);
++
++ var drawColorBoxes = vm.displayColors;
++ xLinePadding = drawColorBoxes ? (bodyFontSize + 2) : 0;
++
++ // Draw body lines now
++ helpers.each(body, function(bodyItem, i) {
++ helpers.each(bodyItem.before, fillLineOfText);
++
++ helpers.each(bodyItem.lines, function(line) {
++ // Draw Legend-like boxes if needed
++ if (drawColorBoxes) {
++ // Fill a white rect so that colours merge nicely if the opacity is < 1
++ ctx.fillStyle = mergeOpacity(vm.legendColorBackground, opacity);
++ ctx.fillRect(pt.x, pt.y, bodyFontSize, bodyFontSize);
++
++ // Border
++ ctx.strokeStyle = mergeOpacity(vm.labelColors[i].borderColor, opacity);
++ ctx.strokeRect(pt.x, pt.y, bodyFontSize, bodyFontSize);
++
++ // Inner square
++ ctx.fillStyle = mergeOpacity(vm.labelColors[i].backgroundColor, opacity);
++ ctx.fillRect(pt.x + 1, pt.y + 1, bodyFontSize - 2, bodyFontSize - 2);
++
++ ctx.fillStyle = textColor;
++ }
++
++ fillLineOfText(line);
++ });
++
++ helpers.each(bodyItem.after, fillLineOfText);
++ });
++
++ // Reset back to 0 for after body
++ xLinePadding = 0;
++
++ // After body lines
++ helpers.each(vm.afterBody, fillLineOfText);
++ pt.y -= bodySpacing; // Remove last body spacing
++ },
++ drawFooter: function(pt, vm, ctx, opacity) {
++ var footer = vm.footer;
++
++ if (footer.length) {
++ pt.y += vm.footerMarginTop;
++
++ ctx.textAlign = vm._footerAlign;
++ ctx.textBaseline = 'top';
++
++ ctx.fillStyle = mergeOpacity(vm.footerFontColor, opacity);
++ ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily);
++
++ helpers.each(footer, function(line) {
++ ctx.fillText(line, pt.x, pt.y);
++ pt.y += vm.footerFontSize + vm.footerSpacing;
++ });
++ }
++ },
++ drawBackground: function(pt, vm, ctx, tooltipSize, opacity) {
++ ctx.fillStyle = mergeOpacity(vm.backgroundColor, opacity);
++ helpers.drawRoundedRectangle(ctx, pt.x, pt.y, tooltipSize.width, tooltipSize.height, vm.cornerRadius);
++ ctx.fill();
++ },
++ draw: function() {
++ var ctx = this._chart.ctx;
++ var vm = this._view;
++
++ if (vm.opacity === 0) {
++ return;
++ }
++
++ var tooltipSize = {
++ width: vm.width,
++ height: vm.height
++ };
++ var pt = {
++ x: vm.x,
++ y: vm.y
++ };
++
++ // IE11/Edge does not like very small opacities, so snap to 0
++ var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity;
++
++ if (this._options.enabled) {
++ // Draw Background
++ this.drawBackground(pt, vm, ctx, tooltipSize, opacity);
++
++ // Draw Caret
++ this.drawCaret(pt, tooltipSize, opacity);
++
++ // Draw Title, Body, and Footer
++ pt.x += vm.xPadding;
++ pt.y += vm.yPadding;
++
++ // Titles
++ this.drawTitle(pt, vm, ctx, opacity);
++
++ // Body
++ this.drawBody(pt, vm, ctx, opacity);
++
++ // Footer
++ this.drawFooter(pt, vm, ctx, opacity);
++ }
++ },
++
++ /**
++ * Handle an event
++ * @private
++ * @param e {Event} the event to handle
++ * @returns {Boolean} true if the tooltip changed
++ */
++ handleEvent: function(e) {
++ var me = this;
++ var options = me._options;
++ var changed = false;
++
++ me._lastActive = me._lastActive || [];
++
++ // Find Active Elements for tooltips
++ if (e.type === 'mouseout') {
++ me._active = [];
++ } else {
++ me._active = me._chartInstance.getElementsAtEventForMode(e, options.mode, options);
++ }
++
++ // Remember Last Actives
++ changed = !helpers.arrayEquals(me._active, me._lastActive);
++ me._lastActive = me._active;
++
++ if (options.enabled || options.custom) {
++ me._eventPosition = helpers.getRelativePosition(e, me._chart);
++
++ var model = me._model;
++ me.update(true);
++ me.pivot();
++
++ // See if our tooltip position changed
++ changed |= (model.x !== me._model.x) || (model.y !== me._model.y);
++ }
++
++ return changed;
++ }
++ });
++
++ /**
++ * @namespace Chart.Tooltip.positioners
++ */
++ Chart.Tooltip.positioners = {
++ /**
++ * Average mode places the tooltip at the average position of the elements shown
++ * @function Chart.Tooltip.positioners.average
++ * @param elements {ChartElement[]} the elements being displayed in the tooltip
++ * @returns {Point} tooltip position
++ */
++ average: function(elements) {
++ if (!elements.length) {
++ return false;
++ }
++
++ var i, len;
++ var x = 0;
++ var y = 0;
++ var count = 0;
++
++ for (i = 0, len = elements.length; i < len; ++i) {
++ var el = elements[i];
++ if (el && el.hasValue()) {
++ var pos = el.tooltipPosition();
++ x += pos.x;
++ y += pos.y;
++ ++count;
++ }
++ }
++
++ return {
++ x: Math.round(x / count),
++ y: Math.round(y / count)
++ };
++ },
++
++ /**
++ * Gets the tooltip position nearest of the item nearest to the event position
++ * @function Chart.Tooltip.positioners.nearest
++ * @param elements {Chart.Element[]} the tooltip elements
++ * @param eventPosition {Point} the position of the event in canvas coordinates
++ * @returns {Point} the tooltip position
++ */
++ nearest: function(elements, eventPosition) {
++ var x = eventPosition.x;
++ var y = eventPosition.y;
++
++ var nearestElement;
++ var minDistance = Number.POSITIVE_INFINITY;
++ var i, len;
++ for (i = 0, len = elements.length; i < len; ++i) {
++ var el = elements[i];
++ if (el && el.hasValue()) {
++ var center = el.getCenterPoint();
++ var d = helpers.distanceBetweenPoints(eventPosition, center);
++
++ if (d < minDistance) {
++ minDistance = d;
++ nearestElement = el;
++ }
++ }
++ }
++
++ if (nearestElement) {
++ var tp = nearestElement.tooltipPosition();
++ x = tp.x;
++ y = tp.y;
++ }
++
++ return {
++ x: x,
++ y: y
++ };
++ }
++ };
++};
++
++},{}],37:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++
++ var helpers = Chart.helpers,
++ globalOpts = Chart.defaults.global;
++
++ globalOpts.elements.arc = {
++ backgroundColor: globalOpts.defaultColor,
++ borderColor: '#fff',
++ borderWidth: 2
++ };
++
++ Chart.elements.Arc = Chart.Element.extend({
++ inLabelRange: function(mouseX) {
++ var vm = this._view;
++
++ if (vm) {
++ return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2));
++ }
++ return false;
++ },
++ inRange: function(chartX, chartY) {
++ var vm = this._view;
++
++ if (vm) {
++ var pointRelativePosition = helpers.getAngleFromPoint(vm, {
++ x: chartX,
++ y: chartY
++ }),
++ angle = pointRelativePosition.angle,
++ distance = pointRelativePosition.distance;
++
++ // Sanitise angle range
++ var startAngle = vm.startAngle;
++ var endAngle = vm.endAngle;
++ while (endAngle < startAngle) {
++ endAngle += 2.0 * Math.PI;
++ }
++ while (angle > endAngle) {
++ angle -= 2.0 * Math.PI;
++ }
++ while (angle < startAngle) {
++ angle += 2.0 * Math.PI;
++ }
++
++ // Check if within the range of the open/close angle
++ var betweenAngles = (angle >= startAngle && angle <= endAngle),
++ withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius);
++
++ return (betweenAngles && withinRadius);
++ }
++ return false;
++ },
++ getCenterPoint: function() {
++ var vm = this._view;
++ var halfAngle = (vm.startAngle + vm.endAngle) / 2;
++ var halfRadius = (vm.innerRadius + vm.outerRadius) / 2;
++ return {
++ x: vm.x + Math.cos(halfAngle) * halfRadius,
++ y: vm.y + Math.sin(halfAngle) * halfRadius
++ };
++ },
++ getArea: function() {
++ var vm = this._view;
++ return Math.PI * ((vm.endAngle - vm.startAngle) / (2 * Math.PI)) * (Math.pow(vm.outerRadius, 2) - Math.pow(vm.innerRadius, 2));
++ },
++ tooltipPosition: function() {
++ var vm = this._view;
++
++ var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2),
++ rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius;
++ return {
++ x: vm.x + (Math.cos(centreAngle) * rangeFromCentre),
++ y: vm.y + (Math.sin(centreAngle) * rangeFromCentre)
++ };
++ },
++ draw: function() {
++
++ var ctx = this._chart.ctx,
++ vm = this._view,
++ sA = vm.startAngle,
++ eA = vm.endAngle;
++
++ ctx.beginPath();
++
++ ctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA);
++ ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true);
++
++ ctx.closePath();
++ ctx.strokeStyle = vm.borderColor;
++ ctx.lineWidth = vm.borderWidth;
++
++ ctx.fillStyle = vm.backgroundColor;
++
++ ctx.fill();
++ ctx.lineJoin = 'bevel';
++
++ if (vm.borderWidth) {
++ ctx.stroke();
++ }
++ }
++ });
++};
++
++},{}],38:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++
++ var helpers = Chart.helpers;
++ var globalDefaults = Chart.defaults.global;
++
++ Chart.defaults.global.elements.line = {
++ tension: 0.4,
++ backgroundColor: globalDefaults.defaultColor,
++ borderWidth: 3,
++ borderColor: globalDefaults.defaultColor,
++ borderCapStyle: 'butt',
++ borderDash: [],
++ borderDashOffset: 0.0,
++ borderJoinStyle: 'miter',
++ capBezierPoints: true,
++ fill: true, // do we fill in the area between the line and its base axis
++ };
++
++ Chart.elements.Line = Chart.Element.extend({
++ draw: function() {
++ var me = this;
++ var vm = me._view;
++ var spanGaps = vm.spanGaps;
++ var fillPoint = vm.scaleZero;
++ var loop = me._loop;
++
++ // Handle different fill modes for cartesian lines
++ if (!loop) {
++ if (vm.fill === 'top') {
++ fillPoint = vm.scaleTop;
++ } else if (vm.fill === 'bottom') {
++ fillPoint = vm.scaleBottom;
++ }
++ }
++
++ var ctx = me._chart.ctx;
++ ctx.save();
++
++ // Helper function to draw a line to a point
++ function lineToPoint(previousPoint, point) {
++ var pointVM = point._view;
++ if (point._view.steppedLine === true) {
++ ctx.lineTo(pointVM.x, previousPoint._view.y);
++ ctx.lineTo(pointVM.x, pointVM.y);
++ } else if (point._view.tension === 0) {
++ ctx.lineTo(pointVM.x, pointVM.y);
++ } else {
++ ctx.bezierCurveTo(
++ previousPoint._view.controlPointNextX,
++ previousPoint._view.controlPointNextY,
++ pointVM.controlPointPreviousX,
++ pointVM.controlPointPreviousY,
++ pointVM.x,
++ pointVM.y
++ );
++ }
++ }
++
++ var points = me._children.slice(); // clone array
++ var lastDrawnIndex = -1;
++
++ // If we are looping, adding the first point again
++ if (loop && points.length) {
++ points.push(points[0]);
++ }
++
++ var index, current, previous, currentVM;
++
++ // Fill Line
++ if (points.length && vm.fill) {
++ ctx.beginPath();
++
++ for (index = 0; index < points.length; ++index) {
++ current = points[index];
++ previous = helpers.previousItem(points, index);
++ currentVM = current._view;
++
++ // First point moves to it's starting position no matter what
++ if (index === 0) {
++ if (loop) {
++ ctx.moveTo(fillPoint.x, fillPoint.y);
++ } else {
++ ctx.moveTo(currentVM.x, fillPoint);
++ }
++
++ if (!currentVM.skip) {
++ lastDrawnIndex = index;
++ ctx.lineTo(currentVM.x, currentVM.y);
++ }
++ } else {
++ previous = lastDrawnIndex === -1 ? previous : points[lastDrawnIndex];
++
++ if (currentVM.skip) {
++ // Only do this if this is the first point that is skipped
++ if (!spanGaps && lastDrawnIndex === (index - 1)) {
++ if (loop) {
++ ctx.lineTo(fillPoint.x, fillPoint.y);
++ } else {
++ ctx.lineTo(previous._view.x, fillPoint);
++ }
++ }
++ } else {
++ if (lastDrawnIndex !== (index - 1)) {
++ // There was a gap and this is the first point after the gap. If we've never drawn a point, this is a special case.
++ // If the first data point is NaN, then there is no real gap to skip
++ if (spanGaps && lastDrawnIndex !== -1) {
++ // We are spanning the gap, so simple draw a line to this point
++ lineToPoint(previous, current);
++ } else if (loop) {
++ ctx.lineTo(currentVM.x, currentVM.y);
++ } else {
++ ctx.lineTo(currentVM.x, fillPoint);
++ ctx.lineTo(currentVM.x, currentVM.y);
++ }
++ } else {
++ // Line to next point
++ lineToPoint(previous, current);
++ }
++ lastDrawnIndex = index;
++ }
++ }
++ }
++
++ if (!loop && lastDrawnIndex !== -1) {
++ ctx.lineTo(points[lastDrawnIndex]._view.x, fillPoint);
++ }
++
++ ctx.fillStyle = vm.backgroundColor || globalDefaults.defaultColor;
++ ctx.closePath();
++ ctx.fill();
++ }
++
++ // Stroke Line Options
++ var globalOptionLineElements = globalDefaults.elements.line;
++ ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle;
++
++ // IE 9 and 10 do not support line dash
++ if (ctx.setLineDash) {
++ ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash);
++ }
++
++ ctx.lineDashOffset = vm.borderDashOffset || globalOptionLineElements.borderDashOffset;
++ ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle;
++ ctx.lineWidth = vm.borderWidth || globalOptionLineElements.borderWidth;
++ ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor;
++
++ // Stroke Line
++ ctx.beginPath();
++ lastDrawnIndex = -1;
++
++ for (index = 0; index < points.length; ++index) {
++ current = points[index];
++ previous = helpers.previousItem(points, index);
++ currentVM = current._view;
++
++ // First point moves to it's starting position no matter what
++ if (index === 0) {
++ if (!currentVM.skip) {
++ ctx.moveTo(currentVM.x, currentVM.y);
++ lastDrawnIndex = index;
++ }
++ } else {
++ previous = lastDrawnIndex === -1 ? previous : points[lastDrawnIndex];
++
++ if (!currentVM.skip) {
++ if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) {
++ // There was a gap and this is the first point after the gap
++ ctx.moveTo(currentVM.x, currentVM.y);
++ } else {
++ // Line to next point
++ lineToPoint(previous, current);
++ }
++ lastDrawnIndex = index;
++ }
++ }
++ }
++
++ ctx.stroke();
++ ctx.restore();
++ }
++ });
++};
++
++},{}],39:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++
++ var helpers = Chart.helpers,
++ globalOpts = Chart.defaults.global,
++ defaultColor = globalOpts.defaultColor;
++
++ globalOpts.elements.point = {
++ radius: 3,
++ pointStyle: 'circle',
++ backgroundColor: defaultColor,
++ borderWidth: 1,
++ borderColor: defaultColor,
++ // Hover
++ hitRadius: 1,
++ hoverRadius: 4,
++ hoverBorderWidth: 1
++ };
++
++ function xRange(mouseX) {
++ var vm = this._view;
++ return vm ? (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hitRadius, 2)) : false;
++ }
++
++ function yRange(mouseY) {
++ var vm = this._view;
++ return vm ? (Math.pow(mouseY - vm.y, 2) < Math.pow(vm.radius + vm.hitRadius, 2)) : false;
++ }
++
++ Chart.elements.Point = Chart.Element.extend({
++ inRange: function(mouseX, mouseY) {
++ var vm = this._view;
++ return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false;
++ },
++
++ inLabelRange: xRange,
++ inXRange: xRange,
++ inYRange: yRange,
++
++ getCenterPoint: function() {
++ var vm = this._view;
++ return {
++ x: vm.x,
++ y: vm.y
++ };
++ },
++ getArea: function() {
++ return Math.PI * Math.pow(this._view.radius, 2);
++ },
++ tooltipPosition: function() {
++ var vm = this._view;
++ return {
++ x: vm.x,
++ y: vm.y,
++ padding: vm.radius + vm.borderWidth
++ };
++ },
++ draw: function() {
++ var vm = this._view;
++ var ctx = this._chart.ctx;
++ var pointStyle = vm.pointStyle;
++ var radius = vm.radius;
++ var x = vm.x;
++ var y = vm.y;
++
++ if (vm.skip) {
++ return;
++ }
++
++ ctx.strokeStyle = vm.borderColor || defaultColor;
++ ctx.lineWidth = helpers.getValueOrDefault(vm.borderWidth, globalOpts.elements.point.borderWidth);
++ ctx.fillStyle = vm.backgroundColor || defaultColor;
++
++ Chart.canvasHelpers.drawPoint(ctx, pointStyle, radius, x, y);
++ }
++ });
++};
++
++},{}],40:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++
++ var globalOpts = Chart.defaults.global;
++
++ globalOpts.elements.rectangle = {
++ backgroundColor: globalOpts.defaultColor,
++ borderWidth: 0,
++ borderColor: globalOpts.defaultColor,
++ borderSkipped: 'bottom'
++ };
++
++ function isVertical(bar) {
++ return bar._view.width !== undefined;
++ }
++
++ /**
++ * Helper function to get the bounds of the bar regardless of the orientation
++ * @private
++ * @param bar {Chart.Element.Rectangle} the bar
++ * @return {Bounds} bounds of the bar
++ */
++ function getBarBounds(bar) {
++ var vm = bar._view;
++ var x1, x2, y1, y2;
++
++ if (isVertical(bar)) {
++ // vertical
++ var halfWidth = vm.width / 2;
++ x1 = vm.x - halfWidth;
++ x2 = vm.x + halfWidth;
++ y1 = Math.min(vm.y, vm.base);
++ y2 = Math.max(vm.y, vm.base);
++ } else {
++ // horizontal bar
++ var halfHeight = vm.height / 2;
++ x1 = Math.min(vm.x, vm.base);
++ x2 = Math.max(vm.x, vm.base);
++ y1 = vm.y - halfHeight;
++ y2 = vm.y + halfHeight;
++ }
++
++ return {
++ left: x1,
++ top: y1,
++ right: x2,
++ bottom: y2
++ };
++ }
++
++ Chart.elements.Rectangle = Chart.Element.extend({
++ draw: function() {
++ var ctx = this._chart.ctx;
++ var vm = this._view;
++
++ var halfWidth = vm.width / 2,
++ leftX = vm.x - halfWidth,
++ rightX = vm.x + halfWidth,
++ top = vm.base - (vm.base - vm.y),
++ halfStroke = vm.borderWidth / 2;
++
++ // Canvas doesn't allow us to stroke inside the width so we can
++ // adjust the sizes to fit if we're setting a stroke on the line
++ if (vm.borderWidth) {
++ leftX += halfStroke;
++ rightX -= halfStroke;
++ top += halfStroke;
++ }
++
++ ctx.beginPath();
++ ctx.fillStyle = vm.backgroundColor;
++ ctx.strokeStyle = vm.borderColor;
++ ctx.lineWidth = vm.borderWidth;
++
++ // Corner points, from bottom-left to bottom-right clockwise
++ // | 1 2 |
++ // | 0 3 |
++ var corners = [
++ [leftX, vm.base],
++ [leftX, top],
++ [rightX, top],
++ [rightX, vm.base]
++ ];
++
++ // Find first (starting) corner with fallback to 'bottom'
++ var borders = ['bottom', 'left', 'top', 'right'];
++ var startCorner = borders.indexOf(vm.borderSkipped, 0);
++ if (startCorner === -1) {
++ startCorner = 0;
++ }
++
++ function cornerAt(index) {
++ return corners[(startCorner + index) % 4];
++ }
++
++ // Draw rectangle from 'startCorner'
++ var corner = cornerAt(0);
++ ctx.moveTo(corner[0], corner[1]);
++
++ for (var i = 1; i < 4; i++) {
++ corner = cornerAt(i);
++ ctx.lineTo(corner[0], corner[1]);
++ }
++
++ ctx.fill();
++ if (vm.borderWidth) {
++ ctx.stroke();
++ }
++ },
++ height: function() {
++ var vm = this._view;
++ return vm.base - vm.y;
++ },
++ inRange: function(mouseX, mouseY) {
++ var inRange = false;
++
++ if (this._view) {
++ var bounds = getBarBounds(this);
++ inRange = mouseX >= bounds.left && mouseX <= bounds.right && mouseY >= bounds.top && mouseY <= bounds.bottom;
++ }
++
++ return inRange;
++ },
++ inLabelRange: function(mouseX, mouseY) {
++ var me = this;
++ if (!me._view) {
++ return false;
++ }
++
++ var inRange = false;
++ var bounds = getBarBounds(me);
++
++ if (isVertical(me)) {
++ inRange = mouseX >= bounds.left && mouseX <= bounds.right;
++ } else {
++ inRange = mouseY >= bounds.top && mouseY <= bounds.bottom;
++ }
++
++ return inRange;
++ },
++ inXRange: function(mouseX) {
++ var bounds = getBarBounds(this);
++ return mouseX >= bounds.left && mouseX <= bounds.right;
++ },
++ inYRange: function(mouseY) {
++ var bounds = getBarBounds(this);
++ return mouseY >= bounds.top && mouseY <= bounds.bottom;
++ },
++ getCenterPoint: function() {
++ var vm = this._view;
++ var x, y;
++ if (isVertical(this)) {
++ x = vm.x;
++ y = (vm.y + vm.base) / 2;
++ } else {
++ x = (vm.x + vm.base) / 2;
++ y = vm.y;
++ }
++
++ return {x: x, y: y};
++ },
++ getArea: function() {
++ var vm = this._view;
++ return vm.width * Math.abs(vm.y - vm.base);
++ },
++ tooltipPosition: function() {
++ var vm = this._view;
++ return {
++ x: vm.x,
++ y: vm.y
++ };
++ }
++ });
++
++};
++
++},{}],41:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++
++ var helpers = Chart.helpers;
++ // Default config for a category scale
++ var defaultConfig = {
++ position: 'bottom'
++ };
++
++ var DatasetScale = Chart.Scale.extend({
++ /**
++ * Internal function to get the correct labels. If data.xLabels or data.yLabels are defined, use those
++ * else fall back to data.labels
++ * @private
++ */
++ getLabels: function() {
++ var data = this.chart.data;
++ return (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels;
++ },
++ // Implement this so that
++ determineDataLimits: function() {
++ var me = this;
++ var labels = me.getLabels();
++ me.minIndex = 0;
++ me.maxIndex = labels.length - 1;
++ var findIndex;
++
++ if (me.options.ticks.min !== undefined) {
++ // user specified min value
++ findIndex = helpers.indexOf(labels, me.options.ticks.min);
++ me.minIndex = findIndex !== -1 ? findIndex : me.minIndex;
++ }
++
++ if (me.options.ticks.max !== undefined) {
++ // user specified max value
++ findIndex = helpers.indexOf(labels, me.options.ticks.max);
++ me.maxIndex = findIndex !== -1 ? findIndex : me.maxIndex;
++ }
++
++ me.min = labels[me.minIndex];
++ me.max = labels[me.maxIndex];
++ },
++
++ buildTicks: function() {
++ var me = this;
++ var labels = me.getLabels();
++ // If we are viewing some subset of labels, slice the original array
++ me.ticks = (me.minIndex === 0 && me.maxIndex === labels.length - 1) ? labels : labels.slice(me.minIndex, me.maxIndex + 1);
++ },
++
++ getLabelForIndex: function(index, datasetIndex) {
++ var me = this;
++ var data = me.chart.data;
++ var isHorizontal = me.isHorizontal();
++
++ if ((data.xLabels && isHorizontal) || (data.yLabels && !isHorizontal)) {
++ return me.getRightValue(data.datasets[datasetIndex].data[index]);
++ }
++ return me.ticks[index];
++ },
++
++ // Used to get data value locations. Value can either be an index or a numerical value
++ getPixelForValue: function(value, index, datasetIndex, includeOffset) {
++ var me = this;
++ // 1 is added because we need the length but we have the indexes
++ var offsetAmt = Math.max((me.maxIndex + 1 - me.minIndex - ((me.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
++
++ if (value !== undefined && isNaN(index)) {
++ var labels = me.getLabels();
++ var idx = labels.indexOf(value);
++ index = idx !== -1 ? idx : index;
++ }
++
++ if (me.isHorizontal()) {
++ var innerWidth = me.width - (me.paddingLeft + me.paddingRight);
++ var valueWidth = innerWidth / offsetAmt;
++ var widthOffset = (valueWidth * (index - me.minIndex)) + me.paddingLeft;
++
++ if (me.options.gridLines.offsetGridLines && includeOffset || me.maxIndex === me.minIndex && includeOffset) {
++ widthOffset += (valueWidth / 2);
++ }
++
++ return me.left + Math.round(widthOffset);
++ }
++ var innerHeight = me.height - (me.paddingTop + me.paddingBottom);
++ var valueHeight = innerHeight / offsetAmt;
++ var heightOffset = (valueHeight * (index - me.minIndex)) + me.paddingTop;
++
++ if (me.options.gridLines.offsetGridLines && includeOffset) {
++ heightOffset += (valueHeight / 2);
++ }
++
++ return me.top + Math.round(heightOffset);
++ },
++ getPixelForTick: function(index, includeOffset) {
++ return this.getPixelForValue(this.ticks[index], index + this.minIndex, null, includeOffset);
++ },
++ getValueForPixel: function(pixel) {
++ var me = this;
++ var value;
++ var offsetAmt = Math.max((me.ticks.length - ((me.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
++ var horz = me.isHorizontal();
++ var innerDimension = horz ? me.width - (me.paddingLeft + me.paddingRight) : me.height - (me.paddingTop + me.paddingBottom);
++ var valueDimension = innerDimension / offsetAmt;
++
++ pixel -= horz ? me.left : me.top;
++
++ if (me.options.gridLines.offsetGridLines) {
++ pixel -= (valueDimension / 2);
++ }
++ pixel -= horz ? me.paddingLeft : me.paddingTop;
++
++ if (pixel <= 0) {
++ value = 0;
++ } else {
++ value = Math.round(pixel / valueDimension);
++ }
++
++ return value;
++ },
++ getBasePixel: function() {
++ return this.bottom;
++ }
++ });
++
++ Chart.scaleService.registerScaleType('category', DatasetScale, defaultConfig);
++
++};
++
++},{}],42:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++
++ var helpers = Chart.helpers;
++
++ var defaultConfig = {
++ position: 'left',
++ ticks: {
++ callback: Chart.Ticks.formatters.linear
++ }
++ };
++
++ var LinearScale = Chart.LinearScaleBase.extend({
++ determineDataLimits: function() {
++ var me = this;
++ var opts = me.options;
++ var chart = me.chart;
++ var data = chart.data;
++ var datasets = data.datasets;
++ var isHorizontal = me.isHorizontal();
++
++ function IDMatches(meta) {
++ return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id;
++ }
++
++ // First Calculate the range
++ me.min = null;
++ me.max = null;
++
++ if (opts.stacked) {
++ var valuesPerType = {};
++
++ helpers.each(datasets, function(dataset, datasetIndex) {
++ var meta = chart.getDatasetMeta(datasetIndex);
++ if (valuesPerType[meta.type] === undefined) {
++ valuesPerType[meta.type] = {
++ positiveValues: [],
++ negativeValues: []
++ };
++ }
++
++ // Store these per type
++ var positiveValues = valuesPerType[meta.type].positiveValues;
++ var negativeValues = valuesPerType[meta.type].negativeValues;
++
++ if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
++ helpers.each(dataset.data, function(rawValue, index) {
++ var value = +me.getRightValue(rawValue);
++ if (isNaN(value) || meta.data[index].hidden) {
++ return;
++ }
++
++ positiveValues[index] = positiveValues[index] || 0;
++ negativeValues[index] = negativeValues[index] || 0;
++
++ if (opts.relativePoints) {
++ positiveValues[index] = 100;
++ } else if (value < 0) {
++ negativeValues[index] += value;
++ } else {
++ positiveValues[index] += value;
++ }
++ });
++ }
++ });
++
++ helpers.each(valuesPerType, function(valuesForType) {
++ var values = valuesForType.positiveValues.concat(valuesForType.negativeValues);
++ var minVal = helpers.min(values);
++ var maxVal = helpers.max(values);
++ me.min = me.min === null ? minVal : Math.min(me.min, minVal);
++ me.max = me.max === null ? maxVal : Math.max(me.max, maxVal);
++ });
++
++ } else {
++ helpers.each(datasets, function(dataset, datasetIndex) {
++ var meta = chart.getDatasetMeta(datasetIndex);
++ if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
++ helpers.each(dataset.data, function(rawValue, index) {
++ var value = +me.getRightValue(rawValue);
++ if (isNaN(value) || meta.data[index].hidden) {
++ return;
++ }
++
++ if (me.min === null) {
++ me.min = value;
++ } else if (value < me.min) {
++ me.min = value;
++ }
++
++ if (me.max === null) {
++ me.max = value;
++ } else if (value > me.max) {
++ me.max = value;
++ }
++ });
++ }
++ });
++ }
++
++ // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero
++ this.handleTickRangeOptions();
++ },
++ getTickLimit: function() {
++ var maxTicks;
++ var me = this;
++ var tickOpts = me.options.ticks;
++
++ if (me.isHorizontal()) {
++ maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.width / 50));
++ } else {
++ // The factor of 2 used to scale the font size has been experimentally determined.
++ var tickFontSize = helpers.getValueOrDefault(tickOpts.fontSize, Chart.defaults.global.defaultFontSize);
++ maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.height / (2 * tickFontSize)));
++ }
++
++ return maxTicks;
++ },
++ // Called after the ticks are built. We need
++ handleDirectionalChanges: function() {
++ if (!this.isHorizontal()) {
++ // We are in a vertical orientation. The top value is the highest. So reverse the array
++ this.ticks.reverse();
++ }
++ },
++ getLabelForIndex: function(index, datasetIndex) {
++ return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
++ },
++ // Utils
++ getPixelForValue: function(value) {
++ // This must be called after fit has been run so that
++ // this.left, this.top, this.right, and this.bottom have been defined
++ var me = this;
++ var paddingLeft = me.paddingLeft;
++ var paddingBottom = me.paddingBottom;
++ var start = me.start;
++
++ var rightValue = +me.getRightValue(value);
++ var pixel;
++ var innerDimension;
++ var range = me.end - start;
++
++ if (me.isHorizontal()) {
++ innerDimension = me.width - (paddingLeft + me.paddingRight);
++ pixel = me.left + (innerDimension / range * (rightValue - start));
++ return Math.round(pixel + paddingLeft);
++ }
++ innerDimension = me.height - (me.paddingTop + paddingBottom);
++ pixel = (me.bottom - paddingBottom) - (innerDimension / range * (rightValue - start));
++ return Math.round(pixel);
++ },
++ getValueForPixel: function(pixel) {
++ var me = this;
++ var isHorizontal = me.isHorizontal();
++ var paddingLeft = me.paddingLeft;
++ var paddingBottom = me.paddingBottom;
++ var innerDimension = isHorizontal ? me.width - (paddingLeft + me.paddingRight) : me.height - (me.paddingTop + paddingBottom);
++ var offset = (isHorizontal ? pixel - me.left - paddingLeft : me.bottom - paddingBottom - pixel) / innerDimension;
++ return me.start + ((me.end - me.start) * offset);
++ },
++ getPixelForTick: function(index) {
++ return this.getPixelForValue(this.ticksAsNumbers[index]);
++ }
++ });
++ Chart.scaleService.registerScaleType('linear', LinearScale, defaultConfig);
++
++};
++
++},{}],43:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++
++ var helpers = Chart.helpers,
++ noop = helpers.noop;
++
++ Chart.LinearScaleBase = Chart.Scale.extend({
++ handleTickRangeOptions: function() {
++ var me = this;
++ var opts = me.options;
++ var tickOpts = opts.ticks;
++
++ // If we are forcing it to begin at 0, but 0 will already be rendered on the chart,
++ // do nothing since that would make the chart weird. If the user really wants a weird chart
++ // axis, they can manually override it
++ if (tickOpts.beginAtZero) {
++ var minSign = helpers.sign(me.min);
++ var maxSign = helpers.sign(me.max);
++
++ if (minSign < 0 && maxSign < 0) {
++ // move the top up to 0
++ me.max = 0;
++ } else if (minSign > 0 && maxSign > 0) {
++ // move the bottom down to 0
++ me.min = 0;
++ }
++ }
++
++ if (tickOpts.min !== undefined) {
++ me.min = tickOpts.min;
++ } else if (tickOpts.suggestedMin !== undefined) {
++ me.min = Math.min(me.min, tickOpts.suggestedMin);
++ }
++
++ if (tickOpts.max !== undefined) {
++ me.max = tickOpts.max;
++ } else if (tickOpts.suggestedMax !== undefined) {
++ me.max = Math.max(me.max, tickOpts.suggestedMax);
++ }
++
++ if (me.min === me.max) {
++ me.max++;
++
++ if (!tickOpts.beginAtZero) {
++ me.min--;
++ }
++ }
++ },
++ getTickLimit: noop,
++ handleDirectionalChanges: noop,
++
++ buildTicks: function() {
++ var me = this;
++ var opts = me.options;
++ var tickOpts = opts.ticks;
++
++ // Figure out what the max number of ticks we can support it is based on the size of
++ // the axis area. For now, we say that the minimum tick spacing in pixels must be 50
++ // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
++ // the graph. Make sure we always have at least 2 ticks
++ var maxTicks = me.getTickLimit();
++ maxTicks = Math.max(2, maxTicks);
++
++ var numericGeneratorOptions = {
++ maxTicks: maxTicks,
++ min: tickOpts.min,
++ max: tickOpts.max,
++ stepSize: helpers.getValueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize)
++ };
++ var ticks = me.ticks = Chart.Ticks.generators.linear(numericGeneratorOptions, me);
++
++ me.handleDirectionalChanges();
++
++ // At this point, we need to update our max and min given the tick values since we have expanded the
++ // range of the scale
++ me.max = helpers.max(ticks);
++ me.min = helpers.min(ticks);
++
++ if (tickOpts.reverse) {
++ ticks.reverse();
++
++ me.start = me.max;
++ me.end = me.min;
++ } else {
++ me.start = me.min;
++ me.end = me.max;
++ }
++ },
++ convertTicksToLabels: function() {
++ var me = this;
++ me.ticksAsNumbers = me.ticks.slice();
++ me.zeroLineIndex = me.ticks.indexOf(0);
++
++ Chart.Scale.prototype.convertTicksToLabels.call(me);
++ }
++ });
++};
++
++},{}],44:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++
++ var helpers = Chart.helpers;
++
++ var defaultConfig = {
++ position: 'left',
++
++ // label settings
++ ticks: {
++ callback: Chart.Ticks.formatters.logarithmic
++ }
++ };
++
++ var LogarithmicScale = Chart.Scale.extend({
++ determineDataLimits: function() {
++ var me = this;
++ var opts = me.options;
++ var tickOpts = opts.ticks;
++ var chart = me.chart;
++ var data = chart.data;
++ var datasets = data.datasets;
++ var getValueOrDefault = helpers.getValueOrDefault;
++ var isHorizontal = me.isHorizontal();
++ function IDMatches(meta) {
++ return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id;
++ }
++
++ // Calculate Range
++ me.min = null;
++ me.max = null;
++ me.minNotZero = null;
++
++ if (opts.stacked) {
++ var valuesPerType = {};
++
++ helpers.each(datasets, function(dataset, datasetIndex) {
++ var meta = chart.getDatasetMeta(datasetIndex);
++ if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
++ if (valuesPerType[meta.type] === undefined) {
++ valuesPerType[meta.type] = [];
++ }
++
++ helpers.each(dataset.data, function(rawValue, index) {
++ var values = valuesPerType[meta.type];
++ var value = +me.getRightValue(rawValue);
++ if (isNaN(value) || meta.data[index].hidden) {
++ return;
++ }
++
++ values[index] = values[index] || 0;
++
++ if (opts.relativePoints) {
++ values[index] = 100;
++ } else {
++ // Don't need to split positive and negative since the log scale can't handle a 0 crossing
++ values[index] += value;
++ }
++ });
++ }
++ });
++
++ helpers.each(valuesPerType, function(valuesForType) {
++ var minVal = helpers.min(valuesForType);
++ var maxVal = helpers.max(valuesForType);
++ me.min = me.min === null ? minVal : Math.min(me.min, minVal);
++ me.max = me.max === null ? maxVal : Math.max(me.max, maxVal);
++ });
++
++ } else {
++ helpers.each(datasets, function(dataset, datasetIndex) {
++ var meta = chart.getDatasetMeta(datasetIndex);
++ if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
++ helpers.each(dataset.data, function(rawValue, index) {
++ var value = +me.getRightValue(rawValue);
++ if (isNaN(value) || meta.data[index].hidden) {
++ return;
++ }
++
++ if (me.min === null) {
++ me.min = value;
++ } else if (value < me.min) {
++ me.min = value;
++ }
++
++ if (me.max === null) {
++ me.max = value;
++ } else if (value > me.max) {
++ me.max = value;
++ }
++
++ if (value !== 0 && (me.minNotZero === null || value < me.minNotZero)) {
++ me.minNotZero = value;
++ }
++ });
++ }
++ });
++ }
++
++ me.min = getValueOrDefault(tickOpts.min, me.min);
++ me.max = getValueOrDefault(tickOpts.max, me.max);
++
++ if (me.min === me.max) {
++ if (me.min !== 0 && me.min !== null) {
++ me.min = Math.pow(10, Math.floor(helpers.log10(me.min)) - 1);
++ me.max = Math.pow(10, Math.floor(helpers.log10(me.max)) + 1);
++ } else {
++ me.min = 1;
++ me.max = 10;
++ }
++ }
++ },
++ buildTicks: function() {
++ var me = this;
++ var opts = me.options;
++ var tickOpts = opts.ticks;
++
++ var generationOptions = {
++ min: tickOpts.min,
++ max: tickOpts.max
++ };
++ var ticks = me.ticks = Chart.Ticks.generators.logarithmic(generationOptions, me);
++
++ if (!me.isHorizontal()) {
++ // We are in a vertical orientation. The top value is the highest. So reverse the array
++ ticks.reverse();
++ }
++
++ // At this point, we need to update our max and min given the tick values since we have expanded the
++ // range of the scale
++ me.max = helpers.max(ticks);
++ me.min = helpers.min(ticks);
++
++ if (tickOpts.reverse) {
++ ticks.reverse();
++
++ me.start = me.max;
++ me.end = me.min;
++ } else {
++ me.start = me.min;
++ me.end = me.max;
++ }
++ },
++ convertTicksToLabels: function() {
++ this.tickValues = this.ticks.slice();
++
++ Chart.Scale.prototype.convertTicksToLabels.call(this);
++ },
++ // Get the correct tooltip label
++ getLabelForIndex: function(index, datasetIndex) {
++ return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
++ },
++ getPixelForTick: function(index) {
++ return this.getPixelForValue(this.tickValues[index]);
++ },
++ getPixelForValue: function(value) {
++ var me = this;
++ var innerDimension;
++ var pixel;
++
++ var start = me.start;
++ var newVal = +me.getRightValue(value);
++ var range;
++ var paddingTop = me.paddingTop;
++ var paddingBottom = me.paddingBottom;
++ var paddingLeft = me.paddingLeft;
++ var opts = me.options;
++ var tickOpts = opts.ticks;
++
++ if (me.isHorizontal()) {
++ range = helpers.log10(me.end) - helpers.log10(start); // todo: if start === 0
++ if (newVal === 0) {
++ pixel = me.left + paddingLeft;
++ } else {
++ innerDimension = me.width - (paddingLeft + me.paddingRight);
++ pixel = me.left + (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start)));
++ pixel += paddingLeft;
++ }
++ } else {
++ // Bottom - top since pixels increase downward on a screen
++ innerDimension = me.height - (paddingTop + paddingBottom);
++ if (start === 0 && !tickOpts.reverse) {
++ range = helpers.log10(me.end) - helpers.log10(me.minNotZero);
++ if (newVal === start) {
++ pixel = me.bottom - paddingBottom;
++ } else if (newVal === me.minNotZero) {
++ pixel = me.bottom - paddingBottom - innerDimension * 0.02;
++ } else {
++ pixel = me.bottom - paddingBottom - innerDimension * 0.02 - (innerDimension * 0.98/ range * (helpers.log10(newVal)-helpers.log10(me.minNotZero)));
++ }
++ } else if (me.end === 0 && tickOpts.reverse) {
++ range = helpers.log10(me.start) - helpers.log10(me.minNotZero);
++ if (newVal === me.end) {
++ pixel = me.top + paddingTop;
++ } else if (newVal === me.minNotZero) {
++ pixel = me.top + paddingTop + innerDimension * 0.02;
++ } else {
++ pixel = me.top + paddingTop + innerDimension * 0.02 + (innerDimension * 0.98/ range * (helpers.log10(newVal)-helpers.log10(me.minNotZero)));
++ }
++ } else {
++ range = helpers.log10(me.end) - helpers.log10(start);
++ innerDimension = me.height - (paddingTop + paddingBottom);
++ pixel = (me.bottom - paddingBottom) - (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start)));
++ }
++ }
++ return pixel;
++ },
++ getValueForPixel: function(pixel) {
++ var me = this;
++ var range = helpers.log10(me.end) - helpers.log10(me.start);
++ var value, innerDimension;
++
++ if (me.isHorizontal()) {
++ innerDimension = me.width - (me.paddingLeft + me.paddingRight);
++ value = me.start * Math.pow(10, (pixel - me.left - me.paddingLeft) * range / innerDimension);
++ } else { // todo: if start === 0
++ innerDimension = me.height - (me.paddingTop + me.paddingBottom);
++ value = Math.pow(10, (me.bottom - me.paddingBottom - pixel) * range / innerDimension) / me.start;
++ }
++ return value;
++ }
++ });
++ Chart.scaleService.registerScaleType('logarithmic', LogarithmicScale, defaultConfig);
++
++};
++
++},{}],45:[function(require,module,exports){
++'use strict';
++
++module.exports = function(Chart) {
++
++ var helpers = Chart.helpers;
++ var globalDefaults = Chart.defaults.global;
++
++ var defaultConfig = {
++ display: true,
++
++ // Boolean - Whether to animate scaling the chart from the centre
++ animate: true,
++ lineArc: false,
++ position: 'chartArea',
++
++ angleLines: {
++ display: true,
++ color: 'rgba(0, 0, 0, 0.1)',
++ lineWidth: 1
++ },
++
++ // label settings
++ ticks: {
++ // Boolean - Show a backdrop to the scale label
++ showLabelBackdrop: true,
++
++ // String - The colour of the label backdrop
++ backdropColor: 'rgba(255,255,255,0.75)',
++
++ // Number - The backdrop padding above & below the label in pixels
++ backdropPaddingY: 2,
++
++ // Number - The backdrop padding to the side of the label in pixels
++ backdropPaddingX: 2,
++
++ callback: Chart.Ticks.formatters.linear
++ },
++
++ pointLabels: {
++ // Number - Point label font size in pixels
++ fontSize: 10,
++
++ // Function - Used to convert point labels
++ callback: function(label) {
++ return label;
++ }
++ }
++ };
++
++ var LinearRadialScale = Chart.LinearScaleBase.extend({
++ getValueCount: function() {
++ return this.chart.data.labels.length;
++ },
++ setDimensions: function() {
++ var me = this;
++ var opts = me.options;
++ var tickOpts = opts.ticks;
++ // Set the unconstrained dimension before label rotation
++ me.width = me.maxWidth;
++ me.height = me.maxHeight;
++ me.xCenter = Math.round(me.width / 2);
++ me.yCenter = Math.round(me.height / 2);
++
++ var minSize = helpers.min([me.height, me.width]);
++ var tickFontSize = helpers.getValueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
++ me.drawingArea = opts.display ? (minSize / 2) - (tickFontSize / 2 + tickOpts.backdropPaddingY) : (minSize / 2);
++ },
++ determineDataLimits: function() {
++ var me = this;
++ var chart = me.chart;
++ me.min = null;
++ me.max = null;
++
++
++ helpers.each(chart.data.datasets, function(dataset, datasetIndex) {
++ if (chart.isDatasetVisible(datasetIndex)) {
++ var meta = chart.getDatasetMeta(datasetIndex);
++
++ helpers.each(dataset.data, function(rawValue, index) {
++ var value = +me.getRightValue(rawValue);
++ if (isNaN(value) || meta.data[index].hidden) {
++ return;
++ }
++
++ if (me.min === null) {
++ me.min = value;
++ } else if (value < me.min) {
++ me.min = value;
++ }
++
++ if (me.max === null) {
++ me.max = value;
++ } else if (value > me.max) {
++ me.max = value;
++ }
++ });
++ }
++ });
++
++ // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero
++ me.handleTickRangeOptions();
++ },
++ getTickLimit: function() {
++ var tickOpts = this.options.ticks;
++ var tickFontSize = helpers.getValueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
++ return Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(this.drawingArea / (1.5 * tickFontSize)));
++ },
++ convertTicksToLabels: function() {
++ var me = this;
++ Chart.LinearScaleBase.prototype.convertTicksToLabels.call(me);
++
++ // Point labels
++ me.pointLabels = me.chart.data.labels.map(me.options.pointLabels.callback, me);
++ },
++ getLabelForIndex: function(index, datasetIndex) {
++ return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
++ },
++ fit: function() {
++ /*
++ * Right, this is really confusing and there is a lot of maths going on here
++ * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9
++ *
++ * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif
++ *
++ * Solution:
++ *
++ * We assume the radius of the polygon is half the size of the canvas at first
++ * at each index we check if the text overlaps.
++ *
++ * Where it does, we store that angle and that index.
++ *
++ * After finding the largest index and angle we calculate how much we need to remove
++ * from the shape radius to move the point inwards by that x.
++ *
++ * We average the left and right distances to get the maximum shape radius that can fit in the box
++ * along with labels.
++ *
++ * Once we have that, we can find the centre point for the chart, by taking the x text protrusion
++ * on each side, removing that from the size, halving it and adding the left x protrusion width.
++ *
++ * This will mean we have a shape fitted to the canvas, as large as it can be with the labels
++ * and position it in the most space efficient manner
++ *
++ * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif
++ */
++
++ var pointLabels = this.options.pointLabels;
++ var pointLabelFontSize = helpers.getValueOrDefault(pointLabels.fontSize, globalDefaults.defaultFontSize);
++ var pointLabeFontStyle = helpers.getValueOrDefault(pointLabels.fontStyle, globalDefaults.defaultFontStyle);
++ var pointLabeFontFamily = helpers.getValueOrDefault(pointLabels.fontFamily, globalDefaults.defaultFontFamily);
++ var pointLabeFont = helpers.fontString(pointLabelFontSize, pointLabeFontStyle, pointLabeFontFamily);
++
++ // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width.
++ // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points
++ var largestPossibleRadius = helpers.min([(this.height / 2 - pointLabelFontSize - 5), this.width / 2]),
++ pointPosition,
++ i,
++ textWidth,
++ halfTextWidth,
++ furthestRight = this.width,
++ furthestRightIndex,
++ furthestRightAngle,
++ furthestLeft = 0,
++ furthestLeftIndex,
++ furthestLeftAngle,
++ xProtrusionLeft,
++ xProtrusionRight,
++ radiusReductionRight,
++ radiusReductionLeft;
++ this.ctx.font = pointLabeFont;
++
++ for (i = 0; i < this.getValueCount(); i++) {
++ // 5px to space the text slightly out - similar to what we do in the draw function.
++ pointPosition = this.getPointPosition(i, largestPossibleRadius);
++ textWidth = this.ctx.measureText(this.pointLabels[i] ? this.pointLabels[i] : '').width + 5;
++
++ // Add quarter circle to make degree 0 mean top of circle
++ var angleRadians = this.getIndexAngle(i) + (Math.PI / 2);
++ var angle = (angleRadians * 360 / (2 * Math.PI)) % 360;
++
++ if (angle === 0 || angle === 180) {
++ // At angle 0 and 180, we're at exactly the top/bottom
++ // of the radar chart, so text will be aligned centrally, so we'll half it and compare
++ // w/left and right text sizes
++ halfTextWidth = textWidth / 2;
++ if (pointPosition.x + halfTextWidth > furthestRight) {
++ furthestRight = pointPosition.x + halfTextWidth;
++ furthestRightIndex = i;
++ }
++ if (pointPosition.x - halfTextWidth < furthestLeft) {
++ furthestLeft = pointPosition.x - halfTextWidth;
++ furthestLeftIndex = i;
++ }
++ } else if (angle < 180) {
++ // Less than half the values means we'll left align the text
++ if (pointPosition.x + textWidth > furthestRight) {
++ furthestRight = pointPosition.x + textWidth;
++ furthestRightIndex = i;
++ }
++ // More than half the values means we'll right align the text
++ } else if (pointPosition.x - textWidth < furthestLeft) {
++ furthestLeft = pointPosition.x - textWidth;
++ furthestLeftIndex = i;
++ }
++ }
++
++ xProtrusionLeft = furthestLeft;
++ xProtrusionRight = Math.ceil(furthestRight - this.width);
++
++ furthestRightAngle = this.getIndexAngle(furthestRightIndex);
++ furthestLeftAngle = this.getIndexAngle(furthestLeftIndex);
++
++ radiusReductionRight = xProtrusionRight / Math.sin(furthestRightAngle + Math.PI / 2);
++ radiusReductionLeft = xProtrusionLeft / Math.sin(furthestLeftAngle + Math.PI / 2);
++
++ // Ensure we actually need to reduce the size of the chart
++ radiusReductionRight = (helpers.isNumber(radiusReductionRight)) ? radiusReductionRight : 0;
++ radiusReductionLeft = (helpers.isNumber(radiusReductionLeft)) ? radiusReductionLeft : 0;
++
++ this.drawingArea = Math.round(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2);
++ this.setCenterPoint(radiusReductionLeft, radiusReductionRight);
++ },
++ setCenterPoint: function(leftMovement, rightMovement) {
++ var me = this;
++ var maxRight = me.width - rightMovement - me.drawingArea,
++ maxLeft = leftMovement + me.drawingArea;
++
++ me.xCenter = Math.round(((maxLeft + maxRight) / 2) + me.left);
++ // Always vertically in the centre as the text height doesn't change
++ me.yCenter = Math.round((me.height / 2) + me.top);
++ },
++
++ getIndexAngle: function(index) {
++ var angleMultiplier = (Math.PI * 2) / this.getValueCount();
++ var startAngle = this.chart.options && this.chart.options.startAngle ?
++ this.chart.options.startAngle :
++ 0;
++
++ var startAngleRadians = startAngle * Math.PI * 2 / 360;
++
++ // Start from the top instead of right, so remove a quarter of the circle
++ return index * angleMultiplier - (Math.PI / 2) + startAngleRadians;
++ },
++ getDistanceFromCenterForValue: function(value) {
++ var me = this;
++
++ if (value === null) {
++ return 0; // null always in center
++ }
++
++ // Take into account half font size + the yPadding of the top value
++ var scalingFactor = me.drawingArea / (me.max - me.min);
++ if (me.options.reverse) {
++ return (me.max - value) * scalingFactor;
++ }
++ return (value - me.min) * scalingFactor;
++ },
++ getPointPosition: function(index, distanceFromCenter) {
++ var me = this;
++ var thisAngle = me.getIndexAngle(index);
++ return {
++ x: Math.round(Math.cos(thisAngle) * distanceFromCenter) + me.xCenter,
++ y: Math.round(Math.sin(thisAngle) * distanceFromCenter) + me.yCenter
++ };
++ },
++ getPointPositionForValue: function(index, value) {
++ return this.getPointPosition(index, this.getDistanceFromCenterForValue(value));
++ },
++
++ getBasePosition: function() {
++ var me = this;
++ var min = me.min;
++ var max = me.max;
++
++ return me.getPointPositionForValue(0,
++ me.beginAtZero? 0:
++ min < 0 && max < 0? max :
++ min > 0 && max > 0? min :
++ 0);
++ },
++
++ draw: function() {
++ var me = this;
++ var opts = me.options;
++ var gridLineOpts = opts.gridLines;
++ var tickOpts = opts.ticks;
++ var angleLineOpts = opts.angleLines;
++ var pointLabelOpts = opts.pointLabels;
++ var getValueOrDefault = helpers.getValueOrDefault;
++
++ if (opts.display) {
++ var ctx = me.ctx;
++
++ // Tick Font
++ var tickFontSize = getValueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
++ var tickFontStyle = getValueOrDefault(tickOpts.fontStyle, globalDefaults.defaultFontStyle);
++ var tickFontFamily = getValueOrDefault(tickOpts.fontFamily, globalDefaults.defaultFontFamily);
++ var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);
++
++ helpers.each(me.ticks, function(label, index) {
++ // Don't draw a centre value (if it is minimum)
++ if (index > 0 || opts.reverse) {
++ var yCenterOffset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]);
++ var yHeight = me.yCenter - yCenterOffset;
++
++ // Draw circular lines around the scale
++ if (gridLineOpts.display && index !== 0) {
++ ctx.strokeStyle = helpers.getValueAtIndexOrDefault(gridLineOpts.color, index - 1);
++ ctx.lineWidth = helpers.getValueAtIndexOrDefault(gridLineOpts.lineWidth, index - 1);
++
++ if (opts.lineArc) {
++ // Draw circular arcs between the points
++ ctx.beginPath();
++ ctx.arc(me.xCenter, me.yCenter, yCenterOffset, 0, Math.PI * 2);
++ ctx.closePath();
++ ctx.stroke();
++ } else {
++ // Draw straight lines connecting each index
++ ctx.beginPath();
++ for (var i = 0; i < me.getValueCount(); i++) {
++ var pointPosition = me.getPointPosition(i, yCenterOffset);
++ if (i === 0) {
++ ctx.moveTo(pointPosition.x, pointPosition.y);
++ } else {
++ ctx.lineTo(pointPosition.x, pointPosition.y);
++ }
++ }
++ ctx.closePath();
++ ctx.stroke();
++ }
++ }
++
++ if (tickOpts.display) {
++ var tickFontColor = getValueOrDefault(tickOpts.fontColor, globalDefaults.defaultFontColor);
++ ctx.font = tickLabelFont;
++
++ if (tickOpts.showLabelBackdrop) {
++ var labelWidth = ctx.measureText(label).width;
++ ctx.fillStyle = tickOpts.backdropColor;
++ ctx.fillRect(
++ me.xCenter - labelWidth / 2 - tickOpts.backdropPaddingX,
++ yHeight - tickFontSize / 2 - tickOpts.backdropPaddingY,
++ labelWidth + tickOpts.backdropPaddingX * 2,
++ tickFontSize + tickOpts.backdropPaddingY * 2
++ );
++ }
++
++ ctx.textAlign = 'center';
++ ctx.textBaseline = 'middle';
++ ctx.fillStyle = tickFontColor;
++ ctx.fillText(label, me.xCenter, yHeight);
++ }
++ }
++ });
++
++ if (!opts.lineArc) {
++ ctx.lineWidth = angleLineOpts.lineWidth;
++ ctx.strokeStyle = angleLineOpts.color;
++
++ var outerDistance = me.getDistanceFromCenterForValue(opts.reverse ? me.min : me.max);
++
++ // Point Label Font
++ var pointLabelFontSize = getValueOrDefault(pointLabelOpts.fontSize, globalDefaults.defaultFontSize);
++ var pointLabeFontStyle = getValueOrDefault(pointLabelOpts.fontStyle, globalDefaults.defaultFontStyle);
++ var pointLabeFontFamily = getValueOrDefault(pointLabelOpts.fontFamily, globalDefaults.defaultFontFamily);
++ var pointLabeFont = helpers.fontString(pointLabelFontSize, pointLabeFontStyle, pointLabeFontFamily);
++
++ for (var i = me.getValueCount() - 1; i >= 0; i--) {
++ if (angleLineOpts.display) {
++ var outerPosition = me.getPointPosition(i, outerDistance);
++ ctx.beginPath();
++ ctx.moveTo(me.xCenter, me.yCenter);
++ ctx.lineTo(outerPosition.x, outerPosition.y);
++ ctx.stroke();
++ ctx.closePath();
++ }
++ // Extra 3px out for some label spacing
++ var pointLabelPosition = me.getPointPosition(i, outerDistance + 5);
++
++ // Keep this in loop since we may support array properties here
++ var pointLabelFontColor = getValueOrDefault(pointLabelOpts.fontColor, globalDefaults.defaultFontColor);
++ ctx.font = pointLabeFont;
++ ctx.fillStyle = pointLabelFontColor;
++
++ var pointLabels = me.pointLabels;
++
++ // Add quarter circle to make degree 0 mean top of circle
++ var angleRadians = this.getIndexAngle(i) + (Math.PI / 2);
++ var angle = (angleRadians * 360 / (2 * Math.PI)) % 360;
++
++ if (angle === 0 || angle === 180) {
++ ctx.textAlign = 'center';
++ } else if (angle < 180) {
++ ctx.textAlign = 'left';
++ } else {
++ ctx.textAlign = 'right';
++ }
++
++ // Set the correct text baseline based on outer positioning
++ if (angle === 90 || angle === 270) {
++ ctx.textBaseline = 'middle';
++ } else if (angle > 270 || angle < 90) {
++ ctx.textBaseline = 'bottom';
++ } else {
++ ctx.textBaseline = 'top';
++ }
++
++ ctx.fillText(pointLabels[i] ? pointLabels[i] : '', pointLabelPosition.x, pointLabelPosition.y);
++ }
++ }
++ }
++ }
++ });
++ Chart.scaleService.registerScaleType('radialLinear', LinearRadialScale, defaultConfig);
++
++};
++
++},{}],46:[function(require,module,exports){
++/* global window: false */
++'use strict';
++
++var moment = require(1);
++moment = typeof(moment) === 'function' ? moment : window.moment;
++
++module.exports = function(Chart) {
++
++ var helpers = Chart.helpers;
++ var time = {
++ units: [{
++ name: 'millisecond',
++ steps: [1, 2, 5, 10, 20, 50, 100, 250, 500]
++ }, {
++ name: 'second',
++ steps: [1, 2, 5, 10, 30]
++ }, {
++ name: 'minute',
++ steps: [1, 2, 5, 10, 30]
++ }, {
++ name: 'hour',
++ steps: [1, 2, 3, 6, 12]
++ }, {
++ name: 'day',
++ steps: [1, 2, 5]
++ }, {
++ name: 'week',
++ maxStep: 4
++ }, {
++ name: 'month',
++ maxStep: 3
++ }, {
++ name: 'quarter',
++ maxStep: 4
++ }, {
++ name: 'year',
++ maxStep: false
++ }]
++ };
++
++ var defaultConfig = {
++ position: 'bottom',
++
++ time: {
++ parser: false, // false == a pattern string from http://momentjs.com/docs/#/parsing/string-format/ or a custom callback that converts its argument to a moment
++ format: false, // DEPRECATED false == date objects, moment object, callback or a pattern string from http://momentjs.com/docs/#/parsing/string-format/
++ unit: false, // false == automatic or override with week, month, year, etc.
++ round: false, // none, or override with week, month, year, etc.
++ displayFormat: false, // DEPRECATED
++ isoWeekday: false, // override week start day - see http://momentjs.com/docs/#/get-set/iso-weekday/
++ minUnit: 'millisecond',
++
++ // defaults to unit's corresponding unitFormat below or override using pattern string from http://momentjs.com/docs/#/displaying/format/
++ displayFormats: {
++ millisecond: 'h:mm:ss.SSS a', // 11:20:01.123 AM,
++ second: 'h:mm:ss a', // 11:20:01 AM
++ minute: 'h:mm:ss a', // 11:20:01 AM
++ hour: 'MMM D, hA', // Sept 4, 5PM
++ day: 'll', // Sep 4 2015
++ week: 'll', // Week 46, or maybe "[W]WW - YYYY" ?
++ month: 'MMM YYYY', // Sept 2015
++ quarter: '[Q]Q - YYYY', // Q3
++ year: 'YYYY' // 2015
++ }
++ },
++ ticks: {
++ autoSkip: false
++ }
++ };
++
++ var TimeScale = Chart.Scale.extend({
++ initialize: function() {
++ if (!moment) {
++ throw new Error('Chart.js - Moment.js could not be found! You must include it before Chart.js to use the time scale. Download at https://momentjs.com');
++ }
++
++ Chart.Scale.prototype.initialize.call(this);
++ },
++ getLabelMoment: function(datasetIndex, index) {
++ if (datasetIndex === null || index === null) {
++ return null;
++ }
++
++ if (typeof this.labelMoments[datasetIndex] !== 'undefined') {
++ return this.labelMoments[datasetIndex][index];
++ }
++
++ return null;
++ },
++ getLabelDiff: function(datasetIndex, index) {
++ var me = this;
++ if (datasetIndex === null || index === null) {
++ return null;
++ }
++
++ if (me.labelDiffs === undefined) {
++ me.buildLabelDiffs();
++ }
++
++ if (typeof me.labelDiffs[datasetIndex] !== 'undefined') {
++ return me.labelDiffs[datasetIndex][index];
++ }
++
++ return null;
++ },
++ getMomentStartOf: function(tick) {
++ var me = this;
++ if (me.options.time.unit === 'week' && me.options.time.isoWeekday !== false) {
++ return tick.clone().startOf('isoWeek').isoWeekday(me.options.time.isoWeekday);
++ }
++ return tick.clone().startOf(me.tickUnit);
++ },
++ determineDataLimits: function() {
++ var me = this;
++ me.labelMoments = [];
++
++ // Only parse these once. If the dataset does not have data as x,y pairs, we will use
++ // these
++ var scaleLabelMoments = [];
++ if (me.chart.data.labels && me.chart.data.labels.length > 0) {
++ helpers.each(me.chart.data.labels, function(label) {
++ var labelMoment = me.parseTime(label);
++
++ if (labelMoment.isValid()) {
++ if (me.options.time.round) {
++ labelMoment.startOf(me.options.time.round);
++ }
++ scaleLabelMoments.push(labelMoment);
++ }
++ }, me);
++
++ me.firstTick = moment.min.call(me, scaleLabelMoments);
++ me.lastTick = moment.max.call(me, scaleLabelMoments);
++ } else {
++ me.firstTick = null;
++ me.lastTick = null;
++ }
++
++ helpers.each(me.chart.data.datasets, function(dataset, datasetIndex) {
++ var momentsForDataset = [];
++ var datasetVisible = me.chart.isDatasetVisible(datasetIndex);
++
++ if (typeof dataset.data[0] === 'object' && dataset.data[0] !== null) {
++ helpers.each(dataset.data, function(value) {
++ var labelMoment = me.parseTime(me.getRightValue(value));
++
++ if (labelMoment.isValid()) {
++ if (me.options.time.round) {
++ labelMoment.startOf(me.options.time.round);
++ }
++ momentsForDataset.push(labelMoment);
++
++ if (datasetVisible) {
++ // May have gone outside the scale ranges, make sure we keep the first and last ticks updated
++ me.firstTick = me.firstTick !== null ? moment.min(me.firstTick, labelMoment) : labelMoment;
++ me.lastTick = me.lastTick !== null ? moment.max(me.lastTick, labelMoment) : labelMoment;
++ }
++ }
++ }, me);
++ } else {
++ // We have no labels. Use the ones from the scale
++ momentsForDataset = scaleLabelMoments;
++ }
++
++ me.labelMoments.push(momentsForDataset);
++ }, me);
++
++ // Set these after we've done all the data
++ if (me.options.time.min) {
++ me.firstTick = me.parseTime(me.options.time.min);
++ }
++
++ if (me.options.time.max) {
++ me.lastTick = me.parseTime(me.options.time.max);
++ }
++
++ // We will modify these, so clone for later
++ me.firstTick = (me.firstTick || moment()).clone();
++ me.lastTick = (me.lastTick || moment()).clone();
++ },
++ buildLabelDiffs: function() {
++ var me = this;
++ me.labelDiffs = [];
++ var scaleLabelDiffs = [];
++ // Parse common labels once
++ if (me.chart.data.labels && me.chart.data.labels.length > 0) {
++ helpers.each(me.chart.data.labels, function(label) {
++ var labelMoment = me.parseTime(label);
++
++ if (labelMoment.isValid()) {
++ if (me.options.time.round) {
++ labelMoment.startOf(me.options.time.round);
++ }
++ scaleLabelDiffs.push(labelMoment.diff(me.firstTick, me.tickUnit, true));
++ }
++ }, me);
++ }
++
++ helpers.each(me.chart.data.datasets, function(dataset) {
++ var diffsForDataset = [];
++
++ if (typeof dataset.data[0] === 'object' && dataset.data[0] !== null) {
++ helpers.each(dataset.data, function(value) {
++ var labelMoment = me.parseTime(me.getRightValue(value));
++
++ if (labelMoment.isValid()) {
++ if (me.options.time.round) {
++ labelMoment.startOf(me.options.time.round);
++ }
++ diffsForDataset.push(labelMoment.diff(me.firstTick, me.tickUnit, true));
++ }
++ }, me);
++ } else {
++ // We have no labels. Use common ones
++ diffsForDataset = scaleLabelDiffs;
++ }
++
++ me.labelDiffs.push(diffsForDataset);
++ }, me);
++ },
++ buildTicks: function() {
++ var me = this;
++
++ me.ctx.save();
++ var tickFontSize = helpers.getValueOrDefault(me.options.ticks.fontSize, Chart.defaults.global.defaultFontSize);
++ var tickFontStyle = helpers.getValueOrDefault(me.options.ticks.fontStyle, Chart.defaults.global.defaultFontStyle);
++ var tickFontFamily = helpers.getValueOrDefault(me.options.ticks.fontFamily, Chart.defaults.global.defaultFontFamily);
++ var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);
++ me.ctx.font = tickLabelFont;
++
++ me.ticks = [];
++ me.unitScale = 1; // How much we scale the unit by, ie 2 means 2x unit per step
++ me.scaleSizeInUnits = 0; // How large the scale is in the base unit (seconds, minutes, etc)
++
++ // Set unit override if applicable
++ if (me.options.time.unit) {
++ me.tickUnit = me.options.time.unit || 'day';
++ me.displayFormat = me.options.time.displayFormats[me.tickUnit];
++ me.scaleSizeInUnits = me.lastTick.diff(me.firstTick, me.tickUnit, true);
++ me.unitScale = helpers.getValueOrDefault(me.options.time.unitStepSize, 1);
++ } else {
++ // Determine the smallest needed unit of the time
++ var innerWidth = me.isHorizontal() ? me.width - (me.paddingLeft + me.paddingRight) : me.height - (me.paddingTop + me.paddingBottom);
++
++ // Crude approximation of what the label length might be
++ var tempFirstLabel = me.tickFormatFunction(me.firstTick, 0, []);
++ var tickLabelWidth = me.ctx.measureText(tempFirstLabel).width;
++ var cosRotation = Math.cos(helpers.toRadians(me.options.ticks.maxRotation));
++ var sinRotation = Math.sin(helpers.toRadians(me.options.ticks.maxRotation));
++ tickLabelWidth = (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation);
++ var labelCapacity = innerWidth / (tickLabelWidth);
++
++ // Start as small as possible
++ me.tickUnit = me.options.time.minUnit;
++ me.scaleSizeInUnits = me.lastTick.diff(me.firstTick, me.tickUnit, true);
++ me.displayFormat = me.options.time.displayFormats[me.tickUnit];
++
++ var unitDefinitionIndex = 0;
++ var unitDefinition = time.units[unitDefinitionIndex];
++
++ // While we aren't ideal and we don't have units left
++ while (unitDefinitionIndex < time.units.length) {
++ // Can we scale this unit. If `false` we can scale infinitely
++ me.unitScale = 1;
++
++ if (helpers.isArray(unitDefinition.steps) && Math.ceil(me.scaleSizeInUnits / labelCapacity) < helpers.max(unitDefinition.steps)) {
++ // Use one of the predefined steps
++ for (var idx = 0; idx < unitDefinition.steps.length; ++idx) {
++ if (unitDefinition.steps[idx] >= Math.ceil(me.scaleSizeInUnits / labelCapacity)) {
++ me.unitScale = helpers.getValueOrDefault(me.options.time.unitStepSize, unitDefinition.steps[idx]);
++ break;
++ }
++ }
++
++ break;
++ } else if ((unitDefinition.maxStep === false) || (Math.ceil(me.scaleSizeInUnits / labelCapacity) < unitDefinition.maxStep)) {
++ // We have a max step. Scale this unit
++ me.unitScale = helpers.getValueOrDefault(me.options.time.unitStepSize, Math.ceil(me.scaleSizeInUnits / labelCapacity));
++ break;
++ } else {
++ // Move to the next unit up
++ ++unitDefinitionIndex;
++ unitDefinition = time.units[unitDefinitionIndex];
++
++ me.tickUnit = unitDefinition.name;
++ var leadingUnitBuffer = me.firstTick.diff(me.getMomentStartOf(me.firstTick), me.tickUnit, true);
++ var trailingUnitBuffer = me.getMomentStartOf(me.lastTick.clone().add(1, me.tickUnit)).diff(me.lastTick, me.tickUnit, true);
++ me.scaleSizeInUnits = me.lastTick.diff(me.firstTick, me.tickUnit, true) + leadingUnitBuffer + trailingUnitBuffer;
++ me.displayFormat = me.options.time.displayFormats[unitDefinition.name];
++ }
++ }
++ }
++
++ var roundedStart;
++
++ // Only round the first tick if we have no hard minimum
++ if (!me.options.time.min) {
++ me.firstTick = me.getMomentStartOf(me.firstTick);
++ roundedStart = me.firstTick;
++ } else {
++ roundedStart = me.getMomentStartOf(me.firstTick);
++ }
++
++ // Only round the last tick if we have no hard maximum
++ if (!me.options.time.max) {
++ var roundedEnd = me.getMomentStartOf(me.lastTick);
++ var delta = roundedEnd.diff(me.lastTick, me.tickUnit, true);
++ if (delta < 0) {
++ // Do not use end of because we need me to be in the next time unit
++ me.lastTick = me.getMomentStartOf(me.lastTick.add(1, me.tickUnit));
++ } else if (delta >= 0) {
++ me.lastTick = roundedEnd;
++ }
++
++ me.scaleSizeInUnits = me.lastTick.diff(me.firstTick, me.tickUnit, true);
++ }
++
++ // Tick displayFormat override
++ if (me.options.time.displayFormat) {
++ me.displayFormat = me.options.time.displayFormat;
++ }
++
++ // first tick. will have been rounded correctly if options.time.min is not specified
++ me.ticks.push(me.firstTick.clone());
++
++ // For every unit in between the first and last moment, create a moment and add it to the ticks tick
++ for (var i = 1; i <= me.scaleSizeInUnits; ++i) {
++ var newTick = roundedStart.clone().add(i, me.tickUnit);
++
++ // Are we greater than the max time
++ if (me.options.time.max && newTick.diff(me.lastTick, me.tickUnit, true) >= 0) {
++ break;
++ }
++
++ if (i % me.unitScale === 0) {
++ me.ticks.push(newTick);
++ }
++ }
++
++ // Always show the right tick
++ var diff = me.ticks[me.ticks.length - 1].diff(me.lastTick, me.tickUnit);
++ if (diff !== 0 || me.scaleSizeInUnits === 0) {
++ // this is a weird case. If the <max> option is the same as the end option, we can't just diff the times because the tick was created from the roundedStart
++ // but the last tick was not rounded.
++ if (me.options.time.max) {
++ me.ticks.push(me.lastTick.clone());
++ me.scaleSizeInUnits = me.lastTick.diff(me.ticks[0], me.tickUnit, true);
++ } else {
++ me.ticks.push(me.lastTick.clone());
++ me.scaleSizeInUnits = me.lastTick.diff(me.firstTick, me.tickUnit, true);
++ }
++ }
++
++ me.ctx.restore();
++
++ // Invalidate label diffs cache
++ me.labelDiffs = undefined;
++ },
++ // Get tooltip label
++ getLabelForIndex: function(index, datasetIndex) {
++ var me = this;
++ var label = me.chart.data.labels && index < me.chart.data.labels.length ? me.chart.data.labels[index] : '';
++
++ if (typeof me.chart.data.datasets[datasetIndex].data[0] === 'object') {
++ label = me.getRightValue(me.chart.data.datasets[datasetIndex].data[index]);
++ }
++
++ // Format nicely
++ if (me.options.time.tooltipFormat) {
++ label = me.parseTime(label).format(me.options.time.tooltipFormat);
++ }
++
++ return label;
++ },
++ // Function to format an individual tick mark
++ tickFormatFunction: function(tick, index, ticks) {
++ var formattedTick = tick.format(this.displayFormat);
++ var tickOpts = this.options.ticks;
++ var callback = helpers.getValueOrDefault(tickOpts.callback, tickOpts.userCallback);
++
++ if (callback) {
++ return callback(formattedTick, index, ticks);
++ }
++ return formattedTick;
++ },
++ convertTicksToLabels: function() {
++ var me = this;
++ me.tickMoments = me.ticks;
++ me.ticks = me.ticks.map(me.tickFormatFunction, me);
++ },
++ getPixelForValue: function(value, index, datasetIndex) {
++ var me = this;
++ var offset = null;
++ if (index !== undefined && datasetIndex !== undefined) {
++ offset = me.getLabelDiff(datasetIndex, index);
++ }
++
++ if (offset === null) {
++ if (!value || !value.isValid) {
++ // not already a moment object
++ value = me.parseTime(me.getRightValue(value));
++ }
++ if (value && value.isValid && value.isValid()) {
++ offset = value.diff(me.firstTick, me.tickUnit, true);
++ }
++ }
++
++ if (offset !== null) {
++ var decimal = offset !== 0 ? offset / me.scaleSizeInUnits : offset;
++
++ if (me.isHorizontal()) {
++ var innerWidth = me.width - (me.paddingLeft + me.paddingRight);
++ var valueOffset = (innerWidth * decimal) + me.paddingLeft;
++
++ return me.left + Math.round(valueOffset);
++ }
++ var innerHeight = me.height - (me.paddingTop + me.paddingBottom);
++ var heightOffset = (innerHeight * decimal) + me.paddingTop;
++
++ return me.top + Math.round(heightOffset);
++ }
++ },
++ getPixelForTick: function(index) {
++ return this.getPixelForValue(this.tickMoments[index], null, null);
++ },
++ getValueForPixel: function(pixel) {
++ var me = this;
++ var innerDimension = me.isHorizontal() ? me.width - (me.paddingLeft + me.paddingRight) : me.height - (me.paddingTop + me.paddingBottom);
++ var offset = (pixel - (me.isHorizontal() ? me.left + me.paddingLeft : me.top + me.paddingTop)) / innerDimension;
++ offset *= me.scaleSizeInUnits;
++ return me.firstTick.clone().add(moment.duration(offset, me.tickUnit).asSeconds(), 'seconds');
++ },
++ parseTime: function(label) {
++ var me = this;
++ if (typeof me.options.time.parser === 'string') {
++ return moment(label, me.options.time.parser);
++ }
++ if (typeof me.options.time.parser === 'function') {
++ return me.options.time.parser(label);
++ }
++ // Date objects
++ if (typeof label.getMonth === 'function' || typeof label === 'number') {
++ return moment(label);
++ }
++ // Moment support
++ if (label.isValid && label.isValid()) {
++ return label;
++ }
++ // Custom parsing (return an instance of moment)
++ if (typeof me.options.time.format !== 'string' && me.options.time.format.call) {
++ console.warn('options.time.format is deprecated and replaced by options.time.parser. See http://nnnick.github.io/Chart.js/docs-v2/#scales-time-scale');
++ return me.options.time.format(label);
++ }
++ // Moment format parsing
++ return moment(label, me.options.time.format);
++ }
++ });
++ Chart.scaleService.registerScaleType('time', TimeScale, defaultConfig);
++
++};
++
++},{"1":1}]},{},[7])(7)
++});
--- /dev/null
--- /dev/null
++//! moment.js
++//! version : 2.17.1
++//! authors : Tim Wood, Iskren Chernev, Moment.js contributors
++//! license : MIT
++//! momentjs.com
++
++import { hooks as moment, setHookCallback } from './lib/utils/hooks';
++
++moment.version = '2.17.1';
++
++import {
++ min,
++ max,
++ now,
++ isMoment,
++ momentPrototype as fn,
++ createUTC as utc,
++ createUnix as unix,
++ createLocal as local,
++ createInvalid as invalid,
++ createInZone as parseZone
++} from './lib/moment/moment';
++
++import {
++ getCalendarFormat
++} from './lib/moment/calendar';
++
++import {
++ defineLocale,
++ updateLocale,
++ getSetGlobalLocale as locale,
++ getLocale as localeData,
++ listLocales as locales,
++ listMonths as months,
++ listMonthsShort as monthsShort,
++ listWeekdays as weekdays,
++ listWeekdaysMin as weekdaysMin,
++ listWeekdaysShort as weekdaysShort
++} from './lib/locale/locale';
++
++import {
++ isDuration,
++ createDuration as duration,
++ getSetRelativeTimeRounding as relativeTimeRounding,
++ getSetRelativeTimeThreshold as relativeTimeThreshold
++} from './lib/duration/duration';
++
++import { normalizeUnits } from './lib/units/units';
++
++import isDate from './lib/utils/is-date';
++
++setHookCallback(local);
++
++moment.fn = fn;
++moment.min = min;
++moment.max = max;
++moment.now = now;
++moment.utc = utc;
++moment.unix = unix;
++moment.months = months;
++moment.isDate = isDate;
++moment.locale = locale;
++moment.invalid = invalid;
++moment.duration = duration;
++moment.isMoment = isMoment;
++moment.weekdays = weekdays;
++moment.parseZone = parseZone;
++moment.localeData = localeData;
++moment.isDuration = isDuration;
++moment.monthsShort = monthsShort;
++moment.weekdaysMin = weekdaysMin;
++moment.defineLocale = defineLocale;
++moment.updateLocale = updateLocale;
++moment.locales = locales;
++moment.weekdaysShort = weekdaysShort;
++moment.normalizeUnits = normalizeUnits;
++moment.relativeTimeRounding = relativeTimeRounding;
++moment.relativeTimeThreshold = relativeTimeThreshold;
++moment.calendarFormat = getCalendarFormat;
++moment.prototype = fn;
++
++export default moment;
--- /dev/null
--- /dev/null
++// Rivets.js
++// version: 0.9.6
++// author: Michael Richards
++// license: MIT
++(function() {
++ var Rivets, bindMethod, jQuery, unbindMethod, _ref,
++ __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
++ __slice = [].slice,
++ __hasProp = {}.hasOwnProperty,
++ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
++ __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
++
++ Rivets = {
++ options: ['prefix', 'templateDelimiters', 'rootInterface', 'preloadData', 'handler', 'executeFunctions'],
++ extensions: ['binders', 'formatters', 'components', 'adapters'],
++ "public": {
++ binders: {},
++ components: {},
++ formatters: {},
++ adapters: {},
++ prefix: 'rv',
++ templateDelimiters: ['{', '}'],
++ rootInterface: '.',
++ preloadData: true,
++ executeFunctions: false,
++ iterationAlias: function(modelName) {
++ return '%' + modelName + '%';
++ },
++ handler: function(context, ev, binding) {
++ return this.call(context, ev, binding.view.models);
++ },
++ configure: function(options) {
++ var descriptor, key, option, value;
++ if (options == null) {
++ options = {};
++ }
++ for (option in options) {
++ value = options[option];
++ if (option === 'binders' || option === 'components' || option === 'formatters' || option === 'adapters') {
++ for (key in value) {
++ descriptor = value[key];
++ Rivets[option][key] = descriptor;
++ }
++ } else {
++ Rivets["public"][option] = value;
++ }
++ }
++ },
++ bind: function(el, models, options) {
++ var view;
++ if (models == null) {
++ models = {};
++ }
++ if (options == null) {
++ options = {};
++ }
++ view = new Rivets.View(el, models, options);
++ view.bind();
++ return view;
++ },
++ init: function(component, el, data) {
++ var scope, template, view;
++ if (data == null) {
++ data = {};
++ }
++ if (el == null) {
++ el = document.createElement('div');
++ }
++ component = Rivets["public"].components[component];
++ template = component.template.call(this, el);
++ if (template instanceof HTMLElement) {
++ while (el.firstChild) {
++ el.removeChild(el.firstChild);
++ }
++ el.appendChild(template);
++ } else {
++ el.innerHTML = template;
++ }
++ scope = component.initialize.call(this, el, data);
++ view = new Rivets.View(el, scope);
++ view.bind();
++ return view;
++ }
++ }
++ };
++
++ if (window['jQuery'] || window['$']) {
++ jQuery = window['jQuery'] || window['$'];
++ _ref = 'on' in jQuery.prototype ? ['on', 'off'] : ['bind', 'unbind'], bindMethod = _ref[0], unbindMethod = _ref[1];
++ Rivets.Util = {
++ bindEvent: function(el, event, handler) {
++ return jQuery(el)[bindMethod](event, handler);
++ },
++ unbindEvent: function(el, event, handler) {
++ return jQuery(el)[unbindMethod](event, handler);
++ },
++ getInputValue: function(el) {
++ var $el;
++ $el = jQuery(el);
++ if ($el.attr('type') === 'checkbox') {
++ return $el.is(':checked');
++ } else {
++ return $el.val();
++ }
++ }
++ };
++ } else {
++ Rivets.Util = {
++ bindEvent: (function() {
++ if ('addEventListener' in window) {
++ return function(el, event, handler) {
++ return el.addEventListener(event, handler, false);
++ };
++ }
++ return function(el, event, handler) {
++ return el.attachEvent('on' + event, handler);
++ };
++ })(),
++ unbindEvent: (function() {
++ if ('removeEventListener' in window) {
++ return function(el, event, handler) {
++ return el.removeEventListener(event, handler, false);
++ };
++ }
++ return function(el, event, handler) {
++ return el.detachEvent('on' + event, handler);
++ };
++ })(),
++ getInputValue: function(el) {
++ var o, _i, _len, _results;
++ if (el.type === 'checkbox') {
++ return el.checked;
++ } else if (el.type === 'select-multiple') {
++ _results = [];
++ for (_i = 0, _len = el.length; _i < _len; _i++) {
++ o = el[_i];
++ if (o.selected) {
++ _results.push(o.value);
++ }
++ }
++ return _results;
++ } else {
++ return el.value;
++ }
++ }
++ };
++ }
++
++ Rivets.TypeParser = (function() {
++ function TypeParser() {}
++
++ TypeParser.types = {
++ primitive: 0,
++ keypath: 1
++ };
++
++ TypeParser.parse = function(string) {
++ if (/^'.*'$|^".*"$/.test(string)) {
++ return {
++ type: this.types.primitive,
++ value: string.slice(1, -1)
++ };
++ } else if (string === 'true') {
++ return {
++ type: this.types.primitive,
++ value: true
++ };
++ } else if (string === 'false') {
++ return {
++ type: this.types.primitive,
++ value: false
++ };
++ } else if (string === 'null') {
++ return {
++ type: this.types.primitive,
++ value: null
++ };
++ } else if (string === 'undefined') {
++ return {
++ type: this.types.primitive,
++ value: void 0
++ };
++ } else if (string === '') {
++ return {
++ type: this.types.primitive,
++ value: void 0
++ };
++ } else if (isNaN(Number(string)) === false) {
++ return {
++ type: this.types.primitive,
++ value: Number(string)
++ };
++ } else {
++ return {
++ type: this.types.keypath,
++ value: string
++ };
++ }
++ };
++
++ return TypeParser;
++
++ })();
++
++ Rivets.TextTemplateParser = (function() {
++ function TextTemplateParser() {}
++
++ TextTemplateParser.types = {
++ text: 0,
++ binding: 1
++ };
++
++ TextTemplateParser.parse = function(template, delimiters) {
++ var index, lastIndex, lastToken, length, substring, tokens, value;
++ tokens = [];
++ length = template.length;
++ index = 0;
++ lastIndex = 0;
++ while (lastIndex < length) {
++ index = template.indexOf(delimiters[0], lastIndex);
++ if (index < 0) {
++ tokens.push({
++ type: this.types.text,
++ value: template.slice(lastIndex)
++ });
++ break;
++ } else {
++ if (index > 0 && lastIndex < index) {
++ tokens.push({
++ type: this.types.text,
++ value: template.slice(lastIndex, index)
++ });
++ }
++ lastIndex = index + delimiters[0].length;
++ index = template.indexOf(delimiters[1], lastIndex);
++ if (index < 0) {
++ substring = template.slice(lastIndex - delimiters[1].length);
++ lastToken = tokens[tokens.length - 1];
++ if ((lastToken != null ? lastToken.type : void 0) === this.types.text) {
++ lastToken.value += substring;
++ } else {
++ tokens.push({
++ type: this.types.text,
++ value: substring
++ });
++ }
++ break;
++ }
++ value = template.slice(lastIndex, index).trim();
++ tokens.push({
++ type: this.types.binding,
++ value: value
++ });
++ lastIndex = index + delimiters[1].length;
++ }
++ }
++ return tokens;
++ };
++
++ return TextTemplateParser;
++
++ })();
++
++ Rivets.View = (function() {
++ function View(els, models, options) {
++ var k, option, v, _base, _i, _j, _len, _len1, _ref1, _ref2, _ref3, _ref4, _ref5;
++ this.els = els;
++ this.models = models;
++ if (options == null) {
++ options = {};
++ }
++ this.update = __bind(this.update, this);
++ this.publish = __bind(this.publish, this);
++ this.sync = __bind(this.sync, this);
++ this.unbind = __bind(this.unbind, this);
++ this.bind = __bind(this.bind, this);
++ this.select = __bind(this.select, this);
++ this.traverse = __bind(this.traverse, this);
++ this.build = __bind(this.build, this);
++ this.buildBinding = __bind(this.buildBinding, this);
++ this.bindingRegExp = __bind(this.bindingRegExp, this);
++ this.options = __bind(this.options, this);
++ if (!(this.els.jquery || this.els instanceof Array)) {
++ this.els = [this.els];
++ }
++ _ref1 = Rivets.extensions;
++ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
++ option = _ref1[_i];
++ this[option] = {};
++ if (options[option]) {
++ _ref2 = options[option];
++ for (k in _ref2) {
++ v = _ref2[k];
++ this[option][k] = v;
++ }
++ }
++ _ref3 = Rivets["public"][option];
++ for (k in _ref3) {
++ v = _ref3[k];
++ if ((_base = this[option])[k] == null) {
++ _base[k] = v;
++ }
++ }
++ }
++ _ref4 = Rivets.options;
++ for (_j = 0, _len1 = _ref4.length; _j < _len1; _j++) {
++ option = _ref4[_j];
++ this[option] = (_ref5 = options[option]) != null ? _ref5 : Rivets["public"][option];
++ }
++ this.build();
++ }
++
++ View.prototype.options = function() {
++ var option, options, _i, _len, _ref1;
++ options = {};
++ _ref1 = Rivets.extensions.concat(Rivets.options);
++ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
++ option = _ref1[_i];
++ options[option] = this[option];
++ }
++ return options;
++ };
++
++ View.prototype.bindingRegExp = function() {
++ return new RegExp("^" + this.prefix + "-");
++ };
++
++ View.prototype.buildBinding = function(binding, node, type, declaration) {
++ var context, ctx, dependencies, keypath, options, pipe, pipes;
++ options = {};
++ pipes = (function() {
++ var _i, _len, _ref1, _results;
++ _ref1 = declaration.match(/((?:'[^']*')*(?:(?:[^\|']*(?:'[^']*')+[^\|']*)+|[^\|]+))|^$/g);
++ _results = [];
++ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
++ pipe = _ref1[_i];
++ _results.push(pipe.trim());
++ }
++ return _results;
++ })();
++ context = (function() {
++ var _i, _len, _ref1, _results;
++ _ref1 = pipes.shift().split('<');
++ _results = [];
++ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
++ ctx = _ref1[_i];
++ _results.push(ctx.trim());
++ }
++ return _results;
++ })();
++ keypath = context.shift();
++ options.formatters = pipes;
++ if (dependencies = context.shift()) {
++ options.dependencies = dependencies.split(/\s+/);
++ }
++ return this.bindings.push(new Rivets[binding](this, node, type, keypath, options));
++ };
++
++ View.prototype.build = function() {
++ var el, parse, _i, _len, _ref1;
++ this.bindings = [];
++ parse = (function(_this) {
++ return function(node) {
++ var block, childNode, delimiters, n, parser, text, token, tokens, _i, _j, _len, _len1, _ref1;
++ if (node.nodeType === 3) {
++ parser = Rivets.TextTemplateParser;
++ if (delimiters = _this.templateDelimiters) {
++ if ((tokens = parser.parse(node.data, delimiters)).length) {
++ if (!(tokens.length === 1 && tokens[0].type === parser.types.text)) {
++ for (_i = 0, _len = tokens.length; _i < _len; _i++) {
++ token = tokens[_i];
++ text = document.createTextNode(token.value);
++ node.parentNode.insertBefore(text, node);
++ if (token.type === 1) {
++ _this.buildBinding('TextBinding', text, null, token.value);
++ }
++ }
++ node.parentNode.removeChild(node);
++ }
++ }
++ }
++ } else if (node.nodeType === 1) {
++ block = _this.traverse(node);
++ }
++ if (!block) {
++ _ref1 = (function() {
++ var _k, _len1, _ref1, _results;
++ _ref1 = node.childNodes;
++ _results = [];
++ for (_k = 0, _len1 = _ref1.length; _k < _len1; _k++) {
++ n = _ref1[_k];
++ _results.push(n);
++ }
++ return _results;
++ })();
++ for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
++ childNode = _ref1[_j];
++ parse(childNode);
++ }
++ }
++ };
++ })(this);
++ _ref1 = this.els;
++ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
++ el = _ref1[_i];
++ parse(el);
++ }
++ this.bindings.sort(function(a, b) {
++ var _ref2, _ref3;
++ return (((_ref2 = b.binder) != null ? _ref2.priority : void 0) || 0) - (((_ref3 = a.binder) != null ? _ref3.priority : void 0) || 0);
++ });
++ };
++
++ View.prototype.traverse = function(node) {
++ var attribute, attributes, binder, bindingRegExp, block, identifier, regexp, type, value, _i, _j, _len, _len1, _ref1, _ref2, _ref3;
++ bindingRegExp = this.bindingRegExp();
++ block = node.nodeName === 'SCRIPT' || node.nodeName === 'STYLE';
++ _ref1 = node.attributes;
++ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
++ attribute = _ref1[_i];
++ if (bindingRegExp.test(attribute.name)) {
++ type = attribute.name.replace(bindingRegExp, '');
++ if (!(binder = this.binders[type])) {
++ _ref2 = this.binders;
++ for (identifier in _ref2) {
++ value = _ref2[identifier];
++ if (identifier !== '*' && identifier.indexOf('*') !== -1) {
++ regexp = new RegExp("^" + (identifier.replace(/\*/g, '.+')) + "$");
++ if (regexp.test(type)) {
++ binder = value;
++ }
++ }
++ }
++ }
++ binder || (binder = this.binders['*']);
++ if (binder.block) {
++ block = true;
++ attributes = [attribute];
++ }
++ }
++ }
++ _ref3 = attributes || node.attributes;
++ for (_j = 0, _len1 = _ref3.length; _j < _len1; _j++) {
++ attribute = _ref3[_j];
++ if (bindingRegExp.test(attribute.name)) {
++ type = attribute.name.replace(bindingRegExp, '');
++ this.buildBinding('Binding', node, type, attribute.value);
++ }
++ }
++ if (!block) {
++ type = node.nodeName.toLowerCase();
++ if (this.components[type] && !node._bound) {
++ this.bindings.push(new Rivets.ComponentBinding(this, node, type));
++ block = true;
++ }
++ }
++ return block;
++ };
++
++ View.prototype.select = function(fn) {
++ var binding, _i, _len, _ref1, _results;
++ _ref1 = this.bindings;
++ _results = [];
++ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
++ binding = _ref1[_i];
++ if (fn(binding)) {
++ _results.push(binding);
++ }
++ }
++ return _results;
++ };
++
++ View.prototype.bind = function() {
++ var binding, _i, _len, _ref1;
++ _ref1 = this.bindings;
++ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
++ binding = _ref1[_i];
++ binding.bind();
++ }
++ };
++
++ View.prototype.unbind = function() {
++ var binding, _i, _len, _ref1;
++ _ref1 = this.bindings;
++ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
++ binding = _ref1[_i];
++ binding.unbind();
++ }
++ };
++
++ View.prototype.sync = function() {
++ var binding, _i, _len, _ref1;
++ _ref1 = this.bindings;
++ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
++ binding = _ref1[_i];
++ if (typeof binding.sync === "function") {
++ binding.sync();
++ }
++ }
++ };
++
++ View.prototype.publish = function() {
++ var binding, _i, _len, _ref1;
++ _ref1 = this.select(function(b) {
++ var _ref1;
++ return (_ref1 = b.binder) != null ? _ref1.publishes : void 0;
++ });
++ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
++ binding = _ref1[_i];
++ binding.publish();
++ }
++ };
++
++ View.prototype.update = function(models) {
++ var binding, key, model, _i, _len, _ref1;
++ if (models == null) {
++ models = {};
++ }
++ for (key in models) {
++ model = models[key];
++ this.models[key] = model;
++ }
++ _ref1 = this.bindings;
++ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
++ binding = _ref1[_i];
++ if (typeof binding.update === "function") {
++ binding.update(models);
++ }
++ }
++ };
++
++ return View;
++
++ })();
++
++ Rivets.Binding = (function() {
++ function Binding(view, el, type, keypath, options) {
++ this.view = view;
++ this.el = el;
++ this.type = type;
++ this.keypath = keypath;
++ this.options = options != null ? options : {};
++ this.getValue = __bind(this.getValue, this);
++ this.update = __bind(this.update, this);
++ this.unbind = __bind(this.unbind, this);
++ this.bind = __bind(this.bind, this);
++ this.publish = __bind(this.publish, this);
++ this.sync = __bind(this.sync, this);
++ this.set = __bind(this.set, this);
++ this.eventHandler = __bind(this.eventHandler, this);
++ this.formattedValue = __bind(this.formattedValue, this);
++ this.parseFormatterArguments = __bind(this.parseFormatterArguments, this);
++ this.parseTarget = __bind(this.parseTarget, this);
++ this.observe = __bind(this.observe, this);
++ this.setBinder = __bind(this.setBinder, this);
++ this.formatters = this.options.formatters || [];
++ this.dependencies = [];
++ this.formatterObservers = {};
++ this.model = void 0;
++ this.setBinder();
++ }
++
++ Binding.prototype.setBinder = function() {
++ var identifier, regexp, value, _ref1;
++ if (!(this.binder = this.view.binders[this.type])) {
++ _ref1 = this.view.binders;
++ for (identifier in _ref1) {
++ value = _ref1[identifier];
++ if (identifier !== '*' && identifier.indexOf('*') !== -1) {
++ regexp = new RegExp("^" + (identifier.replace(/\*/g, '.+')) + "$");
++ if (regexp.test(this.type)) {
++ this.binder = value;
++ this.args = new RegExp("^" + (identifier.replace(/\*/g, '(.+)')) + "$").exec(this.type);
++ this.args.shift();
++ }
++ }
++ }
++ }
++ this.binder || (this.binder = this.view.binders['*']);
++ if (this.binder instanceof Function) {
++ return this.binder = {
++ routine: this.binder
++ };
++ }
++ };
++
++ Binding.prototype.observe = function(obj, keypath, callback) {
++ return Rivets.sightglass(obj, keypath, callback, {
++ root: this.view.rootInterface,
++ adapters: this.view.adapters
++ });
++ };
++
++ Binding.prototype.parseTarget = function() {
++ var token;
++ token = Rivets.TypeParser.parse(this.keypath);
++ if (token.type === Rivets.TypeParser.types.primitive) {
++ return this.value = token.value;
++ } else {
++ this.observer = this.observe(this.view.models, this.keypath, this.sync);
++ return this.model = this.observer.target;
++ }
++ };
++
++ Binding.prototype.parseFormatterArguments = function(args, formatterIndex) {
++ var ai, arg, observer, processedArgs, _base, _i, _len;
++ args = (function() {
++ var _i, _len, _results;
++ _results = [];
++ for (_i = 0, _len = args.length; _i < _len; _i++) {
++ arg = args[_i];
++ _results.push(Rivets.TypeParser.parse(arg));
++ }
++ return _results;
++ })();
++ processedArgs = [];
++ for (ai = _i = 0, _len = args.length; _i < _len; ai = ++_i) {
++ arg = args[ai];
++ processedArgs.push(arg.type === Rivets.TypeParser.types.primitive ? arg.value : ((_base = this.formatterObservers)[formatterIndex] || (_base[formatterIndex] = {}), !(observer = this.formatterObservers[formatterIndex][ai]) ? (observer = this.observe(this.view.models, arg.value, this.sync), this.formatterObservers[formatterIndex][ai] = observer) : void 0, observer.value()));
++ }
++ return processedArgs;
++ };
++
++ Binding.prototype.formattedValue = function(value) {
++ var args, fi, formatter, id, processedArgs, _i, _len, _ref1, _ref2;
++ _ref1 = this.formatters;
++ for (fi = _i = 0, _len = _ref1.length; _i < _len; fi = ++_i) {
++ formatter = _ref1[fi];
++ args = formatter.match(/[^\s']+|'([^']|'[^\s])*'|"([^"]|"[^\s])*"/g);
++ id = args.shift();
++ formatter = this.view.formatters[id];
++ processedArgs = this.parseFormatterArguments(args, fi);
++ if ((formatter != null ? formatter.read : void 0) instanceof Function) {
++ value = (_ref2 = formatter.read).call.apply(_ref2, [this.model, value].concat(__slice.call(processedArgs)));
++ } else if (formatter instanceof Function) {
++ value = formatter.call.apply(formatter, [this.model, value].concat(__slice.call(processedArgs)));
++ }
++ }
++ return value;
++ };
++
++ Binding.prototype.eventHandler = function(fn) {
++ var binding, handler;
++ handler = (binding = this).view.handler;
++ return function(ev) {
++ return handler.call(fn, this, ev, binding);
++ };
++ };
++
++ Binding.prototype.set = function(value) {
++ var _ref1;
++ value = value instanceof Function && !this.binder["function"] && Rivets["public"].executeFunctions ? this.formattedValue(value.call(this.model)) : this.formattedValue(value);
++ return (_ref1 = this.binder.routine) != null ? _ref1.call(this, this.el, value) : void 0;
++ };
++
++ Binding.prototype.sync = function() {
++ var dependency, observer;
++ return this.set((function() {
++ var _i, _j, _len, _len1, _ref1, _ref2, _ref3;
++ if (this.observer) {
++ if (this.model !== this.observer.target) {
++ _ref1 = this.dependencies;
++ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
++ observer = _ref1[_i];
++ observer.unobserve();
++ }
++ this.dependencies = [];
++ if (((this.model = this.observer.target) != null) && ((_ref2 = this.options.dependencies) != null ? _ref2.length : void 0)) {
++ _ref3 = this.options.dependencies;
++ for (_j = 0, _len1 = _ref3.length; _j < _len1; _j++) {
++ dependency = _ref3[_j];
++ observer = this.observe(this.model, dependency, this.sync);
++ this.dependencies.push(observer);
++ }
++ }
++ }
++ return this.observer.value();
++ } else {
++ return this.value;
++ }
++ }).call(this));
++ };
++
++ Binding.prototype.publish = function() {
++ var args, fi, fiReversed, formatter, id, lastformatterIndex, processedArgs, value, _i, _len, _ref1, _ref2, _ref3;
++ if (this.observer) {
++ value = this.getValue(this.el);
++ lastformatterIndex = this.formatters.length - 1;
++ _ref1 = this.formatters.slice(0).reverse();
++ for (fiReversed = _i = 0, _len = _ref1.length; _i < _len; fiReversed = ++_i) {
++ formatter = _ref1[fiReversed];
++ fi = lastformatterIndex - fiReversed;
++ args = formatter.split(/\s+/);
++ id = args.shift();
++ processedArgs = this.parseFormatterArguments(args, fi);
++ if ((_ref2 = this.view.formatters[id]) != null ? _ref2.publish : void 0) {
++ value = (_ref3 = this.view.formatters[id]).publish.apply(_ref3, [value].concat(__slice.call(processedArgs)));
++ }
++ }
++ return this.observer.setValue(value);
++ }
++ };
++
++ Binding.prototype.bind = function() {
++ var dependency, observer, _i, _len, _ref1, _ref2, _ref3;
++ this.parseTarget();
++ if ((_ref1 = this.binder.bind) != null) {
++ _ref1.call(this, this.el);
++ }
++ if ((this.model != null) && ((_ref2 = this.options.dependencies) != null ? _ref2.length : void 0)) {
++ _ref3 = this.options.dependencies;
++ for (_i = 0, _len = _ref3.length; _i < _len; _i++) {
++ dependency = _ref3[_i];
++ observer = this.observe(this.model, dependency, this.sync);
++ this.dependencies.push(observer);
++ }
++ }
++ if (this.view.preloadData) {
++ return this.sync();
++ }
++ };
++
++ Binding.prototype.unbind = function() {
++ var ai, args, fi, observer, _i, _len, _ref1, _ref2, _ref3, _ref4;
++ if ((_ref1 = this.binder.unbind) != null) {
++ _ref1.call(this, this.el);
++ }
++ if ((_ref2 = this.observer) != null) {
++ _ref2.unobserve();
++ }
++ _ref3 = this.dependencies;
++ for (_i = 0, _len = _ref3.length; _i < _len; _i++) {
++ observer = _ref3[_i];
++ observer.unobserve();
++ }
++ this.dependencies = [];
++ _ref4 = this.formatterObservers;
++ for (fi in _ref4) {
++ args = _ref4[fi];
++ for (ai in args) {
++ observer = args[ai];
++ observer.unobserve();
++ }
++ }
++ return this.formatterObservers = {};
++ };
++
++ Binding.prototype.update = function(models) {
++ var _ref1, _ref2;
++ if (models == null) {
++ models = {};
++ }
++ this.model = (_ref1 = this.observer) != null ? _ref1.target : void 0;
++ return (_ref2 = this.binder.update) != null ? _ref2.call(this, models) : void 0;
++ };
++
++ Binding.prototype.getValue = function(el) {
++ if (this.binder && (this.binder.getValue != null)) {
++ return this.binder.getValue.call(this, el);
++ } else {
++ return Rivets.Util.getInputValue(el);
++ }
++ };
++
++ return Binding;
++
++ })();
++
++ Rivets.ComponentBinding = (function(_super) {
++ __extends(ComponentBinding, _super);
++
++ function ComponentBinding(view, el, type) {
++ var attribute, bindingRegExp, propertyName, token, _i, _len, _ref1, _ref2;
++ this.view = view;
++ this.el = el;
++ this.type = type;
++ this.unbind = __bind(this.unbind, this);
++ this.bind = __bind(this.bind, this);
++ this.locals = __bind(this.locals, this);
++ this.component = this.view.components[this.type];
++ this["static"] = {};
++ this.observers = {};
++ this.upstreamObservers = {};
++ bindingRegExp = view.bindingRegExp();
++ _ref1 = this.el.attributes || [];
++ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
++ attribute = _ref1[_i];
++ if (!bindingRegExp.test(attribute.name)) {
++ propertyName = this.camelCase(attribute.name);
++ token = Rivets.TypeParser.parse(attribute.value);
++ if (__indexOf.call((_ref2 = this.component["static"]) != null ? _ref2 : [], propertyName) >= 0) {
++ this["static"][propertyName] = attribute.value;
++ } else if (token.type === Rivets.TypeParser.types.primitive) {
++ this["static"][propertyName] = token.value;
++ } else {
++ this.observers[propertyName] = attribute.value;
++ }
++ }
++ }
++ }
++
++ ComponentBinding.prototype.sync = function() {};
++
++ ComponentBinding.prototype.update = function() {};
++
++ ComponentBinding.prototype.publish = function() {};
++
++ ComponentBinding.prototype.locals = function() {
++ var key, observer, result, value, _ref1, _ref2;
++ result = {};
++ _ref1 = this["static"];
++ for (key in _ref1) {
++ value = _ref1[key];
++ result[key] = value;
++ }
++ _ref2 = this.observers;
++ for (key in _ref2) {
++ observer = _ref2[key];
++ result[key] = observer.value();
++ }
++ return result;
++ };
++
++ ComponentBinding.prototype.camelCase = function(string) {
++ return string.replace(/-([a-z])/g, function(grouped) {
++ return grouped[1].toUpperCase();
++ });
++ };
++
++ ComponentBinding.prototype.bind = function() {
++ var k, key, keypath, observer, option, options, scope, v, _base, _i, _j, _len, _len1, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7;
++ if (!this.bound) {
++ _ref1 = this.observers;
++ for (key in _ref1) {
++ keypath = _ref1[key];
++ this.observers[key] = this.observe(this.view.models, keypath, ((function(_this) {
++ return function(key) {
++ return function() {
++ return _this.componentView.models[key] = _this.observers[key].value();
++ };
++ };
++ })(this)).call(this, key));
++ }
++ this.bound = true;
++ }
++ if (this.componentView != null) {
++ this.componentView.bind();
++ } else {
++ this.el.innerHTML = this.component.template.call(this);
++ scope = this.component.initialize.call(this, this.el, this.locals());
++ this.el._bound = true;
++ options = {};
++ _ref2 = Rivets.extensions;
++ for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
++ option = _ref2[_i];
++ options[option] = {};
++ if (this.component[option]) {
++ _ref3 = this.component[option];
++ for (k in _ref3) {
++ v = _ref3[k];
++ options[option][k] = v;
++ }
++ }
++ _ref4 = this.view[option];
++ for (k in _ref4) {
++ v = _ref4[k];
++ if ((_base = options[option])[k] == null) {
++ _base[k] = v;
++ }
++ }
++ }
++ _ref5 = Rivets.options;
++ for (_j = 0, _len1 = _ref5.length; _j < _len1; _j++) {
++ option = _ref5[_j];
++ options[option] = (_ref6 = this.component[option]) != null ? _ref6 : this.view[option];
++ }
++ this.componentView = new Rivets.View(Array.prototype.slice.call(this.el.childNodes), scope, options);
++ this.componentView.bind();
++ _ref7 = this.observers;
++ for (key in _ref7) {
++ observer = _ref7[key];
++ this.upstreamObservers[key] = this.observe(this.componentView.models, key, ((function(_this) {
++ return function(key, observer) {
++ return function() {
++ return observer.setValue(_this.componentView.models[key]);
++ };
++ };
++ })(this)).call(this, key, observer));
++ }
++ }
++ };
++
++ ComponentBinding.prototype.unbind = function() {
++ var key, observer, _ref1, _ref2, _ref3;
++ _ref1 = this.upstreamObservers;
++ for (key in _ref1) {
++ observer = _ref1[key];
++ observer.unobserve();
++ }
++ _ref2 = this.observers;
++ for (key in _ref2) {
++ observer = _ref2[key];
++ observer.unobserve();
++ }
++ return (_ref3 = this.componentView) != null ? _ref3.unbind.call(this) : void 0;
++ };
++
++ return ComponentBinding;
++
++ })(Rivets.Binding);
++
++ Rivets.TextBinding = (function(_super) {
++ __extends(TextBinding, _super);
++
++ function TextBinding(view, el, type, keypath, options) {
++ this.view = view;
++ this.el = el;
++ this.type = type;
++ this.keypath = keypath;
++ this.options = options != null ? options : {};
++ this.sync = __bind(this.sync, this);
++ this.formatters = this.options.formatters || [];
++ this.dependencies = [];
++ this.formatterObservers = {};
++ }
++
++ TextBinding.prototype.binder = {
++ routine: function(node, value) {
++ return node.data = value != null ? value : '';
++ }
++ };
++
++ TextBinding.prototype.sync = function() {
++ return TextBinding.__super__.sync.apply(this, arguments);
++ };
++
++ return TextBinding;
++
++ })(Rivets.Binding);
++
++ Rivets["public"].binders.text = function(el, value) {
++ if (el.textContent != null) {
++ return el.textContent = value != null ? value : '';
++ } else {
++ return el.innerText = value != null ? value : '';
++ }
++ };
++
++ Rivets["public"].binders.html = function(el, value) {
++ return el.innerHTML = value != null ? value : '';
++ };
++
++ Rivets["public"].binders.show = function(el, value) {
++ return el.style.display = value ? '' : 'none';
++ };
++
++ Rivets["public"].binders.hide = function(el, value) {
++ return el.style.display = value ? 'none' : '';
++ };
++
++ Rivets["public"].binders.enabled = function(el, value) {
++ return el.disabled = !value;
++ };
++
++ Rivets["public"].binders.disabled = function(el, value) {
++ return el.disabled = !!value;
++ };
++
++ Rivets["public"].binders.checked = {
++ publishes: true,
++ priority: 2000,
++ bind: function(el) {
++ return Rivets.Util.bindEvent(el, 'change', this.publish);
++ },
++ unbind: function(el) {
++ return Rivets.Util.unbindEvent(el, 'change', this.publish);
++ },
++ routine: function(el, value) {
++ var _ref1;
++ if (el.type === 'radio') {
++ return el.checked = ((_ref1 = el.value) != null ? _ref1.toString() : void 0) === (value != null ? value.toString() : void 0);
++ } else {
++ return el.checked = !!value;
++ }
++ }
++ };
++
++ Rivets["public"].binders.unchecked = {
++ publishes: true,
++ priority: 2000,
++ bind: function(el) {
++ return Rivets.Util.bindEvent(el, 'change', this.publish);
++ },
++ unbind: function(el) {
++ return Rivets.Util.unbindEvent(el, 'change', this.publish);
++ },
++ routine: function(el, value) {
++ var _ref1;
++ if (el.type === 'radio') {
++ return el.checked = ((_ref1 = el.value) != null ? _ref1.toString() : void 0) !== (value != null ? value.toString() : void 0);
++ } else {
++ return el.checked = !value;
++ }
++ }
++ };
++
++ Rivets["public"].binders.value = {
++ publishes: true,
++ priority: 3000,
++ bind: function(el) {
++ if (!(el.tagName === 'INPUT' && el.type === 'radio')) {
++ this.event = el.tagName === 'SELECT' ? 'change' : 'input';
++ return Rivets.Util.bindEvent(el, this.event, this.publish);
++ }
++ },
++ unbind: function(el) {
++ if (!(el.tagName === 'INPUT' && el.type === 'radio')) {
++ return Rivets.Util.unbindEvent(el, this.event, this.publish);
++ }
++ },
++ routine: function(el, value) {
++ var o, _i, _len, _ref1, _ref2, _ref3, _results;
++ if (el.tagName === 'INPUT' && el.type === 'radio') {
++ return el.setAttribute('value', value);
++ } else if (window.jQuery != null) {
++ el = jQuery(el);
++ if ((value != null ? value.toString() : void 0) !== ((_ref1 = el.val()) != null ? _ref1.toString() : void 0)) {
++ return el.val(value != null ? value : '');
++ }
++ } else {
++ if (el.type === 'select-multiple') {
++ if (value != null) {
++ _results = [];
++ for (_i = 0, _len = el.length; _i < _len; _i++) {
++ o = el[_i];
++ _results.push(o.selected = (_ref2 = o.value, __indexOf.call(value, _ref2) >= 0));
++ }
++ return _results;
++ }
++ } else if ((value != null ? value.toString() : void 0) !== ((_ref3 = el.value) != null ? _ref3.toString() : void 0)) {
++ return el.value = value != null ? value : '';
++ }
++ }
++ }
++ };
++
++ Rivets["public"].binders["if"] = {
++ block: true,
++ priority: 4000,
++ bind: function(el) {
++ var attr, declaration;
++ if (this.marker == null) {
++ attr = [this.view.prefix, this.type].join('-').replace('--', '-');
++ declaration = el.getAttribute(attr);
++ this.marker = document.createComment(" rivets: " + this.type + " " + declaration + " ");
++ this.bound = false;
++ el.removeAttribute(attr);
++ el.parentNode.insertBefore(this.marker, el);
++ return el.parentNode.removeChild(el);
++ }
++ },
++ unbind: function() {
++ if (this.nested) {
++ this.nested.unbind();
++ return this.bound = false;
++ }
++ },
++ routine: function(el, value) {
++ var key, model, models, _ref1;
++ if (!!value === !this.bound) {
++ if (value) {
++ models = {};
++ _ref1 = this.view.models;
++ for (key in _ref1) {
++ model = _ref1[key];
++ models[key] = model;
++ }
++ (this.nested || (this.nested = new Rivets.View(el, models, this.view.options()))).bind();
++ this.marker.parentNode.insertBefore(el, this.marker.nextSibling);
++ return this.bound = true;
++ } else {
++ el.parentNode.removeChild(el);
++ this.nested.unbind();
++ return this.bound = false;
++ }
++ }
++ },
++ update: function(models) {
++ var _ref1;
++ return (_ref1 = this.nested) != null ? _ref1.update(models) : void 0;
++ }
++ };
++
++ Rivets["public"].binders.unless = {
++ block: true,
++ priority: 4000,
++ bind: function(el) {
++ return Rivets["public"].binders["if"].bind.call(this, el);
++ },
++ unbind: function() {
++ return Rivets["public"].binders["if"].unbind.call(this);
++ },
++ routine: function(el, value) {
++ return Rivets["public"].binders["if"].routine.call(this, el, !value);
++ },
++ update: function(models) {
++ return Rivets["public"].binders["if"].update.call(this, models);
++ }
++ };
++
++ Rivets["public"].binders['on-*'] = {
++ "function": true,
++ priority: 1000,
++ unbind: function(el) {
++ if (this.handler) {
++ return Rivets.Util.unbindEvent(el, this.args[0], this.handler);
++ }
++ },
++ routine: function(el, value) {
++ if (this.handler) {
++ Rivets.Util.unbindEvent(el, this.args[0], this.handler);
++ }
++ return Rivets.Util.bindEvent(el, this.args[0], this.handler = this.eventHandler(value));
++ }
++ };
++
++ Rivets["public"].binders['each-*'] = {
++ block: true,
++ priority: 4000,
++ bind: function(el) {
++ var attr, view, _i, _len, _ref1;
++ if (this.marker == null) {
++ attr = [this.view.prefix, this.type].join('-').replace('--', '-');
++ this.marker = document.createComment(" rivets: " + this.type + " ");
++ this.iterated = [];
++ el.removeAttribute(attr);
++ el.parentNode.insertBefore(this.marker, el);
++ el.parentNode.removeChild(el);
++ } else {
++ _ref1 = this.iterated;
++ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
++ view = _ref1[_i];
++ view.bind();
++ }
++ }
++ },
++ unbind: function(el) {
++ var view, _i, _len, _ref1;
++ if (this.iterated != null) {
++ _ref1 = this.iterated;
++ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
++ view = _ref1[_i];
++ view.unbind();
++ }
++ }
++ },
++ routine: function(el, collection) {
++ var binding, data, i, index, key, model, modelName, options, previous, template, view, _i, _j, _k, _len, _len1, _len2, _ref1, _ref2, _ref3;
++ modelName = this.args[0];
++ collection = collection || [];
++ if (this.iterated.length > collection.length) {
++ _ref1 = Array(this.iterated.length - collection.length);
++ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
++ i = _ref1[_i];
++ view = this.iterated.pop();
++ view.unbind();
++ this.marker.parentNode.removeChild(view.els[0]);
++ }
++ }
++ for (index = _j = 0, _len1 = collection.length; _j < _len1; index = ++_j) {
++ model = collection[index];
++ data = {
++ index: index
++ };
++ data[Rivets["public"].iterationAlias(modelName)] = index;
++ data[modelName] = model;
++ if (this.iterated[index] == null) {
++ _ref2 = this.view.models;
++ for (key in _ref2) {
++ model = _ref2[key];
++ if (data[key] == null) {
++ data[key] = model;
++ }
++ }
++ previous = this.iterated.length ? this.iterated[this.iterated.length - 1].els[0] : this.marker;
++ options = this.view.options();
++ options.preloadData = true;
++ template = el.cloneNode(true);
++ view = new Rivets.View(template, data, options);
++ view.bind();
++ this.iterated.push(view);
++ this.marker.parentNode.insertBefore(template, previous.nextSibling);
++ } else if (this.iterated[index].models[modelName] !== model) {
++ this.iterated[index].update(data);
++ }
++ }
++ if (el.nodeName === 'OPTION') {
++ _ref3 = this.view.bindings;
++ for (_k = 0, _len2 = _ref3.length; _k < _len2; _k++) {
++ binding = _ref3[_k];
++ if (binding.el === this.marker.parentNode && binding.type === 'value') {
++ binding.sync();
++ }
++ }
++ }
++ },
++ update: function(models) {
++ var data, key, model, view, _i, _len, _ref1;
++ data = {};
++ for (key in models) {
++ model = models[key];
++ if (key !== this.args[0]) {
++ data[key] = model;
++ }
++ }
++ _ref1 = this.iterated;
++ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
++ view = _ref1[_i];
++ view.update(data);
++ }
++ }
++ };
++
++ Rivets["public"].binders['class-*'] = function(el, value) {
++ var elClass;
++ elClass = " " + el.className + " ";
++ if (!value === (elClass.indexOf(" " + this.args[0] + " ") !== -1)) {
++ return el.className = value ? "" + el.className + " " + this.args[0] : elClass.replace(" " + this.args[0] + " ", ' ').trim();
++ }
++ };
++
++ Rivets["public"].binders['*'] = function(el, value) {
++ if (value != null) {
++ return el.setAttribute(this.type, value);
++ } else {
++ return el.removeAttribute(this.type);
++ }
++ };
++
++ Rivets["public"].formatters['call'] = function() {
++ var args, value;
++ value = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
++ return value.call.apply(value, [this].concat(__slice.call(args)));
++ };
++
++ Rivets["public"].adapters['.'] = {
++ id: '_rv',
++ counter: 0,
++ weakmap: {},
++ weakReference: function(obj) {
++ var id, _base, _name;
++ if (!obj.hasOwnProperty(this.id)) {
++ id = this.counter++;
++ Object.defineProperty(obj, this.id, {
++ value: id
++ });
++ }
++ return (_base = this.weakmap)[_name = obj[this.id]] || (_base[_name] = {
++ callbacks: {}
++ });
++ },
++ cleanupWeakReference: function(ref, id) {
++ if (!Object.keys(ref.callbacks).length) {
++ if (!(ref.pointers && Object.keys(ref.pointers).length)) {
++ return delete this.weakmap[id];
++ }
++ }
++ },
++ stubFunction: function(obj, fn) {
++ var map, original, weakmap;
++ original = obj[fn];
++ map = this.weakReference(obj);
++ weakmap = this.weakmap;
++ return obj[fn] = function() {
++ var callback, k, r, response, _i, _len, _ref1, _ref2, _ref3, _ref4;
++ response = original.apply(obj, arguments);
++ _ref1 = map.pointers;
++ for (r in _ref1) {
++ k = _ref1[r];
++ _ref4 = (_ref2 = (_ref3 = weakmap[r]) != null ? _ref3.callbacks[k] : void 0) != null ? _ref2 : [];
++ for (_i = 0, _len = _ref4.length; _i < _len; _i++) {
++ callback = _ref4[_i];
++ callback();
++ }
++ }
++ return response;
++ };
++ },
++ observeMutations: function(obj, ref, keypath) {
++ var fn, functions, map, _base, _i, _len;
++ if (Array.isArray(obj)) {
++ map = this.weakReference(obj);
++ if (map.pointers == null) {
++ map.pointers = {};
++ functions = ['push', 'pop', 'shift', 'unshift', 'sort', 'reverse', 'splice'];
++ for (_i = 0, _len = functions.length; _i < _len; _i++) {
++ fn = functions[_i];
++ this.stubFunction(obj, fn);
++ }
++ }
++ if ((_base = map.pointers)[ref] == null) {
++ _base[ref] = [];
++ }
++ if (__indexOf.call(map.pointers[ref], keypath) < 0) {
++ return map.pointers[ref].push(keypath);
++ }
++ }
++ },
++ unobserveMutations: function(obj, ref, keypath) {
++ var idx, map, pointers;
++ if (Array.isArray(obj) && (obj[this.id] != null)) {
++ if (map = this.weakmap[obj[this.id]]) {
++ if (pointers = map.pointers[ref]) {
++ if ((idx = pointers.indexOf(keypath)) >= 0) {
++ pointers.splice(idx, 1);
++ }
++ if (!pointers.length) {
++ delete map.pointers[ref];
++ }
++ return this.cleanupWeakReference(map, obj[this.id]);
++ }
++ }
++ }
++ },
++ observe: function(obj, keypath, callback) {
++ var callbacks, desc, value;
++ callbacks = this.weakReference(obj).callbacks;
++ if (callbacks[keypath] == null) {
++ callbacks[keypath] = [];
++ desc = Object.getOwnPropertyDescriptor(obj, keypath);
++ if (!((desc != null ? desc.get : void 0) || (desc != null ? desc.set : void 0))) {
++ value = obj[keypath];
++ Object.defineProperty(obj, keypath, {
++ enumerable: true,
++ get: function() {
++ return value;
++ },
++ set: (function(_this) {
++ return function(newValue) {
++ var cb, map, _i, _len, _ref1;
++ if (newValue !== value) {
++ _this.unobserveMutations(value, obj[_this.id], keypath);
++ value = newValue;
++ if (map = _this.weakmap[obj[_this.id]]) {
++ callbacks = map.callbacks;
++ if (callbacks[keypath]) {
++ _ref1 = callbacks[keypath].slice();
++ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
++ cb = _ref1[_i];
++ if (__indexOf.call(callbacks[keypath], cb) >= 0) {
++ cb();
++ }
++ }
++ }
++ return _this.observeMutations(newValue, obj[_this.id], keypath);
++ }
++ }
++ };
++ })(this)
++ });
++ }
++ }
++ if (__indexOf.call(callbacks[keypath], callback) < 0) {
++ callbacks[keypath].push(callback);
++ }
++ return this.observeMutations(obj[keypath], obj[this.id], keypath);
++ },
++ unobserve: function(obj, keypath, callback) {
++ var callbacks, idx, map;
++ if (map = this.weakmap[obj[this.id]]) {
++ if (callbacks = map.callbacks[keypath]) {
++ if ((idx = callbacks.indexOf(callback)) >= 0) {
++ callbacks.splice(idx, 1);
++ if (!callbacks.length) {
++ delete map.callbacks[keypath];
++ this.unobserveMutations(obj[keypath], obj[this.id], keypath);
++ }
++ }
++ return this.cleanupWeakReference(map, obj[this.id]);
++ }
++ }
++ },
++ get: function(obj, keypath) {
++ return obj[keypath];
++ },
++ set: function(obj, keypath, value) {
++ return obj[keypath] = value;
++ }
++ };
++
++ Rivets.factory = function(sightglass) {
++ Rivets.sightglass = sightglass;
++ Rivets["public"]._ = Rivets;
++ return Rivets["public"];
++ };
++
++ if (typeof (typeof module !== "undefined" && module !== null ? module.exports : void 0) === 'object') {
++ module.exports = Rivets.factory(require('sightglass'));
++ } else if (typeof define === 'function' && define.amd) {
++ define(['sightglass'], function(sightglass) {
++ return this.rivets = Rivets.factory(sightglass);
++ });
++ } else {
++ this.rivets = Rivets.factory(sightglass);
++ }
++
++}).call(this);
--- /dev/null
--- /dev/null
++// Underscore.js 1.8.3
++// http://underscorejs.org
++// (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
++// Underscore may be freely distributed under the MIT license.
++
++(function() {
++
++ // Baseline setup
++ // --------------
++
++ // Establish the root object, `window` in the browser, or `exports` on the server.
++ var root = this;
++
++ // Save the previous value of the `_` variable.
++ var previousUnderscore = root._;
++
++ // Save bytes in the minified (but not gzipped) version:
++ var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
++
++ // Create quick reference variables for speed access to core prototypes.
++ var
++ push = ArrayProto.push,
++ slice = ArrayProto.slice,
++ toString = ObjProto.toString,
++ hasOwnProperty = ObjProto.hasOwnProperty;
++
++ // All **ECMAScript 5** native function implementations that we hope to use
++ // are declared here.
++ var
++ nativeIsArray = Array.isArray,
++ nativeKeys = Object.keys,
++ nativeBind = FuncProto.bind,
++ nativeCreate = Object.create;
++
++ // Naked function reference for surrogate-prototype-swapping.
++ var Ctor = function(){};
++
++ // Create a safe reference to the Underscore object for use below.
++ var _ = function(obj) {
++ if (obj instanceof _) return obj;
++ if (!(this instanceof _)) return new _(obj);
++ this._wrapped = obj;
++ };
++
++ // Export the Underscore object for **Node.js**, with
++ // backwards-compatibility for the old `require()` API. If we're in
++ // the browser, add `_` as a global object.
++ if (typeof exports !== 'undefined') {
++ if (typeof module !== 'undefined' && module.exports) {
++ exports = module.exports = _;
++ }
++ exports._ = _;
++ } else {
++ root._ = _;
++ }
++
++ // Current version.
++ _.VERSION = '1.8.3';
++
++ // Internal function that returns an efficient (for current engines) version
++ // of the passed-in callback, to be repeatedly applied in other Underscore
++ // functions.
++ var optimizeCb = function(func, context, argCount) {
++ if (context === void 0) return func;
++ switch (argCount == null ? 3 : argCount) {
++ case 1: return function(value) {
++ return func.call(context, value);
++ };
++ case 2: return function(value, other) {
++ return func.call(context, value, other);
++ };
++ case 3: return function(value, index, collection) {
++ return func.call(context, value, index, collection);
++ };
++ case 4: return function(accumulator, value, index, collection) {
++ return func.call(context, accumulator, value, index, collection);
++ };
++ }
++ return function() {
++ return func.apply(context, arguments);
++ };
++ };
++
++ // A mostly-internal function to generate callbacks that can be applied
++ // to each element in a collection, returning the desired result — either
++ // identity, an arbitrary callback, a property matcher, or a property accessor.
++ var cb = function(value, context, argCount) {
++ if (value == null) return _.identity;
++ if (_.isFunction(value)) return optimizeCb(value, context, argCount);
++ if (_.isObject(value)) return _.matcher(value);
++ return _.property(value);
++ };
++ _.iteratee = function(value, context) {
++ return cb(value, context, Infinity);
++ };
++
++ // An internal function for creating assigner functions.
++ var createAssigner = function(keysFunc, undefinedOnly) {
++ return function(obj) {
++ var length = arguments.length;
++ if (length < 2 || obj == null) return obj;
++ for (var index = 1; index < length; index++) {
++ var source = arguments[index],
++ keys = keysFunc(source),
++ l = keys.length;
++ for (var i = 0; i < l; i++) {
++ var key = keys[i];
++ if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key];
++ }
++ }
++ return obj;
++ };
++ };
++
++ // An internal function for creating a new object that inherits from another.
++ var baseCreate = function(prototype) {
++ if (!_.isObject(prototype)) return {};
++ if (nativeCreate) return nativeCreate(prototype);
++ Ctor.prototype = prototype;
++ var result = new Ctor;
++ Ctor.prototype = null;
++ return result;
++ };
++
++ var property = function(key) {
++ return function(obj) {
++ return obj == null ? void 0 : obj[key];
++ };
++ };
++
++ // Helper for collection methods to determine whether a collection
++ // should be iterated as an array or as an object
++ // Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength
++ // Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094
++ var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
++ var getLength = property('length');
++ var isArrayLike = function(collection) {
++ var length = getLength(collection);
++ return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
++ };
++
++ // Collection Functions
++ // --------------------
++
++ // The cornerstone, an `each` implementation, aka `forEach`.
++ // Handles raw objects in addition to array-likes. Treats all
++ // sparse array-likes as if they were dense.
++ _.each = _.forEach = function(obj, iteratee, context) {
++ iteratee = optimizeCb(iteratee, context);
++ var i, length;
++ if (isArrayLike(obj)) {
++ for (i = 0, length = obj.length; i < length; i++) {
++ iteratee(obj[i], i, obj);
++ }
++ } else {
++ var keys = _.keys(obj);
++ for (i = 0, length = keys.length; i < length; i++) {
++ iteratee(obj[keys[i]], keys[i], obj);
++ }
++ }
++ return obj;
++ };
++
++ // Return the results of applying the iteratee to each element.
++ _.map = _.collect = function(obj, iteratee, context) {
++ iteratee = cb(iteratee, context);
++ var keys = !isArrayLike(obj) && _.keys(obj),
++ length = (keys || obj).length,
++ results = Array(length);
++ for (var index = 0; index < length; index++) {
++ var currentKey = keys ? keys[index] : index;
++ results[index] = iteratee(obj[currentKey], currentKey, obj);
++ }
++ return results;
++ };
++
++ // Create a reducing function iterating left or right.
++ function createReduce(dir) {
++ // Optimized iterator function as using arguments.length
++ // in the main function will deoptimize the, see #1991.
++ function iterator(obj, iteratee, memo, keys, index, length) {
++ for (; index >= 0 && index < length; index += dir) {
++ var currentKey = keys ? keys[index] : index;
++ memo = iteratee(memo, obj[currentKey], currentKey, obj);
++ }
++ return memo;
++ }
++
++ return function(obj, iteratee, memo, context) {
++ iteratee = optimizeCb(iteratee, context, 4);
++ var keys = !isArrayLike(obj) && _.keys(obj),
++ length = (keys || obj).length,
++ index = dir > 0 ? 0 : length - 1;
++ // Determine the initial value if none is provided.
++ if (arguments.length < 3) {
++ memo = obj[keys ? keys[index] : index];
++ index += dir;
++ }
++ return iterator(obj, iteratee, memo, keys, index, length);
++ };
++ }
++
++ // **Reduce** builds up a single result from a list of values, aka `inject`,
++ // or `foldl`.
++ _.reduce = _.foldl = _.inject = createReduce(1);
++
++ // The right-associative version of reduce, also known as `foldr`.
++ _.reduceRight = _.foldr = createReduce(-1);
++
++ // Return the first value which passes a truth test. Aliased as `detect`.
++ _.find = _.detect = function(obj, predicate, context) {
++ var key;
++ if (isArrayLike(obj)) {
++ key = _.findIndex(obj, predicate, context);
++ } else {
++ key = _.findKey(obj, predicate, context);
++ }
++ if (key !== void 0 && key !== -1) return obj[key];
++ };
++
++ // Return all the elements that pass a truth test.
++ // Aliased as `select`.
++ _.filter = _.select = function(obj, predicate, context) {
++ var results = [];
++ predicate = cb(predicate, context);
++ _.each(obj, function(value, index, list) {
++ if (predicate(value, index, list)) results.push(value);
++ });
++ return results;
++ };
++
++ // Return all the elements for which a truth test fails.
++ _.reject = function(obj, predicate, context) {
++ return _.filter(obj, _.negate(cb(predicate)), context);
++ };
++
++ // Determine whether all of the elements match a truth test.
++ // Aliased as `all`.
++ _.every = _.all = function(obj, predicate, context) {
++ predicate = cb(predicate, context);
++ var keys = !isArrayLike(obj) && _.keys(obj),
++ length = (keys || obj).length;
++ for (var index = 0; index < length; index++) {
++ var currentKey = keys ? keys[index] : index;
++ if (!predicate(obj[currentKey], currentKey, obj)) return false;
++ }
++ return true;
++ };
++
++ // Determine if at least one element in the object matches a truth test.
++ // Aliased as `any`.
++ _.some = _.any = function(obj, predicate, context) {
++ predicate = cb(predicate, context);
++ var keys = !isArrayLike(obj) && _.keys(obj),
++ length = (keys || obj).length;
++ for (var index = 0; index < length; index++) {
++ var currentKey = keys ? keys[index] : index;
++ if (predicate(obj[currentKey], currentKey, obj)) return true;
++ }
++ return false;
++ };
++
++ // Determine if the array or object contains a given item (using `===`).
++ // Aliased as `includes` and `include`.
++ _.contains = _.includes = _.include = function(obj, item, fromIndex, guard) {
++ if (!isArrayLike(obj)) obj = _.values(obj);
++ if (typeof fromIndex != 'number' || guard) fromIndex = 0;
++ return _.indexOf(obj, item, fromIndex) >= 0;
++ };
++
++ // Invoke a method (with arguments) on every item in a collection.
++ _.invoke = function(obj, method) {
++ var args = slice.call(arguments, 2);
++ var isFunc = _.isFunction(method);
++ return _.map(obj, function(value) {
++ var func = isFunc ? method : value[method];
++ return func == null ? func : func.apply(value, args);
++ });
++ };
++
++ // Convenience version of a common use case of `map`: fetching a property.
++ _.pluck = function(obj, key) {
++ return _.map(obj, _.property(key));
++ };
++
++ // Convenience version of a common use case of `filter`: selecting only objects
++ // containing specific `key:value` pairs.
++ _.where = function(obj, attrs) {
++ return _.filter(obj, _.matcher(attrs));
++ };
++
++ // Convenience version of a common use case of `find`: getting the first object
++ // containing specific `key:value` pairs.
++ _.findWhere = function(obj, attrs) {
++ return _.find(obj, _.matcher(attrs));
++ };
++
++ // Return the maximum element (or element-based computation).
++ _.max = function(obj, iteratee, context) {
++ var result = -Infinity, lastComputed = -Infinity,
++ value, computed;
++ if (iteratee == null && obj != null) {
++ obj = isArrayLike(obj) ? obj : _.values(obj);
++ for (var i = 0, length = obj.length; i < length; i++) {
++ value = obj[i];
++ if (value > result) {
++ result = value;
++ }
++ }
++ } else {
++ iteratee = cb(iteratee, context);
++ _.each(obj, function(value, index, list) {
++ computed = iteratee(value, index, list);
++ if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
++ result = value;
++ lastComputed = computed;
++ }
++ });
++ }
++ return result;
++ };
++
++ // Return the minimum element (or element-based computation).
++ _.min = function(obj, iteratee, context) {
++ var result = Infinity, lastComputed = Infinity,
++ value, computed;
++ if (iteratee == null && obj != null) {
++ obj = isArrayLike(obj) ? obj : _.values(obj);
++ for (var i = 0, length = obj.length; i < length; i++) {
++ value = obj[i];
++ if (value < result) {
++ result = value;
++ }
++ }
++ } else {
++ iteratee = cb(iteratee, context);
++ _.each(obj, function(value, index, list) {
++ computed = iteratee(value, index, list);
++ if (computed < lastComputed || computed === Infinity && result === Infinity) {
++ result = value;
++ lastComputed = computed;
++ }
++ });
++ }
++ return result;
++ };
++
++ // Shuffle a collection, using the modern version of the
++ // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
++ _.shuffle = function(obj) {
++ var set = isArrayLike(obj) ? obj : _.values(obj);
++ var length = set.length;
++ var shuffled = Array(length);
++ for (var index = 0, rand; index < length; index++) {
++ rand = _.random(0, index);
++ if (rand !== index) shuffled[index] = shuffled[rand];
++ shuffled[rand] = set[index];
++ }
++ return shuffled;
++ };
++
++ // Sample **n** random values from a collection.
++ // If **n** is not specified, returns a single random element.
++ // The internal `guard` argument allows it to work with `map`.
++ _.sample = function(obj, n, guard) {
++ if (n == null || guard) {
++ if (!isArrayLike(obj)) obj = _.values(obj);
++ return obj[_.random(obj.length - 1)];
++ }
++ return _.shuffle(obj).slice(0, Math.max(0, n));
++ };
++
++ // Sort the object's values by a criterion produced by an iteratee.
++ _.sortBy = function(obj, iteratee, context) {
++ iteratee = cb(iteratee, context);
++ return _.pluck(_.map(obj, function(value, index, list) {
++ return {
++ value: value,
++ index: index,
++ criteria: iteratee(value, index, list)
++ };
++ }).sort(function(left, right) {
++ var a = left.criteria;
++ var b = right.criteria;
++ if (a !== b) {
++ if (a > b || a === void 0) return 1;
++ if (a < b || b === void 0) return -1;
++ }
++ return left.index - right.index;
++ }), 'value');
++ };
++
++ // An internal function used for aggregate "group by" operations.
++ var group = function(behavior) {
++ return function(obj, iteratee, context) {
++ var result = {};
++ iteratee = cb(iteratee, context);
++ _.each(obj, function(value, index) {
++ var key = iteratee(value, index, obj);
++ behavior(result, value, key);
++ });
++ return result;
++ };
++ };
++
++ // Groups the object's values by a criterion. Pass either a string attribute
++ // to group by, or a function that returns the criterion.
++ _.groupBy = group(function(result, value, key) {
++ if (_.has(result, key)) result[key].push(value); else result[key] = [value];
++ });
++
++ // Indexes the object's values by a criterion, similar to `groupBy`, but for
++ // when you know that your index values will be unique.
++ _.indexBy = group(function(result, value, key) {
++ result[key] = value;
++ });
++
++ // Counts instances of an object that group by a certain criterion. Pass
++ // either a string attribute to count by, or a function that returns the
++ // criterion.
++ _.countBy = group(function(result, value, key) {
++ if (_.has(result, key)) result[key]++; else result[key] = 1;
++ });
++
++ // Safely create a real, live array from anything iterable.
++ _.toArray = function(obj) {
++ if (!obj) return [];
++ if (_.isArray(obj)) return slice.call(obj);
++ if (isArrayLike(obj)) return _.map(obj, _.identity);
++ return _.values(obj);
++ };
++
++ // Return the number of elements in an object.
++ _.size = function(obj) {
++ if (obj == null) return 0;
++ return isArrayLike(obj) ? obj.length : _.keys(obj).length;
++ };
++
++ // Split a collection into two arrays: one whose elements all satisfy the given
++ // predicate, and one whose elements all do not satisfy the predicate.
++ _.partition = function(obj, predicate, context) {
++ predicate = cb(predicate, context);
++ var pass = [], fail = [];
++ _.each(obj, function(value, key, obj) {
++ (predicate(value, key, obj) ? pass : fail).push(value);
++ });
++ return [pass, fail];
++ };
++
++ // Array Functions
++ // ---------------
++
++ // Get the first element of an array. Passing **n** will return the first N
++ // values in the array. Aliased as `head` and `take`. The **guard** check
++ // allows it to work with `_.map`.
++ _.first = _.head = _.take = function(array, n, guard) {
++ if (array == null) return void 0;
++ if (n == null || guard) return array[0];
++ return _.initial(array, array.length - n);
++ };
++
++ // Returns everything but the last entry of the array. Especially useful on
++ // the arguments object. Passing **n** will return all the values in
++ // the array, excluding the last N.
++ _.initial = function(array, n, guard) {
++ return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
++ };
++
++ // Get the last element of an array. Passing **n** will return the last N
++ // values in the array.
++ _.last = function(array, n, guard) {
++ if (array == null) return void 0;
++ if (n == null || guard) return array[array.length - 1];
++ return _.rest(array, Math.max(0, array.length - n));
++ };
++
++ // Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
++ // Especially useful on the arguments object. Passing an **n** will return
++ // the rest N values in the array.
++ _.rest = _.tail = _.drop = function(array, n, guard) {
++ return slice.call(array, n == null || guard ? 1 : n);
++ };
++
++ // Trim out all falsy values from an array.
++ _.compact = function(array) {
++ return _.filter(array, _.identity);
++ };
++
++ // Internal implementation of a recursive `flatten` function.
++ var flatten = function(input, shallow, strict, startIndex) {
++ var output = [], idx = 0;
++ for (var i = startIndex || 0, length = getLength(input); i < length; i++) {
++ var value = input[i];
++ if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) {
++ //flatten current level of array or arguments object
++ if (!shallow) value = flatten(value, shallow, strict);
++ var j = 0, len = value.length;
++ output.length += len;
++ while (j < len) {
++ output[idx++] = value[j++];
++ }
++ } else if (!strict) {
++ output[idx++] = value;
++ }
++ }
++ return output;
++ };
++
++ // Flatten out an array, either recursively (by default), or just one level.
++ _.flatten = function(array, shallow) {
++ return flatten(array, shallow, false);
++ };
++
++ // Return a version of the array that does not contain the specified value(s).
++ _.without = function(array) {
++ return _.difference(array, slice.call(arguments, 1));
++ };
++
++ // Produce a duplicate-free version of the array. If the array has already
++ // been sorted, you have the option of using a faster algorithm.
++ // Aliased as `unique`.
++ _.uniq = _.unique = function(array, isSorted, iteratee, context) {
++ if (!_.isBoolean(isSorted)) {
++ context = iteratee;
++ iteratee = isSorted;
++ isSorted = false;
++ }
++ if (iteratee != null) iteratee = cb(iteratee, context);
++ var result = [];
++ var seen = [];
++ for (var i = 0, length = getLength(array); i < length; i++) {
++ var value = array[i],
++ computed = iteratee ? iteratee(value, i, array) : value;
++ if (isSorted) {
++ if (!i || seen !== computed) result.push(value);
++ seen = computed;
++ } else if (iteratee) {
++ if (!_.contains(seen, computed)) {
++ seen.push(computed);
++ result.push(value);
++ }
++ } else if (!_.contains(result, value)) {
++ result.push(value);
++ }
++ }
++ return result;
++ };
++
++ // Produce an array that contains the union: each distinct element from all of
++ // the passed-in arrays.
++ _.union = function() {
++ return _.uniq(flatten(arguments, true, true));
++ };
++
++ // Produce an array that contains every item shared between all the
++ // passed-in arrays.
++ _.intersection = function(array) {
++ var result = [];
++ var argsLength = arguments.length;
++ for (var i = 0, length = getLength(array); i < length; i++) {
++ var item = array[i];
++ if (_.contains(result, item)) continue;
++ for (var j = 1; j < argsLength; j++) {
++ if (!_.contains(arguments[j], item)) break;
++ }
++ if (j === argsLength) result.push(item);
++ }
++ return result;
++ };
++
++ // Take the difference between one array and a number of other arrays.
++ // Only the elements present in just the first array will remain.
++ _.difference = function(array) {
++ var rest = flatten(arguments, true, true, 1);
++ return _.filter(array, function(value){
++ return !_.contains(rest, value);
++ });
++ };
++
++ // Zip together multiple lists into a single array -- elements that share
++ // an index go together.
++ _.zip = function() {
++ return _.unzip(arguments);
++ };
++
++ // Complement of _.zip. Unzip accepts an array of arrays and groups
++ // each array's elements on shared indices
++ _.unzip = function(array) {
++ var length = array && _.max(array, getLength).length || 0;
++ var result = Array(length);
++
++ for (var index = 0; index < length; index++) {
++ result[index] = _.pluck(array, index);
++ }
++ return result;
++ };
++
++ // Converts lists into objects. Pass either a single array of `[key, value]`
++ // pairs, or two parallel arrays of the same length -- one of keys, and one of
++ // the corresponding values.
++ _.object = function(list, values) {
++ var result = {};
++ for (var i = 0, length = getLength(list); i < length; i++) {
++ if (values) {
++ result[list[i]] = values[i];
++ } else {
++ result[list[i][0]] = list[i][1];
++ }
++ }
++ return result;
++ };
++
++ // Generator function to create the findIndex and findLastIndex functions
++ function createPredicateIndexFinder(dir) {
++ return function(array, predicate, context) {
++ predicate = cb(predicate, context);
++ var length = getLength(array);
++ var index = dir > 0 ? 0 : length - 1;
++ for (; index >= 0 && index < length; index += dir) {
++ if (predicate(array[index], index, array)) return index;
++ }
++ return -1;
++ };
++ }
++
++ // Returns the first index on an array-like that passes a predicate test
++ _.findIndex = createPredicateIndexFinder(1);
++ _.findLastIndex = createPredicateIndexFinder(-1);
++
++ // Use a comparator function to figure out the smallest index at which
++ // an object should be inserted so as to maintain order. Uses binary search.
++ _.sortedIndex = function(array, obj, iteratee, context) {
++ iteratee = cb(iteratee, context, 1);
++ var value = iteratee(obj);
++ var low = 0, high = getLength(array);
++ while (low < high) {
++ var mid = Math.floor((low + high) / 2);
++ if (iteratee(array[mid]) < value) low = mid + 1; else high = mid;
++ }
++ return low;
++ };
++
++ // Generator function to create the indexOf and lastIndexOf functions
++ function createIndexFinder(dir, predicateFind, sortedIndex) {
++ return function(array, item, idx) {
++ var i = 0, length = getLength(array);
++ if (typeof idx == 'number') {
++ if (dir > 0) {
++ i = idx >= 0 ? idx : Math.max(idx + length, i);
++ } else {
++ length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1;
++ }
++ } else if (sortedIndex && idx && length) {
++ idx = sortedIndex(array, item);
++ return array[idx] === item ? idx : -1;
++ }
++ if (item !== item) {
++ idx = predicateFind(slice.call(array, i, length), _.isNaN);
++ return idx >= 0 ? idx + i : -1;
++ }
++ for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) {
++ if (array[idx] === item) return idx;
++ }
++ return -1;
++ };
++ }
++
++ // Return the position of the first occurrence of an item in an array,
++ // or -1 if the item is not included in the array.
++ // If the array is large and already in sort order, pass `true`
++ // for **isSorted** to use binary search.
++ _.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex);
++ _.lastIndexOf = createIndexFinder(-1, _.findLastIndex);
++
++ // Generate an integer Array containing an arithmetic progression. A port of
++ // the native Python `range()` function. See
++ // [the Python documentation](http://docs.python.org/library/functions.html#range).
++ _.range = function(start, stop, step) {
++ if (stop == null) {
++ stop = start || 0;
++ start = 0;
++ }
++ step = step || 1;
++
++ var length = Math.max(Math.ceil((stop - start) / step), 0);
++ var range = Array(length);
++
++ for (var idx = 0; idx < length; idx++, start += step) {
++ range[idx] = start;
++ }
++
++ return range;
++ };
++
++ // Function (ahem) Functions
++ // ------------------
++
++ // Determines whether to execute a function as a constructor
++ // or a normal function with the provided arguments
++ var executeBound = function(sourceFunc, boundFunc, context, callingContext, args) {
++ if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args);
++ var self = baseCreate(sourceFunc.prototype);
++ var result = sourceFunc.apply(self, args);
++ if (_.isObject(result)) return result;
++ return self;
++ };
++
++ // Create a function bound to a given object (assigning `this`, and arguments,
++ // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
++ // available.
++ _.bind = function(func, context) {
++ if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
++ if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function');
++ var args = slice.call(arguments, 2);
++ var bound = function() {
++ return executeBound(func, bound, context, this, args.concat(slice.call(arguments)));
++ };
++ return bound;
++ };
++
++ // Partially apply a function by creating a version that has had some of its
++ // arguments pre-filled, without changing its dynamic `this` context. _ acts
++ // as a placeholder, allowing any combination of arguments to be pre-filled.
++ _.partial = function(func) {
++ var boundArgs = slice.call(arguments, 1);
++ var bound = function() {
++ var position = 0, length = boundArgs.length;
++ var args = Array(length);
++ for (var i = 0; i < length; i++) {
++ args[i] = boundArgs[i] === _ ? arguments[position++] : boundArgs[i];
++ }
++ while (position < arguments.length) args.push(arguments[position++]);
++ return executeBound(func, bound, this, this, args);
++ };
++ return bound;
++ };
++
++ // Bind a number of an object's methods to that object. Remaining arguments
++ // are the method names to be bound. Useful for ensuring that all callbacks
++ // defined on an object belong to it.
++ _.bindAll = function(obj) {
++ var i, length = arguments.length, key;
++ if (length <= 1) throw new Error('bindAll must be passed function names');
++ for (i = 1; i < length; i++) {
++ key = arguments[i];
++ obj[key] = _.bind(obj[key], obj);
++ }
++ return obj;
++ };
++
++ // Memoize an expensive function by storing its results.
++ _.memoize = function(func, hasher) {
++ var memoize = function(key) {
++ var cache = memoize.cache;
++ var address = '' + (hasher ? hasher.apply(this, arguments) : key);
++ if (!_.has(cache, address)) cache[address] = func.apply(this, arguments);
++ return cache[address];
++ };
++ memoize.cache = {};
++ return memoize;
++ };
++
++ // Delays a function for the given number of milliseconds, and then calls
++ // it with the arguments supplied.
++ _.delay = function(func, wait) {
++ var args = slice.call(arguments, 2);
++ return setTimeout(function(){
++ return func.apply(null, args);
++ }, wait);
++ };
++
++ // Defers a function, scheduling it to run after the current call stack has
++ // cleared.
++ _.defer = _.partial(_.delay, _, 1);
++
++ // Returns a function, that, when invoked, will only be triggered at most once
++ // during a given window of time. Normally, the throttled function will run
++ // as much as it can, without ever going more than once per `wait` duration;
++ // but if you'd like to disable the execution on the leading edge, pass
++ // `{leading: false}`. To disable execution on the trailing edge, ditto.
++ _.throttle = function(func, wait, options) {
++ var context, args, result;
++ var timeout = null;
++ var previous = 0;
++ if (!options) options = {};
++ var later = function() {
++ previous = options.leading === false ? 0 : _.now();
++ timeout = null;
++ result = func.apply(context, args);
++ if (!timeout) context = args = null;
++ };
++ return function() {
++ var now = _.now();
++ if (!previous && options.leading === false) previous = now;
++ var remaining = wait - (now - previous);
++ context = this;
++ args = arguments;
++ if (remaining <= 0 || remaining > wait) {
++ if (timeout) {
++ clearTimeout(timeout);
++ timeout = null;
++ }
++ previous = now;
++ result = func.apply(context, args);
++ if (!timeout) context = args = null;
++ } else if (!timeout && options.trailing !== false) {
++ timeout = setTimeout(later, remaining);
++ }
++ return result;
++ };
++ };
++
++ // Returns a function, that, as long as it continues to be invoked, will not
++ // be triggered. The function will be called after it stops being called for
++ // N milliseconds. If `immediate` is passed, trigger the function on the
++ // leading edge, instead of the trailing.
++ _.debounce = function(func, wait, immediate) {
++ var timeout, args, context, timestamp, result;
++
++ var later = function() {
++ var last = _.now() - timestamp;
++
++ if (last < wait && last >= 0) {
++ timeout = setTimeout(later, wait - last);
++ } else {
++ timeout = null;
++ if (!immediate) {
++ result = func.apply(context, args);
++ if (!timeout) context = args = null;
++ }
++ }
++ };
++
++ return function() {
++ context = this;
++ args = arguments;
++ timestamp = _.now();
++ var callNow = immediate && !timeout;
++ if (!timeout) timeout = setTimeout(later, wait);
++ if (callNow) {
++ result = func.apply(context, args);
++ context = args = null;
++ }
++
++ return result;
++ };
++ };
++
++ // Returns the first function passed as an argument to the second,
++ // allowing you to adjust arguments, run code before and after, and
++ // conditionally execute the original function.
++ _.wrap = function(func, wrapper) {
++ return _.partial(wrapper, func);
++ };
++
++ // Returns a negated version of the passed-in predicate.
++ _.negate = function(predicate) {
++ return function() {
++ return !predicate.apply(this, arguments);
++ };
++ };
++
++ // Returns a function that is the composition of a list of functions, each
++ // consuming the return value of the function that follows.
++ _.compose = function() {
++ var args = arguments;
++ var start = args.length - 1;
++ return function() {
++ var i = start;
++ var result = args[start].apply(this, arguments);
++ while (i--) result = args[i].call(this, result);
++ return result;
++ };
++ };
++
++ // Returns a function that will only be executed on and after the Nth call.
++ _.after = function(times, func) {
++ return function() {
++ if (--times < 1) {
++ return func.apply(this, arguments);
++ }
++ };
++ };
++
++ // Returns a function that will only be executed up to (but not including) the Nth call.
++ _.before = function(times, func) {
++ var memo;
++ return function() {
++ if (--times > 0) {
++ memo = func.apply(this, arguments);
++ }
++ if (times <= 1) func = null;
++ return memo;
++ };
++ };
++
++ // Returns a function that will be executed at most one time, no matter how
++ // often you call it. Useful for lazy initialization.
++ _.once = _.partial(_.before, 2);
++
++ // Object Functions
++ // ----------------
++
++ // Keys in IE < 9 that won't be iterated by `for key in ...` and thus missed.
++ var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString');
++ var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString',
++ 'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString'];
++
++ function collectNonEnumProps(obj, keys) {
++ var nonEnumIdx = nonEnumerableProps.length;
++ var constructor = obj.constructor;
++ var proto = (_.isFunction(constructor) && constructor.prototype) || ObjProto;
++
++ // Constructor is a special case.
++ var prop = 'constructor';
++ if (_.has(obj, prop) && !_.contains(keys, prop)) keys.push(prop);
++
++ while (nonEnumIdx--) {
++ prop = nonEnumerableProps[nonEnumIdx];
++ if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)) {
++ keys.push(prop);
++ }
++ }
++ }
++
++ // Retrieve the names of an object's own properties.
++ // Delegates to **ECMAScript 5**'s native `Object.keys`
++ _.keys = function(obj) {
++ if (!_.isObject(obj)) return [];
++ if (nativeKeys) return nativeKeys(obj);
++ var keys = [];
++ for (var key in obj) if (_.has(obj, key)) keys.push(key);
++ // Ahem, IE < 9.
++ if (hasEnumBug) collectNonEnumProps(obj, keys);
++ return keys;
++ };
++
++ // Retrieve all the property names of an object.
++ _.allKeys = function(obj) {
++ if (!_.isObject(obj)) return [];
++ var keys = [];
++ for (var key in obj) keys.push(key);
++ // Ahem, IE < 9.
++ if (hasEnumBug) collectNonEnumProps(obj, keys);
++ return keys;
++ };
++
++ // Retrieve the values of an object's properties.
++ _.values = function(obj) {
++ var keys = _.keys(obj);
++ var length = keys.length;
++ var values = Array(length);
++ for (var i = 0; i < length; i++) {
++ values[i] = obj[keys[i]];
++ }
++ return values;
++ };
++
++ // Returns the results of applying the iteratee to each element of the object
++ // In contrast to _.map it returns an object
++ _.mapObject = function(obj, iteratee, context) {
++ iteratee = cb(iteratee, context);
++ var keys = _.keys(obj),
++ length = keys.length,
++ results = {},
++ currentKey;
++ for (var index = 0; index < length; index++) {
++ currentKey = keys[index];
++ results[currentKey] = iteratee(obj[currentKey], currentKey, obj);
++ }
++ return results;
++ };
++
++ // Convert an object into a list of `[key, value]` pairs.
++ _.pairs = function(obj) {
++ var keys = _.keys(obj);
++ var length = keys.length;
++ var pairs = Array(length);
++ for (var i = 0; i < length; i++) {
++ pairs[i] = [keys[i], obj[keys[i]]];
++ }
++ return pairs;
++ };
++
++ // Invert the keys and values of an object. The values must be serializable.
++ _.invert = function(obj) {
++ var result = {};
++ var keys = _.keys(obj);
++ for (var i = 0, length = keys.length; i < length; i++) {
++ result[obj[keys[i]]] = keys[i];
++ }
++ return result;
++ };
++
++ // Return a sorted list of the function names available on the object.
++ // Aliased as `methods`
++ _.functions = _.methods = function(obj) {
++ var names = [];
++ for (var key in obj) {
++ if (_.isFunction(obj[key])) names.push(key);
++ }
++ return names.sort();
++ };
++
++ // Extend a given object with all the properties in passed-in object(s).
++ _.extend = createAssigner(_.allKeys);
++
++ // Assigns a given object with all the own properties in the passed-in object(s)
++ // (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
++ _.extendOwn = _.assign = createAssigner(_.keys);
++
++ // Returns the first key on an object that passes a predicate test
++ _.findKey = function(obj, predicate, context) {
++ predicate = cb(predicate, context);
++ var keys = _.keys(obj), key;
++ for (var i = 0, length = keys.length; i < length; i++) {
++ key = keys[i];
++ if (predicate(obj[key], key, obj)) return key;
++ }
++ };
++
++ // Return a copy of the object only containing the whitelisted properties.
++ _.pick = function(object, oiteratee, context) {
++ var result = {}, obj = object, iteratee, keys;
++ if (obj == null) return result;
++ if (_.isFunction(oiteratee)) {
++ keys = _.allKeys(obj);
++ iteratee = optimizeCb(oiteratee, context);
++ } else {
++ keys = flatten(arguments, false, false, 1);
++ iteratee = function(value, key, obj) { return key in obj; };
++ obj = Object(obj);
++ }
++ for (var i = 0, length = keys.length; i < length; i++) {
++ var key = keys[i];
++ var value = obj[key];
++ if (iteratee(value, key, obj)) result[key] = value;
++ }
++ return result;
++ };
++
++ // Return a copy of the object without the blacklisted properties.
++ _.omit = function(obj, iteratee, context) {
++ if (_.isFunction(iteratee)) {
++ iteratee = _.negate(iteratee);
++ } else {
++ var keys = _.map(flatten(arguments, false, false, 1), String);
++ iteratee = function(value, key) {
++ return !_.contains(keys, key);
++ };
++ }
++ return _.pick(obj, iteratee, context);
++ };
++
++ // Fill in a given object with default properties.
++ _.defaults = createAssigner(_.allKeys, true);
++
++ // Creates an object that inherits from the given prototype object.
++ // If additional properties are provided then they will be added to the
++ // created object.
++ _.create = function(prototype, props) {
++ var result = baseCreate(prototype);
++ if (props) _.extendOwn(result, props);
++ return result;
++ };
++
++ // Create a (shallow-cloned) duplicate of an object.
++ _.clone = function(obj) {
++ if (!_.isObject(obj)) return obj;
++ return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
++ };
++
++ // Invokes interceptor with the obj, and then returns obj.
++ // The primary purpose of this method is to "tap into" a method chain, in
++ // order to perform operations on intermediate results within the chain.
++ _.tap = function(obj, interceptor) {
++ interceptor(obj);
++ return obj;
++ };
++
++ // Returns whether an object has a given set of `key:value` pairs.
++ _.isMatch = function(object, attrs) {
++ var keys = _.keys(attrs), length = keys.length;
++ if (object == null) return !length;
++ var obj = Object(object);
++ for (var i = 0; i < length; i++) {
++ var key = keys[i];
++ if (attrs[key] !== obj[key] || !(key in obj)) return false;
++ }
++ return true;
++ };
++
++
++ // Internal recursive comparison function for `isEqual`.
++ var eq = function(a, b, aStack, bStack) {
++ // Identical objects are equal. `0 === -0`, but they aren't identical.
++ // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
++ if (a === b) return a !== 0 || 1 / a === 1 / b;
++ // A strict comparison is necessary because `null == undefined`.
++ if (a == null || b == null) return a === b;
++ // Unwrap any wrapped objects.
++ if (a instanceof _) a = a._wrapped;
++ if (b instanceof _) b = b._wrapped;
++ // Compare `[[Class]]` names.
++ var className = toString.call(a);
++ if (className !== toString.call(b)) return false;
++ switch (className) {
++ // Strings, numbers, regular expressions, dates, and booleans are compared by value.
++ case '[object RegExp]':
++ // RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i')
++ case '[object String]':
++ // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
++ // equivalent to `new String("5")`.
++ return '' + a === '' + b;
++ case '[object Number]':
++ // `NaN`s are equivalent, but non-reflexive.
++ // Object(NaN) is equivalent to NaN
++ if (+a !== +a) return +b !== +b;
++ // An `egal` comparison is performed for other numeric values.
++ return +a === 0 ? 1 / +a === 1 / b : +a === +b;
++ case '[object Date]':
++ case '[object Boolean]':
++ // Coerce dates and booleans to numeric primitive values. Dates are compared by their
++ // millisecond representations. Note that invalid dates with millisecond representations
++ // of `NaN` are not equivalent.
++ return +a === +b;
++ }
++
++ var areArrays = className === '[object Array]';
++ if (!areArrays) {
++ if (typeof a != 'object' || typeof b != 'object') return false;
++
++ // Objects with different constructors are not equivalent, but `Object`s or `Array`s
++ // from different frames are.
++ var aCtor = a.constructor, bCtor = b.constructor;
++ if (aCtor !== bCtor && !(_.isFunction(aCtor) && aCtor instanceof aCtor &&
++ _.isFunction(bCtor) && bCtor instanceof bCtor)
++ && ('constructor' in a && 'constructor' in b)) {
++ return false;
++ }
++ }
++ // Assume equality for cyclic structures. The algorithm for detecting cyclic
++ // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
++
++ // Initializing stack of traversed objects.
++ // It's done here since we only need them for objects and arrays comparison.
++ aStack = aStack || [];
++ bStack = bStack || [];
++ var length = aStack.length;
++ while (length--) {
++ // Linear search. Performance is inversely proportional to the number of
++ // unique nested structures.
++ if (aStack[length] === a) return bStack[length] === b;
++ }
++
++ // Add the first object to the stack of traversed objects.
++ aStack.push(a);
++ bStack.push(b);
++
++ // Recursively compare objects and arrays.
++ if (areArrays) {
++ // Compare array lengths to determine if a deep comparison is necessary.
++ length = a.length;
++ if (length !== b.length) return false;
++ // Deep compare the contents, ignoring non-numeric properties.
++ while (length--) {
++ if (!eq(a[length], b[length], aStack, bStack)) return false;
++ }
++ } else {
++ // Deep compare objects.
++ var keys = _.keys(a), key;
++ length = keys.length;
++ // Ensure that both objects contain the same number of properties before comparing deep equality.
++ if (_.keys(b).length !== length) return false;
++ while (length--) {
++ // Deep compare each member
++ key = keys[length];
++ if (!(_.has(b, key) && eq(a[key], b[key], aStack, bStack))) return false;
++ }
++ }
++ // Remove the first object from the stack of traversed objects.
++ aStack.pop();
++ bStack.pop();
++ return true;
++ };
++
++ // Perform a deep comparison to check if two objects are equal.
++ _.isEqual = function(a, b) {
++ return eq(a, b);
++ };
++
++ // Is a given array, string, or object empty?
++ // An "empty" object has no enumerable own-properties.
++ _.isEmpty = function(obj) {
++ if (obj == null) return true;
++ if (isArrayLike(obj) && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj))) return obj.length === 0;
++ return _.keys(obj).length === 0;
++ };
++
++ // Is a given value a DOM element?
++ _.isElement = function(obj) {
++ return !!(obj && obj.nodeType === 1);
++ };
++
++ // Is a given value an array?
++ // Delegates to ECMA5's native Array.isArray
++ _.isArray = nativeIsArray || function(obj) {
++ return toString.call(obj) === '[object Array]';
++ };
++
++ // Is a given variable an object?
++ _.isObject = function(obj) {
++ var type = typeof obj;
++ return type === 'function' || type === 'object' && !!obj;
++ };
++
++ // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError.
++ _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error'], function(name) {
++ _['is' + name] = function(obj) {
++ return toString.call(obj) === '[object ' + name + ']';
++ };
++ });
++
++ // Define a fallback version of the method in browsers (ahem, IE < 9), where
++ // there isn't any inspectable "Arguments" type.
++ if (!_.isArguments(arguments)) {
++ _.isArguments = function(obj) {
++ return _.has(obj, 'callee');
++ };
++ }
++
++ // Optimize `isFunction` if appropriate. Work around some typeof bugs in old v8,
++ // IE 11 (#1621), and in Safari 8 (#1929).
++ if (typeof /./ != 'function' && typeof Int8Array != 'object') {
++ _.isFunction = function(obj) {
++ return typeof obj == 'function' || false;
++ };
++ }
++
++ // Is a given object a finite number?
++ _.isFinite = function(obj) {
++ return isFinite(obj) && !isNaN(parseFloat(obj));
++ };
++
++ // Is the given value `NaN`? (NaN is the only number which does not equal itself).
++ _.isNaN = function(obj) {
++ return _.isNumber(obj) && obj !== +obj;
++ };
++
++ // Is a given value a boolean?
++ _.isBoolean = function(obj) {
++ return obj === true || obj === false || toString.call(obj) === '[object Boolean]';
++ };
++
++ // Is a given value equal to null?
++ _.isNull = function(obj) {
++ return obj === null;
++ };
++
++ // Is a given variable undefined?
++ _.isUndefined = function(obj) {
++ return obj === void 0;
++ };
++
++ // Shortcut function for checking if an object has a given property directly
++ // on itself (in other words, not on a prototype).
++ _.has = function(obj, key) {
++ return obj != null && hasOwnProperty.call(obj, key);
++ };
++
++ // Utility Functions
++ // -----------------
++
++ // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
++ // previous owner. Returns a reference to the Underscore object.
++ _.noConflict = function() {
++ root._ = previousUnderscore;
++ return this;
++ };
++
++ // Keep the identity function around for default iteratees.
++ _.identity = function(value) {
++ return value;
++ };
++
++ // Predicate-generating functions. Often useful outside of Underscore.
++ _.constant = function(value) {
++ return function() {
++ return value;
++ };
++ };
++
++ _.noop = function(){};
++
++ _.property = property;
++
++ // Generates a function for a given object that returns a given property.
++ _.propertyOf = function(obj) {
++ return obj == null ? function(){} : function(key) {
++ return obj[key];
++ };
++ };
++
++ // Returns a predicate for checking whether an object has a given set of
++ // `key:value` pairs.
++ _.matcher = _.matches = function(attrs) {
++ attrs = _.extendOwn({}, attrs);
++ return function(obj) {
++ return _.isMatch(obj, attrs);
++ };
++ };
++
++ // Run a function **n** times.
++ _.times = function(n, iteratee, context) {
++ var accum = Array(Math.max(0, n));
++ iteratee = optimizeCb(iteratee, context, 1);
++ for (var i = 0; i < n; i++) accum[i] = iteratee(i);
++ return accum;
++ };
++
++ // Return a random integer between min and max (inclusive).
++ _.random = function(min, max) {
++ if (max == null) {
++ max = min;
++ min = 0;
++ }
++ return min + Math.floor(Math.random() * (max - min + 1));
++ };
++
++ // A (possibly faster) way to get the current timestamp as an integer.
++ _.now = Date.now || function() {
++ return new Date().getTime();
++ };
++
++ // List of HTML entities for escaping.
++ var escapeMap = {
++ '&': '&',
++ '<': '<',
++ '>': '>',
++ '"': '"',
++ "'": ''',
++ '`': '`'
++ };
++ var unescapeMap = _.invert(escapeMap);
++
++ // Functions for escaping and unescaping strings to/from HTML interpolation.
++ var createEscaper = function(map) {
++ var escaper = function(match) {
++ return map[match];
++ };
++ // Regexes for identifying a key that needs to be escaped
++ var source = '(?:' + _.keys(map).join('|') + ')';
++ var testRegexp = RegExp(source);
++ var replaceRegexp = RegExp(source, 'g');
++ return function(string) {
++ string = string == null ? '' : '' + string;
++ return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string;
++ };
++ };
++ _.escape = createEscaper(escapeMap);
++ _.unescape = createEscaper(unescapeMap);
++
++ // If the value of the named `property` is a function then invoke it with the
++ // `object` as context; otherwise, return it.
++ _.result = function(object, property, fallback) {
++ var value = object == null ? void 0 : object[property];
++ if (value === void 0) {
++ value = fallback;
++ }
++ return _.isFunction(value) ? value.call(object) : value;
++ };
++
++ // Generate a unique integer id (unique within the entire client session).
++ // Useful for temporary DOM ids.
++ var idCounter = 0;
++ _.uniqueId = function(prefix) {
++ var id = ++idCounter + '';
++ return prefix ? prefix + id : id;
++ };
++
++ // By default, Underscore uses ERB-style template delimiters, change the
++ // following template settings to use alternative delimiters.
++ _.templateSettings = {
++ evaluate : /<%([\s\S]+?)%>/g,
++ interpolate : /<%=([\s\S]+?)%>/g,
++ escape : /<%-([\s\S]+?)%>/g
++ };
++
++ // When customizing `templateSettings`, if you don't want to define an
++ // interpolation, evaluation or escaping regex, we need one that is
++ // guaranteed not to match.
++ var noMatch = /(.)^/;
++
++ // Certain characters need to be escaped so that they can be put into a
++ // string literal.
++ var escapes = {
++ "'": "'",
++ '\\': '\\',
++ '\r': 'r',
++ '\n': 'n',
++ '\u2028': 'u2028',
++ '\u2029': 'u2029'
++ };
++
++ var escaper = /\\|'|\r|\n|\u2028|\u2029/g;
++
++ var escapeChar = function(match) {
++ return '\\' + escapes[match];
++ };
++
++ // JavaScript micro-templating, similar to John Resig's implementation.
++ // Underscore templating handles arbitrary delimiters, preserves whitespace,
++ // and correctly escapes quotes within interpolated code.
++ // NB: `oldSettings` only exists for backwards compatibility.
++ _.template = function(text, settings, oldSettings) {
++ if (!settings && oldSettings) settings = oldSettings;
++ settings = _.defaults({}, settings, _.templateSettings);
++
++ // Combine delimiters into one regular expression via alternation.
++ var matcher = RegExp([
++ (settings.escape || noMatch).source,
++ (settings.interpolate || noMatch).source,
++ (settings.evaluate || noMatch).source
++ ].join('|') + '|$', 'g');
++
++ // Compile the template source, escaping string literals appropriately.
++ var index = 0;
++ var source = "__p+='";
++ text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
++ source += text.slice(index, offset).replace(escaper, escapeChar);
++ index = offset + match.length;
++
++ if (escape) {
++ source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
++ } else if (interpolate) {
++ source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
++ } else if (evaluate) {
++ source += "';\n" + evaluate + "\n__p+='";
++ }
++
++ // Adobe VMs need the match returned to produce the correct offest.
++ return match;
++ });
++ source += "';\n";
++
++ // If a variable is not specified, place data values in local scope.
++ if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
++
++ source = "var __t,__p='',__j=Array.prototype.join," +
++ "print=function(){__p+=__j.call(arguments,'');};\n" +
++ source + 'return __p;\n';
++
++ try {
++ var render = new Function(settings.variable || 'obj', '_', source);
++ } catch (e) {
++ e.source = source;
++ throw e;
++ }
++
++ var template = function(data) {
++ return render.call(this, data, _);
++ };
++
++ // Provide the compiled source as a convenience for precompilation.
++ var argument = settings.variable || 'obj';
++ template.source = 'function(' + argument + '){\n' + source + '}';
++
++ return template;
++ };
++
++ // Add a "chain" function. Start chaining a wrapped Underscore object.
++ _.chain = function(obj) {
++ var instance = _(obj);
++ instance._chain = true;
++ return instance;
++ };
++
++ // OOP
++ // ---------------
++ // If Underscore is called as a function, it returns a wrapped object that
++ // can be used OO-style. This wrapper holds altered versions of all the
++ // underscore functions. Wrapped objects may be chained.
++
++ // Helper function to continue chaining intermediate results.
++ var result = function(instance, obj) {
++ return instance._chain ? _(obj).chain() : obj;
++ };
++
++ // Add your own custom functions to the Underscore object.
++ _.mixin = function(obj) {
++ _.each(_.functions(obj), function(name) {
++ var func = _[name] = obj[name];
++ _.prototype[name] = function() {
++ var args = [this._wrapped];
++ push.apply(args, arguments);
++ return result(this, func.apply(_, args));
++ };
++ });
++ };
++
++ // Add all of the Underscore functions to the wrapper object.
++ _.mixin(_);
++
++ // Add all mutator Array functions to the wrapper.
++ _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
++ var method = ArrayProto[name];
++ _.prototype[name] = function() {
++ var obj = this._wrapped;
++ method.apply(obj, arguments);
++ if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0];
++ return result(this, obj);
++ };
++ });
++
++ // Add all accessor Array functions to the wrapper.
++ _.each(['concat', 'join', 'slice'], function(name) {
++ var method = ArrayProto[name];
++ _.prototype[name] = function() {
++ return result(this, method.apply(this._wrapped, arguments));
++ };
++ });
++
++ // Extracts the result from a wrapped and chained object.
++ _.prototype.value = function() {
++ return this._wrapped;
++ };
++
++ // Provide unwrapping proxy for some methods used in engine operations
++ // such as arithmetic and JSON stringification.
++ _.prototype.valueOf = _.prototype.toJSON = _.prototype.value;
++
++ _.prototype.toString = function() {
++ return '' + this._wrapped;
++ };
++
++ // AMD registration happens at the end for compatibility with AMD loaders
++ // that may not enforce next-turn semantics on modules. Even though general
++ // practice for AMD registration is to be anonymous, underscore registers
++ // as a named module because, like jQuery, it is a base library that is
++ // popular enough to be bundled in a third party lib, but not be part of
++ // an AMD load request. Those cases could generate an error when an
++ // anonymous define() is called outside of a loader request.
++ if (typeof define === 'function' && define.amd) {
++ define('underscore', [], function() {
++ return _;
++ });
++ }
++}.call(this));
--- /dev/null
--- /dev/null
++# jars installed by jh_installlib, unfortunately not removed from tmp
++debian/tmp/usr/share/java/libcephfs.jar
++debian/tmp/usr/share/java/libcephfs-test.jar
++# ceph-deploy is another source package, not much point
++# in installing this manpage
++debian/tmp/usr/share/man/man8/ceph-deploy.8
++# Don't install python bytecode. This will be generated on installation
++# by dh-python
++debian/tmp/usr/lib/python2.7/dist-packages/ceph_rest_api.pyc
++debian/tmp/usr/lib/python2.7/dist-packages/ceph_daemon.pyc
++debian/tmp/usr/lib/python2.7/dist-packages/ceph_volume_client.pyc
++debian/tmp/usr/lib/python2.7/dist-packages/ceph_argparse.pyc
--- /dev/null
--- /dev/null
++From 8b2811f9871029023cd6b798f753743df9be36e7 Mon Sep 17 00:00:00 2001
++From: Andrew Kryczka <andrewkr@fb.com>
++Date: Wed, 10 May 2017 18:16:31 -0700
++Subject: [PATCH 1/3] CoreLocalArray class
++
++Summary:
++Moved the logic for core-local array out of ConcurrentArena and into a separate class because I want to reuse it for core-local stats.
++Closes https://github.com/facebook/rocksdb/pull/2256
++
++Differential Revision: D5011518
++
++Pulled By: ajkr
++
++fbshipit-source-id: a75a7b8f7b7a42fd6273489ada405f14c6be196a
++(cherry picked from commit cda5fde2d96624df38afc7f02b6b3e699648c62d)
++---
++ util/concurrent_arena.cc | 25 ++++----------
++ util/concurrent_arena.h | 25 +++++++-------
++ util/core_local.h | 84 ++++++++++++++++++++++++++++++++++++++++++++++++
++ 3 files changed, 103 insertions(+), 31 deletions(-)
++ create mode 100644 util/core_local.h
++
++diff --git a/src/rocksdb/util/concurrent_arena.cc b/src/rocksdb/util/concurrent_arena.cc
++index df87011..a0feb93 100644
++--- a/src/rocksdb/util/concurrent_arena.cc
+++++ b/src/rocksdb/util/concurrent_arena.cc
++@@ -16,35 +16,24 @@
++ namespace rocksdb {
++
++ #ifdef ROCKSDB_SUPPORT_THREAD_LOCAL
++-__thread uint32_t ConcurrentArena::tls_cpuid = 0;
+++__thread size_t ConcurrentArena::tls_cpuid = 0;
++ #endif
++
++ ConcurrentArena::ConcurrentArena(size_t block_size, size_t huge_page_size)
++- : shard_block_size_(block_size / 8), arena_(block_size, huge_page_size) {
++- // find a power of two >= num_cpus and >= 8
++- auto num_cpus = std::thread::hardware_concurrency();
++- index_mask_ = 7;
++- while (index_mask_ + 1 < num_cpus) {
++- index_mask_ = index_mask_ * 2 + 1;
++- }
++-
++- shards_.reset(new Shard[index_mask_ + 1]);
+++ : shard_block_size_(block_size / 8),
+++ shards_(),
+++ arena_(block_size, huge_page_size) {
++ Fixup();
++ }
++
++ ConcurrentArena::Shard* ConcurrentArena::Repick() {
++- int cpuid = port::PhysicalCoreID();
++- if (UNLIKELY(cpuid < 0)) {
++- // cpu id unavailable, just pick randomly
++- cpuid =
++- Random::GetTLSInstance()->Uniform(static_cast<int>(index_mask_) + 1);
++- }
+++ auto shard_and_index = shards_.AccessElementAndIndex();
++ #ifdef ROCKSDB_SUPPORT_THREAD_LOCAL
++ // even if we are cpu 0, use a non-zero tls_cpuid so we can tell we
++ // have repicked
++- tls_cpuid = cpuid | (static_cast<int>(index_mask_) + 1);
+++ tls_cpuid = shard_and_index.second | shards_.Size();
++ #endif
++- return &shards_[cpuid & index_mask_];
+++ return shard_and_index.first;
++ }
++
++ } // namespace rocksdb
++diff --git a/src/rocksdb/util/concurrent_arena.h b/src/rocksdb/util/concurrent_arena.h
++index 3a20bb6..a6db1e9 100644
++--- a/src/rocksdb/util/concurrent_arena.h
+++++ b/src/rocksdb/util/concurrent_arena.h
++@@ -14,6 +14,7 @@
++ #include "port/likely.h"
++ #include "util/allocator.h"
++ #include "util/arena.h"
+++#include "util/core_local.h"
++ #include "util/mutexlock.h"
++ #include "util/thread_local.h"
++
++@@ -63,9 +64,7 @@ class ConcurrentArena : public Allocator {
++
++ size_t ApproximateMemoryUsage() const {
++ std::unique_lock<SpinMutex> lock(arena_mutex_, std::defer_lock);
++- if (index_mask_ != 0) {
++- lock.lock();
++- }
+++ lock.lock();
++ return arena_.ApproximateMemoryUsage() - ShardAllocatedAndUnused();
++ }
++
++@@ -95,18 +94,16 @@ class ConcurrentArena : public Allocator {
++ };
++
++ #ifdef ROCKSDB_SUPPORT_THREAD_LOCAL
++- static __thread uint32_t tls_cpuid;
+++ static __thread size_t tls_cpuid;
++ #else
++- enum ZeroFirstEnum : uint32_t { tls_cpuid = 0 };
+++ enum ZeroFirstEnum : size_t { tls_cpuid = 0 };
++ #endif
++
++ char padding0[56] ROCKSDB_FIELD_UNUSED;
++
++ size_t shard_block_size_;
++
++- // shards_[i & index_mask_] is valid
++- size_t index_mask_;
++- std::unique_ptr<Shard[]> shards_;
+++ CoreLocalArray<Shard> shards_;
++
++ Arena arena_;
++ mutable SpinMutex arena_mutex_;
++@@ -120,15 +117,16 @@ class ConcurrentArena : public Allocator {
++
++ size_t ShardAllocatedAndUnused() const {
++ size_t total = 0;
++- for (size_t i = 0; i <= index_mask_; ++i) {
++- total += shards_[i].allocated_and_unused_.load(std::memory_order_relaxed);
+++ for (size_t i = 0; i < shards_.Size(); ++i) {
+++ total += shards_.AccessAtCore(i)->allocated_and_unused_.load(
+++ std::memory_order_relaxed);
++ }
++ return total;
++ }
++
++ template <typename Func>
++ char* AllocateImpl(size_t bytes, bool force_arena, const Func& func) {
++- uint32_t cpu;
+++ size_t cpu;
++
++ // Go directly to the arena if the allocation is too large, or if
++ // we've never needed to Repick() and the arena mutex is available
++@@ -137,7 +135,8 @@ class ConcurrentArena : public Allocator {
++ std::unique_lock<SpinMutex> arena_lock(arena_mutex_, std::defer_lock);
++ if (bytes > shard_block_size_ / 4 || force_arena ||
++ ((cpu = tls_cpuid) == 0 &&
++- !shards_[0].allocated_and_unused_.load(std::memory_order_relaxed) &&
+++ !shards_.AccessAtCore(0)->allocated_and_unused_.load(
+++ std::memory_order_relaxed) &&
++ arena_lock.try_lock())) {
++ if (!arena_lock.owns_lock()) {
++ arena_lock.lock();
++@@ -148,7 +147,7 @@ class ConcurrentArena : public Allocator {
++ }
++
++ // pick a shard from which to allocate
++- Shard* s = &shards_[cpu & index_mask_];
+++ Shard* s = shards_.AccessAtCore(cpu & (shards_.Size() - 1));
++ if (!s->mutex.try_lock()) {
++ s = Repick();
++ s->mutex.lock();
++diff --git a/src/rocksdb/util/core_local.h b/src/rocksdb/util/core_local.h
++new file mode 100644
++index 0000000..806584d
++--- /dev/null
+++++ b/src/rocksdb/util/core_local.h
++@@ -0,0 +1,84 @@
+++// Copyright (c) 2017-present, Facebook, Inc. All rights reserved.
+++// This source code is licensed under the BSD-style license found in the
+++// LICENSE file in the root directory of this source tree. An additional grant
+++// of patent rights can be found in the PATENTS file in the same directory.
+++// This source code is also licensed under the GPLv2 license found in the
+++// COPYING file in the root directory of this source tree.
+++
+++#pragma once
+++
+++#include "port/likely.h"
+++#include "port/port.h"
+++#include "util/random.h"
+++
+++#include <cstddef>
+++#include <thread>
+++#include <vector>
+++
+++namespace rocksdb {
+++
+++// An array of core-local values. Ideally the value type, T, is cache aligned to
+++// prevent false sharing.
+++template<typename T>
+++class CoreLocalArray {
+++ public:
+++ CoreLocalArray();
+++
+++ size_t Size() const;
+++ // returns pointer to the element corresponding to the core that the thread
+++ // currently runs on.
+++ T* Access() const;
+++ // same as above, but also returns the core index, which the client can cache
+++ // to reduce how often core ID needs to be retrieved. Only do this if some
+++ // inaccuracy is tolerable, as the thread may migrate to a different core.
+++ std::pair<T*, size_t> AccessElementAndIndex() const;
+++ // returns pointer to element for the specified core index. This can be used,
+++ // e.g., for aggregation, or if the client caches core index.
+++ T* AccessAtCore(size_t core_idx) const;
+++
+++ private:
+++ std::unique_ptr<T[]> data_;
+++ size_t size_shift_;
+++};
+++
+++template<typename T>
+++CoreLocalArray<T>::CoreLocalArray() {
+++ unsigned int num_cpus = std::thread::hardware_concurrency();
+++ // find a power of two >= num_cpus and >= 8
+++ size_shift_ = 3;
+++ while (1u << size_shift_ < num_cpus) {
+++ ++size_shift_;
+++ }
+++ data_.reset(new T[1 << size_shift_]);
+++}
+++
+++template<typename T>
+++size_t CoreLocalArray<T>::Size() const {
+++ return 1u << size_shift_;
+++}
+++
+++template<typename T>
+++T* CoreLocalArray<T>::Access() const {
+++ return AccessElementAndIndex().first;
+++}
+++
+++template<typename T>
+++std::pair<T*, size_t> CoreLocalArray<T>::AccessElementAndIndex() const {
+++ int cpuid = port::PhysicalCoreID();
+++ size_t core_idx;
+++ if (UNLIKELY(cpuid < 0)) {
+++ // cpu id unavailable, just pick randomly
+++ core_idx = Random::GetTLSInstance()->Uniform(1 << size_shift_);
+++ } else {
+++ core_idx = static_cast<size_t>(cpuid & ((1 << size_shift_) - 1));
+++ }
+++ return {AccessAtCore(core_idx), core_idx};
+++}
+++
+++template<typename T>
+++T* CoreLocalArray<T>::AccessAtCore(size_t core_idx) const {
+++ assert(core_idx < 1u << size_shift_);
+++ return &data_[core_idx];
+++}
+++
+++} // namespace rocksdb
++--
++2.7.4
++
--- /dev/null
--- /dev/null
++From 26d95dc4fdd72bbc52ae21ff962e15c53a6ffbec Mon Sep 17 00:00:00 2001
++From: Andrew Kryczka <andrewkr@fb.com>
++Date: Fri, 12 May 2017 09:26:40 -0700
++Subject: [PATCH 2/3] core-local array type conversions
++
++Summary:
++try to clean up the type conversions and hope it passes on windows.
++
++one interesting thing I learned is that bitshift operations are special: in `x << y`, the result type depends only on the type of `x`, unlike most arithmetic operations where the result type depends on both operands' types.
++Closes https://github.com/facebook/rocksdb/pull/2277
++
++Differential Revision: D5050145
++
++Pulled By: ajkr
++
++fbshipit-source-id: f3309e77526ac9612c632bf93a62d99757af9a29
++(cherry picked from commit bbe9ee7dd4a542b191ace521ca13b4bdb063008b)
++---
++ util/core_local.h | 12 ++++++------
++ 1 file changed, 6 insertions(+), 6 deletions(-)
++
++diff --git a/src/rocksdb/util/core_local.h b/src/rocksdb/util/core_local.h
++index 806584d..7515c54 100644
++--- a/src/rocksdb/util/core_local.h
+++++ b/src/rocksdb/util/core_local.h
++@@ -38,23 +38,23 @@ class CoreLocalArray {
++
++ private:
++ std::unique_ptr<T[]> data_;
++- size_t size_shift_;
+++ int size_shift_;
++ };
++
++ template<typename T>
++ CoreLocalArray<T>::CoreLocalArray() {
++- unsigned int num_cpus = std::thread::hardware_concurrency();
+++ int num_cpus = static_cast<int>(std::thread::hardware_concurrency());
++ // find a power of two >= num_cpus and >= 8
++ size_shift_ = 3;
++- while (1u << size_shift_ < num_cpus) {
+++ while (1 << size_shift_ < num_cpus) {
++ ++size_shift_;
++ }
++- data_.reset(new T[1 << size_shift_]);
+++ data_.reset(new T[static_cast<size_t>(1) << size_shift_]);
++ }
++
++ template<typename T>
++ size_t CoreLocalArray<T>::Size() const {
++- return 1u << size_shift_;
+++ return static_cast<size_t>(1) << size_shift_;
++ }
++
++ template<typename T>
++@@ -77,7 +77,7 @@ std::pair<T*, size_t> CoreLocalArray<T>::AccessElementAndIndex() const {
++
++ template<typename T>
++ T* CoreLocalArray<T>::AccessAtCore(size_t core_idx) const {
++- assert(core_idx < 1u << size_shift_);
+++ assert(core_idx < static_cast<size_t>(1) << size_shift_);
++ return &data_[core_idx];
++ }
++
++--
++2.7.4
++
--- /dev/null
--- /dev/null
++From 30c2cdc833a62953f380580b1769fec63f770b21 Mon Sep 17 00:00:00 2001
++From: Andrew Kryczka <andrewkr@fb.com>
++Date: Tue, 23 May 2017 10:29:14 -0700
++Subject: [PATCH 3/3] Core-local statistics
++
++Summary:
++This diff changes `StatisticsImpl` from a thread-local approach to a core-local one. The goal is to perform faster aggregations, particularly for applications that have many threads. There should be no behavior change.
++Closes https://github.com/facebook/rocksdb/pull/2258
++
++Differential Revision: D5016258
++
++Pulled By: ajkr
++
++fbshipit-source-id: 7d4d165b4a91d8110f0409d113d1be91f22d31a9
++(cherry picked from commit ac39d6bec5b2c23a2c3fd0f0e61d468be4f3e803)
++---
++ HISTORY.md | 4 ++
++ monitoring/statistics.cc | 129 +++++++++++++----------------------------------
++ monitoring/statistics.h | 111 +++++++++++-----------------------------
++ util/core_local.h | 21 ++++----
++ 4 files changed, 78 insertions(+), 187 deletions(-)
++
++diff --git a/src/rocksdb/HISTORY.md b/src/rocksdb/HISTORY.md
++index 7b51d37..4cde9e2 100644
++--- a/src/rocksdb/HISTORY.md
+++++ b/src/rocksdb/HISTORY.md
++@@ -1,6 +1,10 @@
++ # Rocksdb Change Log
++ ## Unreleased
++ ### New Features
+++* Change ticker/histogram statistics implementations to use core-local storage. This improves aggregation speed compared to our previous thread-local approach, particularly for applications with many threads.
+++
+++## 5.5.0 (05/17/2017)
+++### New Features
++ * DB::ResetStats() to reset internal stats.
++ * Statistics::Reset() to reset user stats.
++ * ldb add option --try_load_options, which will open DB with its own option file.
++diff --git a/src/rocksdb/monitoring/statistics.cc b/src/rocksdb/monitoring/statistics.cc
++index fb5634f..3a69a13 100644
++--- a/src/rocksdb/monitoring/statistics.cc
+++++ b/src/rocksdb/monitoring/statistics.cc
++@@ -21,13 +21,9 @@ std::shared_ptr<Statistics> CreateDBStatistics() {
++ return std::make_shared<StatisticsImpl>(nullptr, false);
++ }
++
++-StatisticsImpl::StatisticsImpl(
++- std::shared_ptr<Statistics> stats,
++- bool enable_internal_stats)
++- : stats_shared_(stats),
++- stats_(stats.get()),
++- enable_internal_stats_(enable_internal_stats) {
++-}
+++StatisticsImpl::StatisticsImpl(std::shared_ptr<Statistics> stats,
+++ bool enable_internal_stats)
+++ : stats_(std::move(stats)), enable_internal_stats_(enable_internal_stats) {}
++
++ StatisticsImpl::~StatisticsImpl() {}
++
++@@ -41,79 +37,36 @@ uint64_t StatisticsImpl::getTickerCountLocked(uint32_t tickerType) const {
++ enable_internal_stats_ ?
++ tickerType < INTERNAL_TICKER_ENUM_MAX :
++ tickerType < TICKER_ENUM_MAX);
++- uint64_t thread_local_sum = 0;
++- tickers_[tickerType].thread_value->Fold(
++- [](void* curr_ptr, void* res) {
++- auto* sum_ptr = static_cast<uint64_t*>(res);
++- *sum_ptr += static_cast<std::atomic_uint_fast64_t*>(curr_ptr)->load(
++- std::memory_order_relaxed);
++- },
++- &thread_local_sum);
++- return thread_local_sum +
++- tickers_[tickerType].merged_sum.load(std::memory_order_relaxed);
++-}
++-
++-std::unique_ptr<HistogramImpl>
++-StatisticsImpl::HistogramInfo::getMergedHistogram() const {
++- std::unique_ptr<HistogramImpl> res_hist(new HistogramImpl());
++- {
++- MutexLock lock(&merge_lock);
++- res_hist->Merge(merged_hist);
+++ uint64_t res = 0;
+++ for (size_t core_idx = 0; core_idx < per_core_stats_.Size(); ++core_idx) {
+++ res += per_core_stats_.AccessAtCore(core_idx)->tickers_[tickerType];
++ }
++- thread_value->Fold(
++- [](void* curr_ptr, void* res) {
++- auto tmp_res_hist = static_cast<HistogramImpl*>(res);
++- auto curr_hist = static_cast<HistogramImpl*>(curr_ptr);
++- tmp_res_hist->Merge(*curr_hist);
++- },
++- res_hist.get());
++- return res_hist;
+++ return res;
++ }
++
++ void StatisticsImpl::histogramData(uint32_t histogramType,
++ HistogramData* const data) const {
++ MutexLock lock(&aggregate_lock_);
++- histogramDataLocked(histogramType, data);
+++ getHistogramImplLocked(histogramType)->Data(data);
++ }
++
++-void StatisticsImpl::histogramDataLocked(uint32_t histogramType,
++- HistogramData* const data) const {
+++std::unique_ptr<HistogramImpl> StatisticsImpl::getHistogramImplLocked(
+++ uint32_t histogramType) const {
++ assert(
++ enable_internal_stats_ ?
++ histogramType < INTERNAL_HISTOGRAM_ENUM_MAX :
++ histogramType < HISTOGRAM_ENUM_MAX);
++- histograms_[histogramType].getMergedHistogram()->Data(data);
+++ std::unique_ptr<HistogramImpl> res_hist(new HistogramImpl());
+++ for (size_t core_idx = 0; core_idx < per_core_stats_.Size(); ++core_idx) {
+++ res_hist->Merge(
+++ per_core_stats_.AccessAtCore(core_idx)->histograms_[histogramType]);
+++ }
+++ return res_hist;
++ }
++
++ std::string StatisticsImpl::getHistogramString(uint32_t histogramType) const {
++ MutexLock lock(&aggregate_lock_);
++- assert(enable_internal_stats_ ? histogramType < INTERNAL_HISTOGRAM_ENUM_MAX
++- : histogramType < HISTOGRAM_ENUM_MAX);
++- return histograms_[histogramType].getMergedHistogram()->ToString();
++-}
++-
++-StatisticsImpl::ThreadTickerInfo* StatisticsImpl::getThreadTickerInfo(
++- uint32_t tickerType) {
++- auto info_ptr =
++- static_cast<ThreadTickerInfo*>(tickers_[tickerType].thread_value->Get());
++- if (info_ptr == nullptr) {
++- info_ptr =
++- new ThreadTickerInfo(0 /* value */, &tickers_[tickerType].merged_sum);
++- tickers_[tickerType].thread_value->Reset(info_ptr);
++- }
++- return info_ptr;
++-}
++-
++-StatisticsImpl::ThreadHistogramInfo* StatisticsImpl::getThreadHistogramInfo(
++- uint32_t histogram_type) {
++- auto info_ptr = static_cast<ThreadHistogramInfo*>(
++- histograms_[histogram_type].thread_value->Get());
++- if (info_ptr == nullptr) {
++- info_ptr = new ThreadHistogramInfo(&histograms_[histogram_type].merged_hist,
++- &histograms_[histogram_type].merge_lock);
++- histograms_[histogram_type].thread_value->Reset(info_ptr);
++- }
++- return info_ptr;
+++ return getHistogramImplLocked(histogramType)->ToString();
++ }
++
++ void StatisticsImpl::setTickerCount(uint32_t tickerType, uint64_t count) {
++@@ -129,14 +82,12 @@ void StatisticsImpl::setTickerCount(uint32_t tickerType, uint64_t count) {
++ void StatisticsImpl::setTickerCountLocked(uint32_t tickerType, uint64_t count) {
++ assert(enable_internal_stats_ ? tickerType < INTERNAL_TICKER_ENUM_MAX
++ : tickerType < TICKER_ENUM_MAX);
++- if (tickerType < TICKER_ENUM_MAX || enable_internal_stats_) {
++- tickers_[tickerType].thread_value->Fold(
++- [](void* curr_ptr, void* res) {
++- static_cast<std::atomic<uint64_t>*>(curr_ptr)->store(
++- 0, std::memory_order_relaxed);
++- },
++- nullptr /* res */);
++- tickers_[tickerType].merged_sum.store(count, std::memory_order_relaxed);
+++ for (size_t core_idx = 0; core_idx < per_core_stats_.Size(); ++core_idx) {
+++ if (core_idx == 0) {
+++ per_core_stats_.AccessAtCore(core_idx)->tickers_[tickerType] = count;
+++ } else {
+++ per_core_stats_.AccessAtCore(core_idx)->tickers_[tickerType] = 0;
+++ }
++ }
++ }
++
++@@ -146,16 +97,10 @@ uint64_t StatisticsImpl::getAndResetTickerCount(uint32_t tickerType) {
++ MutexLock lock(&aggregate_lock_);
++ assert(enable_internal_stats_ ? tickerType < INTERNAL_TICKER_ENUM_MAX
++ : tickerType < TICKER_ENUM_MAX);
++- if (tickerType < TICKER_ENUM_MAX || enable_internal_stats_) {
++- tickers_[tickerType].thread_value->Fold(
++- [](void* curr_ptr, void* res) {
++- auto* sum_ptr = static_cast<uint64_t*>(res);
++- *sum_ptr += static_cast<std::atomic<uint64_t>*>(curr_ptr)->exchange(
++- 0, std::memory_order_relaxed);
++- },
++- &sum);
++- sum += tickers_[tickerType].merged_sum.exchange(
++- 0, std::memory_order_relaxed);
+++ for (size_t core_idx = 0; core_idx < per_core_stats_.Size(); ++core_idx) {
+++ sum +=
+++ per_core_stats_.AccessAtCore(core_idx)->tickers_[tickerType].exchange(
+++ 0, std::memory_order_relaxed);
++ }
++ }
++ if (stats_ && tickerType < TICKER_ENUM_MAX) {
++@@ -169,10 +114,8 @@ void StatisticsImpl::recordTick(uint32_t tickerType, uint64_t count) {
++ enable_internal_stats_ ?
++ tickerType < INTERNAL_TICKER_ENUM_MAX :
++ tickerType < TICKER_ENUM_MAX);
++- if (tickerType < TICKER_ENUM_MAX || enable_internal_stats_) {
++- auto info_ptr = getThreadTickerInfo(tickerType);
++- info_ptr->value.fetch_add(count, std::memory_order_relaxed);
++- }
+++ per_core_stats_.Access()->tickers_[tickerType].fetch_add(
+++ count, std::memory_order_relaxed);
++ if (stats_ && tickerType < TICKER_ENUM_MAX) {
++ stats_->recordTick(tickerType, count);
++ }
++@@ -183,9 +126,7 @@ void StatisticsImpl::measureTime(uint32_t histogramType, uint64_t value) {
++ enable_internal_stats_ ?
++ histogramType < INTERNAL_HISTOGRAM_ENUM_MAX :
++ histogramType < HISTOGRAM_ENUM_MAX);
++- if (histogramType < HISTOGRAM_ENUM_MAX || enable_internal_stats_) {
++- getThreadHistogramInfo(histogramType)->value.Add(value);
++- }
+++ per_core_stats_.Access()->histograms_[histogramType].Add(value);
++ if (stats_ && histogramType < HISTOGRAM_ENUM_MAX) {
++ stats_->measureTime(histogramType, value);
++ }
++@@ -197,11 +138,9 @@ Status StatisticsImpl::Reset() {
++ setTickerCountLocked(i, 0);
++ }
++ for (uint32_t i = 0; i < HISTOGRAM_ENUM_MAX; ++i) {
++- histograms_[i].thread_value->Fold(
++- [](void* curr_ptr, void* res) {
++- static_cast<HistogramImpl*>(curr_ptr)->Clear();
++- },
++- nullptr /* res */);
+++ for (size_t core_idx = 0; core_idx < per_core_stats_.Size(); ++core_idx) {
+++ per_core_stats_.AccessAtCore(core_idx)->histograms_[i].Clear();
+++ }
++ }
++ return Status::OK();
++ }
++@@ -229,7 +168,7 @@ std::string StatisticsImpl::ToString() const {
++ if (h.first < HISTOGRAM_ENUM_MAX || enable_internal_stats_) {
++ char buffer[kTmpStrBufferSize];
++ HistogramData hData;
++- histogramDataLocked(h.first, &hData);
+++ getHistogramImplLocked(h.first)->Data(&hData);
++ snprintf(
++ buffer, kTmpStrBufferSize,
++ "%s statistics Percentiles :=> 50 : %f 95 : %f 99 : %f 100 : %f\n",
++diff --git a/src/rocksdb/monitoring/statistics.h b/src/rocksdb/monitoring/statistics.h
++index 32b7036..96b31a3 100644
++--- a/src/rocksdb/monitoring/statistics.h
+++++ b/src/rocksdb/monitoring/statistics.h
++@@ -13,8 +13,14 @@
++ #include "monitoring/histogram.h"
++ #include "port/likely.h"
++ #include "port/port.h"
+++#include "util/core_local.h"
++ #include "util/mutexlock.h"
++-#include "util/thread_local.h"
+++
+++#ifdef __clang__
+++#define ROCKSDB_FIELD_UNUSED __attribute__((__unused__))
+++#else
+++#define ROCKSDB_FIELD_UNUSED
+++#endif // __clang__
++
++ namespace rocksdb {
++
++@@ -50,97 +56,38 @@ class StatisticsImpl : public Statistics {
++ virtual bool HistEnabledForType(uint32_t type) const override;
++
++ private:
++- std::shared_ptr<Statistics> stats_shared_;
++- Statistics* stats_;
+++ // If non-nullptr, forwards updates to the object pointed to by `stats_`.
+++ std::shared_ptr<Statistics> stats_;
+++ // TODO(ajkr): clean this up since there are no internal stats anymore
++ bool enable_internal_stats_;
++- // Synchronizes anything that operates on other threads' thread-specific data
+++ // Synchronizes anything that operates across other cores' local data,
++ // such that operations like Reset() can be performed atomically.
++ mutable port::Mutex aggregate_lock_;
++
++- // Holds data maintained by each thread for implementing tickers.
++- struct ThreadTickerInfo {
++- std::atomic_uint_fast64_t value;
++- // During teardown, value will be summed into *merged_sum.
++- std::atomic_uint_fast64_t* merged_sum;
++-
++- ThreadTickerInfo(uint_fast64_t _value,
++- std::atomic_uint_fast64_t* _merged_sum)
++- : value(_value), merged_sum(_merged_sum) {}
+++ // The ticker/histogram data are stored in this structure, which we will store
+++ // per-core. It is cache-aligned, so tickers/histograms belonging to different
+++ // cores can never share the same cache line.
+++ //
+++ // Alignment attributes expand to nothing depending on the platform
+++ struct StatisticsData {
+++ std::atomic_uint_fast64_t tickers_[INTERNAL_TICKER_ENUM_MAX] = {{0}};
+++ HistogramImpl histograms_[INTERNAL_HISTOGRAM_ENUM_MAX];
+++ char
+++ padding[(CACHE_LINE_SIZE -
+++ (INTERNAL_TICKER_ENUM_MAX * sizeof(std::atomic_uint_fast64_t) +
+++ INTERNAL_HISTOGRAM_ENUM_MAX * sizeof(HistogramImpl)) %
+++ CACHE_LINE_SIZE) %
+++ CACHE_LINE_SIZE] ROCKSDB_FIELD_UNUSED;
++ };
++
++- // Holds data maintained by each thread for implementing histograms.
++- struct ThreadHistogramInfo {
++- HistogramImpl value;
++- // During teardown, value will be merged into *merged_hist while holding
++- // *merge_lock, which also syncs with the merges necessary for reads.
++- HistogramImpl* merged_hist;
++- port::Mutex* merge_lock;
+++ static_assert(sizeof(StatisticsData) % 64 == 0, "Expected 64-byte aligned");
++
++- ThreadHistogramInfo(HistogramImpl* _merged_hist, port::Mutex* _merge_lock)
++- : value(), merged_hist(_merged_hist), merge_lock(_merge_lock) {}
++- };
++-
++- // Holds global data for implementing tickers.
++- struct TickerInfo {
++- TickerInfo()
++- : thread_value(new ThreadLocalPtr(&mergeThreadValue)), merged_sum(0) {}
++- // Holds thread-specific pointer to ThreadTickerInfo
++- std::unique_ptr<ThreadLocalPtr> thread_value;
++- // Sum of thread-specific values for tickers that have been reset due to
++- // thread termination or ThreadLocalPtr destruction. Also, this is used by
++- // setTickerCount() to conveniently change the global value by setting this
++- // while simultaneously zeroing all thread-local values.
++- std::atomic_uint_fast64_t merged_sum;
++-
++- static void mergeThreadValue(void* ptr) {
++- auto info_ptr = static_cast<ThreadTickerInfo*>(ptr);
++- *info_ptr->merged_sum += info_ptr->value;
++- delete info_ptr;
++- }
++- };
++-
++- // Holds global data for implementing histograms.
++- struct HistogramInfo {
++- HistogramInfo()
++- : merged_hist(),
++- merge_lock(),
++- thread_value(new ThreadLocalPtr(&mergeThreadValue)) {}
++- // Merged thread-specific values for histograms that have been reset due to
++- // thread termination or ThreadLocalPtr destruction. Note these must be
++- // destroyed after thread_value since its destructor accesses them.
++- HistogramImpl merged_hist;
++- mutable port::Mutex merge_lock;
++- // Holds thread-specific pointer to ThreadHistogramInfo
++- std::unique_ptr<ThreadLocalPtr> thread_value;
++-
++- static void mergeThreadValue(void* ptr) {
++- auto info_ptr = static_cast<ThreadHistogramInfo*>(ptr);
++- {
++- MutexLock lock(info_ptr->merge_lock);
++- info_ptr->merged_hist->Merge(info_ptr->value);
++- }
++- delete info_ptr;
++- }
++-
++- // Returns a histogram that merges all histograms (thread-specific and
++- // previously merged ones).
++- std::unique_ptr<HistogramImpl> getMergedHistogram() const;
++- };
+++ CoreLocalArray<StatisticsData> per_core_stats_;
++
++ uint64_t getTickerCountLocked(uint32_t ticker_type) const;
++- void histogramDataLocked(uint32_t histogram_type,
++- HistogramData* const data) const;
+++ std::unique_ptr<HistogramImpl> getHistogramImplLocked(
+++ uint32_t histogram_type) const;
++ void setTickerCountLocked(uint32_t ticker_type, uint64_t count);
++-
++- // Returns the info for this tickerType/thread. It sets a new info with zeroed
++- // counter if none exists.
++- ThreadTickerInfo* getThreadTickerInfo(uint32_t ticker_type);
++- // Returns the info for this histogramType/thread. It sets a new histogram
++- // with zeroed data if none exists.
++- ThreadHistogramInfo* getThreadHistogramInfo(uint32_t histogram_type);
++-
++- TickerInfo tickers_[INTERNAL_TICKER_ENUM_MAX];
++- HistogramInfo histograms_[INTERNAL_HISTOGRAM_ENUM_MAX];
++ };
++
++ // Utility functions
++diff --git a/src/rocksdb/util/core_local.h b/src/rocksdb/util/core_local.h
++index 7515c54..4239df6 100644
++--- a/src/rocksdb/util/core_local.h
+++++ b/src/rocksdb/util/core_local.h
++@@ -7,19 +7,20 @@
++
++ #pragma once
++
++-#include "port/likely.h"
++-#include "port/port.h"
++-#include "util/random.h"
++-
++ #include <cstddef>
++ #include <thread>
+++#include <utility>
++ #include <vector>
++
+++#include "port/likely.h"
+++#include "port/port.h"
+++#include "util/random.h"
+++
++ namespace rocksdb {
++
++ // An array of core-local values. Ideally the value type, T, is cache aligned to
++ // prevent false sharing.
++-template<typename T>
+++template <typename T>
++ class CoreLocalArray {
++ public:
++ CoreLocalArray();
++@@ -41,7 +42,7 @@ class CoreLocalArray {
++ int size_shift_;
++ };
++
++-template<typename T>
+++template <typename T>
++ CoreLocalArray<T>::CoreLocalArray() {
++ int num_cpus = static_cast<int>(std::thread::hardware_concurrency());
++ // find a power of two >= num_cpus and >= 8
++@@ -52,17 +53,17 @@ CoreLocalArray<T>::CoreLocalArray() {
++ data_.reset(new T[static_cast<size_t>(1) << size_shift_]);
++ }
++
++-template<typename T>
+++template <typename T>
++ size_t CoreLocalArray<T>::Size() const {
++ return static_cast<size_t>(1) << size_shift_;
++ }
++
++-template<typename T>
+++template <typename T>
++ T* CoreLocalArray<T>::Access() const {
++ return AccessElementAndIndex().first;
++ }
++
++-template<typename T>
+++template <typename T>
++ std::pair<T*, size_t> CoreLocalArray<T>::AccessElementAndIndex() const {
++ int cpuid = port::PhysicalCoreID();
++ size_t core_idx;
++@@ -75,7 +76,7 @@ std::pair<T*, size_t> CoreLocalArray<T>::AccessElementAndIndex() const {
++ return {AccessAtCore(core_idx), core_idx};
++ }
++
++-template<typename T>
+++template <typename T>
++ T* CoreLocalArray<T>::AccessAtCore(size_t core_idx) const {
++ assert(core_idx < static_cast<size_t>(1) << size_shift_);
++ return &data_[core_idx];
++--
++2.7.4
++
--- /dev/null
--- /dev/null
++From: Shengjing Zhu <i@zhsj.me>
++Date: Sat, 10 Mar 2018 22:36:31 +0800
++Subject: fix various spelling errors
++
++Forwarded: https://github.com/ceph/ceph/pull/20831
++---
++ doc/man/8/ceph-bluestore-tool.rst | 2 +-
++ doc/man/8/ceph-detect-init.rst | 2 +-
++ qa/tasks/ceph.py | 4 ++--
++ qa/tasks/mds_thrash.py | 2 +-
++ src/cls/rgw/cls_rgw.cc | 6 +++---
++ src/common/legacy_config_opts.h | 2 +-
++ src/common/options.cc | 6 +++---
++ src/java/native/libcephfs_jni.cc | 2 +-
++ src/journal/JournalPlayer.cc | 2 +-
++ src/librbd/image/OpenRequest.cc | 6 +++---
++ src/librbd/internal.cc | 2 +-
++ src/librbd/mirror/DisableRequest.cc | 2 +-
++ src/librbd/mirror/EnableRequest.cc | 2 +-
++ src/mds/CDir.cc | 2 +-
++ src/msg/async/PosixStack.cc | 2 +-
++ src/os/bluestore/BlueStore.cc | 2 +-
++ src/os/bluestore/NVMEDevice.cc | 2 +-
++ src/os/bluestore/bluestore_tool.cc | 6 +++---
++ src/pybind/rados/rados.pyx | 8 ++++----
++ src/pybind/rbd/rbd.pyx | 2 +-
++ src/test/objectstore/store_test.cc | 2 +-
++ src/test/osd/TestOSDMap.cc | 2 +-
++ src/tools/ceph_monstore_tool.cc | 2 +-
++ src/tools/monmaptool.cc | 2 +-
++ src/tools/rbd/action/Journal.cc | 2 +-
++ src/tools/rbd_nbd/rbd-nbd.cc | 2 +-
++ 26 files changed, 38 insertions(+), 38 deletions(-)
++
++--- a/doc/man/8/ceph-bluestore-tool.rst
+++++ b/doc/man/8/ceph-bluestore-tool.rst
++@@ -97,7 +97,7 @@
++
++ The main device will have a lot of metadata, including information
++ that used to be stored in small files in the OSD data directory. The
++-auxilliary devices (db and wal) will only have the minimum required
+++auxiliary devices (db and wal) will only have the minimum required
++ fields (OSD UUID, size, device type, birth time).
++
++ OSD directory priming
++--- a/doc/man/8/ceph-detect-init.rst
+++++ b/doc/man/8/ceph-detect-init.rst
++@@ -34,7 +34,7 @@
++
++ .. option:: --default INIT
++
++- If the init system of the host operating system is unkown, return
+++ If the init system of the host operating system is unknown, return
++ the value of *INIT* instead of failing with an error.
++
++ .. option:: --verbose
++--- a/qa/tasks/ceph.py
+++++ b/qa/tasks/ceph.py
++@@ -257,7 +257,7 @@
++ def valgrind_post(ctx, config):
++ """
++ After the tests run, look throught all the valgrind logs. Exceptions are raised
++- if textual errors occured in the logs, or if valgrind exceptions were detected in
+++ if textual errors occurred in the logs, or if valgrind exceptions were detected in
++ the logs.
++
++ :param ctx: Context
++@@ -398,7 +398,7 @@
++ Mkfs mon nodes.
++
++ On exit:
++- If errors occured, extract a failure message and store in ctx.summary.
+++ If errors occurred, extract a failure message and store in ctx.summary.
++ Unmount all test files and temporary journaling files.
++ Save the monitor information and archive all ceph logs.
++ Cleanup the keyring setup, and remove all monitor map and data files left over.
++--- a/qa/tasks/mds_thrash.py
+++++ b/qa/tasks/mds_thrash.py
++@@ -230,7 +230,7 @@
++ self.do_thrash()
++ except Exception as e:
++ # Log exceptions here so we get the full backtrace (gevent loses them).
++- # Also allow succesful completion as gevent exception handling is a broken mess:
+++ # Also allow successful completion as gevent exception handling is a broken mess:
++ #
++ # 2017-02-03T14:34:01.259 CRITICAL:root: File "gevent.libev.corecext.pyx", line 367, in gevent.libev.corecext.loop.handle_error (src/gevent/libev/gevent.corecext.c:5051)
++ # File "/home/teuthworker/src/git.ceph.com_git_teuthology_master/virtualenv/local/lib/python2.7/site-packages/gevent/hub.py", line 558, in handle_error
++--- a/src/cls/rgw/cls_rgw.cc
+++++ b/src/cls/rgw/cls_rgw.cc
++@@ -2513,7 +2513,7 @@
++ bool more;
++ int ret = list_plain_entries(hctx, op.name, op.marker, max, &op_ret.entries, &more);
++ if (ret < 0) {
++- CLS_LOG(0, "ERROR: %s(): list_plain_entries retured ret=%d", __func__, ret);
+++ CLS_LOG(0, "ERROR: %s(): list_plain_entries returned ret=%d", __func__, ret);
++ return ret;
++ }
++ int count = ret;
++@@ -2523,7 +2523,7 @@
++ if (!more) {
++ ret = list_instance_entries(hctx, op.name, op.marker, max - count, &op_ret.entries, &more);
++ if (ret < 0) {
++- CLS_LOG(0, "ERROR: %s(): list_instance_entries retured ret=%d", __func__, ret);
+++ CLS_LOG(0, "ERROR: %s(): list_instance_entries returned ret=%d", __func__, ret);
++ return ret;
++ }
++
++@@ -2533,7 +2533,7 @@
++ if (!more) {
++ ret = list_olh_entries(hctx, op.name, op.marker, max - count, &op_ret.entries, &more);
++ if (ret < 0) {
++- CLS_LOG(0, "ERROR: %s(): list_olh_entries retured ret=%d", __func__, ret);
+++ CLS_LOG(0, "ERROR: %s(): list_olh_entries returned ret=%d", __func__, ret);
++ return ret;
++ }
++
++--- a/src/common/legacy_config_opts.h
+++++ b/src/common/legacy_config_opts.h
++@@ -1552,7 +1552,7 @@
++
++ /* The following are tunables for torrent data */
++ OPTION(rgw_torrent_flag, OPT_BOOL) // produce torrent function flag
++-OPTION(rgw_torrent_tracker, OPT_STR) // torrent field annouce and annouce list
+++OPTION(rgw_torrent_tracker, OPT_STR) // torrent field announce and announce list
++ OPTION(rgw_torrent_createby, OPT_STR) // torrent field created by
++ OPTION(rgw_torrent_comment, OPT_STR) // torrent field comment
++ OPTION(rgw_torrent_encoding, OPT_STR) // torrent field encoding
++--- a/src/common/options.cc
+++++ b/src/common/options.cc
++@@ -5115,7 +5115,7 @@
++ .set_default(0)
++ .set_description("HTTP return code override for object creation")
++ .set_long_description(
++- "If not zero, this is the HTTP return code that will be returned on a succesful S3 "
+++ "If not zero, this is the HTTP return code that will be returned on a successful S3 "
++ "object creation."),
++
++ Option("rgw_resolve_cname", Option::TYPE_BOOL, Option::LEVEL_ADVANCED)
++@@ -5423,7 +5423,7 @@
++ .set_description("Length of time to aggregate metadata changes")
++ .set_long_description(
++ "Length of time (in milliseconds) in which the master zone aggregates all the "
++- "metadata changes that occured, before sending notifications to all the other "
+++ "metadata changes that occurred, before sending notifications to all the other "
++ "zones."),
++
++ Option("rgw_run_sync_thread", Option::TYPE_BOOL, Option::LEVEL_ADVANCED)
++@@ -5557,7 +5557,7 @@
++
++ Option("rgw_torrent_tracker", Option::TYPE_STR, Option::LEVEL_ADVANCED)
++ .set_default("")
++- .set_description("Torrent field annouce and annouce list"),
+++ .set_description("Torrent field announce and announce list"),
++
++ Option("rgw_torrent_createby", Option::TYPE_STR, Option::LEVEL_ADVANCED)
++ .set_default("")
++--- a/src/java/native/libcephfs_jni.cc
+++++ b/src/java/native/libcephfs_jni.cc
++@@ -265,7 +265,7 @@
++ /*
++ * Cast a jlong to ceph_mount_info. Each JNI function is expected to pass in
++ * the class instance variable instance_ptr. Passing a parameter is faster
++- * than reaching back into Java via an upcall to retreive this pointer.
+++ * than reaching back into Java via an upcall to retrieve this pointer.
++ */
++ static inline struct ceph_mount_info *get_ceph_mount(jlong j_mntp)
++ {
++--- a/src/journal/JournalPlayer.cc
+++++ b/src/journal/JournalPlayer.cc
++@@ -502,7 +502,7 @@
++
++ // avoid watch delay when pruning stale tags from journal objects
++ if (pruned) {
++- ldout(m_cct, 15) << __func__ << ": reseting refetch state to immediate"
+++ ldout(m_cct, 15) << __func__ << ": resetting refetch state to immediate"
++ << dendl;
++ for (auto &player_pair : m_object_players) {
++ ObjectPlayerPtr object_player(player_pair.second);
++--- a/src/librbd/image/OpenRequest.cc
+++++ b/src/librbd/image/OpenRequest.cc
++@@ -181,7 +181,7 @@
++ *result = cls_client::dir_get_name_finish(&it, &m_image_ctx->name);
++ }
++ if (*result < 0 && *result != -ENOENT) {
++- lderr(cct) << "failed to retreive name: "
+++ lderr(cct) << "failed to retrieve name: "
++ << cpp_strerror(*result) << dendl;
++ send_close_image(*result);
++ } else if (*result == -ENOENT) {
++@@ -231,7 +231,7 @@
++ ldout(cct, 5) << "failed to retrieve name for image id "
++ << m_image_ctx->id << dendl;
++ } else {
++- lderr(cct) << "failed to retreive name from trash: "
+++ lderr(cct) << "failed to retrieve name from trash: "
++ << cpp_strerror(*result) << dendl;
++ }
++ send_close_image(*result);
++@@ -273,7 +273,7 @@
++ &it, &m_image_ctx->object_prefix, &m_image_ctx->order);
++ }
++ if (*result < 0) {
++- lderr(cct) << "failed to retreive immutable metadata: "
+++ lderr(cct) << "failed to retrieve immutable metadata: "
++ << cpp_strerror(*result) << dendl;
++ send_close_image(*result);
++ } else {
++--- a/src/librbd/internal.cc
+++++ b/src/librbd/internal.cc
++@@ -1353,7 +1353,7 @@
++ r = cls_client::dir_get_id(&io_ctx, RBD_DIRECTORY, image_name, &image_id);
++ if (r < 0) {
++ if (r != -ENOENT) {
++- ldout(cct, 2) << "error reading image id from dirctory: "
+++ ldout(cct, 2) << "error reading image id from directory: "
++ << cpp_strerror(-r) << dendl;
++ }
++ return r;
++--- a/src/librbd/mirror/DisableRequest.cc
+++++ b/src/librbd/mirror/DisableRequest.cc
++@@ -72,7 +72,7 @@
++ ldout(cct, 5) << this << " " << __func__
++ << ": mirroring is not supported by OSD" << dendl;
++ } else {
++- lderr(cct) << "failed to retreive mirror image: " << cpp_strerror(*result)
+++ lderr(cct) << "failed to retrieve mirror image: " << cpp_strerror(*result)
++ << dendl;
++ }
++ return m_on_finish;
++--- a/src/librbd/mirror/EnableRequest.cc
+++++ b/src/librbd/mirror/EnableRequest.cc
++@@ -108,7 +108,7 @@
++ }
++
++ if (*result != -ENOENT) {
++- lderr(m_cct) << "failed to retreive mirror image: " << cpp_strerror(*result)
+++ lderr(m_cct) << "failed to retrieve mirror image: " << cpp_strerror(*result)
++ << dendl;
++ return m_on_finish;
++ }
++--- a/src/mds/CDir.cc
+++++ b/src/mds/CDir.cc
++@@ -1734,7 +1734,7 @@
++ dn->link_remote(dn->get_linkage(), in);
++ dout(12) << "_fetched got remote link " << ino << " which we have " << *in << dendl;
++ } else {
++- dout(12) << "_fetched got remote link " << ino << " (dont' have it)" << dendl;
+++ dout(12) << "_fetched got remote link " << ino << " (don't have it)" << dendl;
++ }
++ }
++ }
++--- a/src/msg/async/PosixStack.cc
+++++ b/src/msg/async/PosixStack.cc
++@@ -74,7 +74,7 @@
++ }
++
++ // return the sent length
++- // < 0 means error occured
+++ // < 0 means error occurred
++ static ssize_t do_sendmsg(int fd, struct msghdr &msg, unsigned len, bool more)
++ {
++ size_t sent = 0;
++--- a/src/os/bluestore/BlueStore.cc
+++++ b/src/os/bluestore/BlueStore.cc
++@@ -3649,7 +3649,7 @@
++
++ if (comp_mode == Compressor::COMP_NONE) {
++ dout(10) << __func__ << " compression mode set to 'none', "
++- << "ignore other compression setttings" << dendl;
+++ << "ignore other compression settings" << dendl;
++ return;
++ }
++
++--- a/src/os/bluestore/NVMEDevice.cc
+++++ b/src/os/bluestore/NVMEDevice.cc
++@@ -628,7 +628,7 @@
++
++ // only support one device per osd now!
++ assert(shared_driver_datas.empty());
++- // index 0 is occured by master thread
+++ // index 0 is occurred by master thread
++ shared_driver_datas.push_back(new SharedDriverData(shared_driver_datas.size()+1, sn_tag, c, ns));
++ *driver = shared_driver_datas.back();
++ }
++--- a/src/os/bluestore/bluestore_tool.cc
+++++ b/src/os/bluestore/bluestore_tool.cc
++@@ -214,7 +214,7 @@
++ exit(EXIT_FAILURE);
++ }
++ if (devs.empty()) {
++- cout << "infering bluefs devices from bluestore path" << std::endl;
+++ cout << "inferring bluefs devices from bluestore path" << std::endl;
++ for (auto fn : {"block", "block.wal", "block.db"}) {
++ string p = path + "/" + fn;
++ struct stat st;
++@@ -233,7 +233,7 @@
++ cerr << "must specify out-dir to export bluefs" << std::endl;
++ exit(EXIT_FAILURE);
++ }
++- cout << "infering bluefs devices from bluestore path" << std::endl;
+++ cout << "inferring bluefs devices from bluestore path" << std::endl;
++ for (auto fn : {"block", "block.wal", "block.db"}) {
++ string p = path + "/" + fn;
++ struct stat st;
++@@ -247,7 +247,7 @@
++ cerr << "must specify bluestore path" << std::endl;
++ exit(EXIT_FAILURE);
++ }
++- cout << "infering bluefs devices from bluestore path" << std::endl;
+++ cout << "inferring bluefs devices from bluestore path" << std::endl;
++ for (auto fn : {"block", "block.wal", "block.db"}) {
++ string p = path + "/" + fn;
++ struct stat st;
++--- a/src/pybind/rados/rados.pyx
+++++ b/src/pybind/rados/rados.pyx
++@@ -3298,7 +3298,7 @@
++ @requires(('write_op', WriteOp), ('oid', str_type), ('mtime', opt(int)), ('flags', opt(int)))
++ def operate_write_op(self, write_op, oid, mtime=0, flags=LIBRADOS_OPERATION_NOFLAG):
++ """
++- excute the real write operation
+++ execute the real write operation
++ :para write_op: write operation object
++ :type write_op: WriteOp
++ :para oid: object name
++@@ -3324,7 +3324,7 @@
++ @requires(('write_op', WriteOp), ('oid', str_type), ('oncomplete', opt(Callable)), ('onsafe', opt(Callable)), ('mtime', opt(int)), ('flags', opt(int)))
++ def operate_aio_write_op(self, write_op, oid, oncomplete=None, onsafe=None, mtime=0, flags=LIBRADOS_OPERATION_NOFLAG):
++ """
++- excute the real write operation asynchronously
+++ execute the real write operation asynchronously
++ :para write_op: write operation object
++ :type write_op: WriteOp
++ :para oid: object name
++@@ -3366,7 +3366,7 @@
++ @requires(('read_op', ReadOp), ('oid', str_type), ('flag', opt(int)))
++ def operate_read_op(self, read_op, oid, flag=LIBRADOS_OPERATION_NOFLAG):
++ """
++- excute the real read operation
+++ execute the real read operation
++ :para read_op: read operation object
++ :type read_op: ReadOp
++ :para oid: object name
++@@ -3388,7 +3388,7 @@
++ @requires(('read_op', ReadOp), ('oid', str_type), ('oncomplete', opt(Callable)), ('onsafe', opt(Callable)), ('flag', opt(int)))
++ def operate_aio_read_op(self, read_op, oid, oncomplete=None, onsafe=None, flag=LIBRADOS_OPERATION_NOFLAG):
++ """
++- excute the real read operation
+++ execute the real read operation
++ :para read_op: read operation object
++ :type read_op: ReadOp
++ :para oid: object name
++--- a/src/pybind/rbd/rbd.pyx
+++++ b/src/pybind/rbd/rbd.pyx
++@@ -2658,7 +2658,7 @@
++
++ def aio_flush(self, oncomplete):
++ """
++- Asyncronously wait until all writes are fully flushed if caching is
+++ Asynchronously wait until all writes are fully flushed if caching is
++ enabled.
++ """
++
++--- a/src/test/objectstore/store_test.cc
+++++ b/src/test/objectstore/store_test.cc
++@@ -1724,7 +1724,7 @@
++ t.write(cid, hoid, 0, 0x30000, bl);
++ t.zero(cid, hoid, 0, 0x10000);
++ t.zero(cid, hoid, 0x20000, 0x10000);
++- cerr << "Rewrite an object and create two holes at the begining and the end" << std::endl;
+++ cerr << "Rewrite an object and create two holes at the beginning and the end" << std::endl;
++ r = apply_transaction(store, &osr, std::move(t));
++ ASSERT_EQ(r, 0);
++
++--- a/src/test/osd/TestOSDMap.cc
+++++ b/src/test/osd/TestOSDMap.cc
++@@ -207,7 +207,7 @@
++ ASSERT_TRUE(features & CEPH_FEATURE_CRUSH_TUNABLES2);
++ ASSERT_TRUE(features & CEPH_FEATURE_CRUSH_TUNABLES3);
++ ASSERT_TRUE(features & CEPH_FEATURE_CRUSH_V2);
++- ASSERT_FALSE(features & CEPH_FEATURE_OSD_ERASURE_CODES); // dont' need this
+++ ASSERT_FALSE(features & CEPH_FEATURE_OSD_ERASURE_CODES); // don't need this
++ ASSERT_TRUE(features & CEPH_FEATURE_OSDHASHPSPOOL);
++ ASSERT_TRUE(features & CEPH_FEATURE_OSD_PRIMARY_AFFINITY);
++
++--- a/src/tools/ceph_monstore_tool.cc
+++++ b/src/tools/ceph_monstore_tool.cc
++@@ -878,7 +878,7 @@
++ ("version,v", po::value<unsigned>(&v),
++ "map version to obtain")
++ ("readable,r", po::value<bool>(&readable)->default_value(false),
++- "print the map infomation in human readable format")
+++ "print the map information in human readable format")
++ ;
++ // this is going to be a positional argument; we don't want to show
++ // it as an option during --help, but we do want to have it captured
++--- a/src/tools/monmaptool.cc
+++++ b/src/tools/monmaptool.cc
++@@ -158,7 +158,7 @@
++ target.unset_feature(f.feature);
++ }
++ } else {
++- cerr << "unknow feature operation type '" << f.op << "'" << std::endl;
+++ cerr << "unknown feature operation type '" << f.op << "'" << std::endl;
++ }
++ }
++ return modified;
++--- a/src/tools/rbd/action/Journal.cc
+++++ b/src/tools/rbd/action/Journal.cc
++@@ -674,7 +674,7 @@
++ return false;
++ }
++ if (r != entry_size) {
++- std::cerr << "rbd: error reading from stdin: trucated"
+++ std::cerr << "rbd: error reading from stdin: truncated"
++ << std::endl;
++ r = -EINVAL;
++ return false;
++--- a/src/tools/rbd_nbd/rbd-nbd.cc
+++++ b/src/tools/rbd_nbd/rbd-nbd.cc
++@@ -437,7 +437,7 @@
++ os << " TRIM ";
++ break;
++ default:
++- os << " UNKNOW(" << ctx.command << ") ";
+++ os << " UNKNOWN(" << ctx.command << ") ";
++ break;
++ }
++
--- /dev/null
--- /dev/null
++From: Shengjing Zhu <i@zhsj.me>
++Date: Mon, 12 Mar 2018 11:01:10 +0800
++Subject: fix unittest_mclock_priority_queue not build when make
++
++Origin: backport, https://github.com/ceph/ceph/commit/494b2f8f5ae11a689bb2032e4527968ff09bcdeb
++---
++ src/test/common/CMakeLists.txt | 2 +-
++ 1 file changed, 1 insertion(+), 1 deletion(-)
++
++diff --git a/src/test/common/CMakeLists.txt b/src/test/common/CMakeLists.txt
++index d31c363..041f127 100644
++--- a/src/test/common/CMakeLists.txt
+++++ b/src/test/common/CMakeLists.txt
++@@ -43,7 +43,7 @@ add_ceph_unittest(unittest_prioritized_queue ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/u
++ target_link_libraries(unittest_prioritized_queue global ${BLKID_LIBRARIES})
++
++ # unittest_mclock_priority_queue
++-add_executable(unittest_mclock_priority_queue EXCLUDE_FROM_ALL
+++add_executable(unittest_mclock_priority_queue
++ test_mclock_priority_queue.cc
++ )
++ add_ceph_unittest(unittest_mclock_priority_queue ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest_mclock_priority_queue)
--- /dev/null
--- /dev/null
++From: Shengjing Zhu <i@zhsj.me>
++Date: Sun, 11 Mar 2018 22:59:20 +0800
++Subject: remove ceph.com ref to favicon
++
++Origin: backport, https://github.com/ceph/ceph/commit/409b8923a24ff557c53260842aaff6400054bc9b
++---
++ src/pybind/mgr/dashboard/base.html | 1 -
++ 1 file changed, 1 deletion(-)
++
++diff --git a/src/pybind/mgr/dashboard/base.html b/src/pybind/mgr/dashboard/base.html
++index 9936eae..0c0a971 100644
++--- a/src/pybind/mgr/dashboard/base.html
+++++ b/src/pybind/mgr/dashboard/base.html
++@@ -156,7 +156,6 @@
++ });
++ </script>
++
++- <link rel="shortcut icon" href="https://ceph.com/wp-content/themes/cephTheme/Resources/Favicons/favicon-96x96.png">
++ <link rel="shortcut icon" href="{{ url_prefix }}/static/favicon.ico">
++
++ <style>
--- /dev/null
--- /dev/null
++From ce2e26d39ac6131cb3782446082ec2737a185517 Mon Sep 17 00:00:00 2001
++From: Boris Ranto <branto@redhat.com>
++Date: Fri, 8 Dec 2017 00:21:38 +0100
++Subject: [PATCH] librbd: Do not instantiate TrimRequest template class
++
++We include TrimRequest.cc in librbd tests at two places:
++ - operation/test_mock_TrimRequest.cc
++ - operation/test_mock_ResizeRequest.cc
++
++That causes linking errors when doing the builds because some of the
++instantiated classes are defined twice.
++
++We can fix this by not instantiating the template class in the
++TrimReqeust.cc file when including it in the tests.
++
++Signed-off-by: Boris Ranto <branto@redhat.com>
++---
++ src/test/librbd/operation/test_mock_ResizeRequest.cc | 1 -
++ 1 file changed, 1 deletion(-)
++
++diff --git a/src/test/librbd/operation/test_mock_ResizeRequest.cc b/src/test/librbd/operation/test_mock_ResizeRequest.cc
++index e67f8a2a8321..7b70a50e8f48 100644
++--- a/src/test/librbd/operation/test_mock_ResizeRequest.cc
+++++ b/src/test/librbd/operation/test_mock_ResizeRequest.cc
++@@ -44,7 +44,6 @@ TrimRequest<MockImageCtx> *TrimRequest<MockImageCtx>::s_instance = nullptr;
++
++ // template definitions
++ #include "librbd/operation/ResizeRequest.cc"
++-#include "librbd/operation/TrimRequest.cc"
++
++ namespace librbd {
++ namespace operation {
--- /dev/null
--- /dev/null
++--- a/src/test/librbd/test_mock_Journal.cc
+++++ b/src/test/librbd/test_mock_Journal.cc
++@@ -214,7 +214,6 @@
++ using ::testing::SaveArg;
++ using ::testing::SetArgPointee;
++ using ::testing::WithArg;
++-using namespace std::placeholders;
++
++ ACTION_P2(StartReplay, wq, ctx) {
++ wq->queue(ctx, 0);
++@@ -482,7 +481,7 @@
++ expect_get_max_append_size(mock_journaler, 1 << 16);
++ expect_start_replay(
++ mock_image_ctx, mock_journaler,
++- std::bind(&invoke_replay_complete, _1, 0));
+++ std::bind(&invoke_replay_complete, std::placeholders::_1, 0));
++
++ MockJournalReplay mock_journal_replay;
++ expect_stop_replay(mock_journaler);
++@@ -531,7 +530,7 @@
++ expect_get_max_append_size(mock_journaler, 1 << 16);
++ expect_start_replay(
++ mock_image_ctx, mock_journaler,
++- std::bind(&invoke_replay_ready, _1));
+++ std::bind(&invoke_replay_ready, std::placeholders::_1));
++
++ ::journal::MockReplayEntry mock_replay_entry;
++ MockJournalReplay mock_journal_replay;
++@@ -540,11 +539,11 @@
++ expect_try_pop_front(mock_image_ctx, mock_journaler, true, mock_replay_entry);
++ expect_replay_process(mock_journal_replay);
++ expect_try_pop_front(mock_image_ctx, mock_journaler, false, mock_replay_entry,
++- std::bind(&invoke_replay_ready, _1));
+++ std::bind(&invoke_replay_ready, std::placeholders::_1));
++ expect_try_pop_front(mock_image_ctx, mock_journaler, true, mock_replay_entry);
++ expect_replay_process(mock_journal_replay);
++ expect_try_pop_front(mock_image_ctx, mock_journaler, false, mock_replay_entry,
++- std::bind(&invoke_replay_complete, _1, 0));
+++ std::bind(&invoke_replay_complete, std::placeholders::_1, 0));
++
++ expect_stop_replay(mock_journaler);
++ expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0);
++@@ -601,7 +600,7 @@
++ expect_get_max_append_size(mock_journaler, 1 << 16);
++ expect_start_replay(
++ mock_image_ctx, mock_journaler,
++- std::bind(&invoke_replay_complete, _1, -EINVAL));
+++ std::bind(&invoke_replay_complete, std::placeholders::_1, -EINVAL));
++
++ MockJournalReplay mock_journal_replay;
++ expect_stop_replay(mock_journaler);
++@@ -616,7 +615,7 @@
++ expect_get_max_append_size(mock_journaler, 1 << 16);
++ expect_start_replay(
++ mock_image_ctx, mock_journaler,
++- std::bind(&invoke_replay_complete, _1, 0));
+++ std::bind(&invoke_replay_complete, std::placeholders::_1, 0));
++
++ expect_stop_replay(mock_journaler);
++ expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0);
++@@ -649,14 +648,14 @@
++ expect_get_max_append_size(mock_journaler, 1 << 16);
++ expect_start_replay(
++ mock_image_ctx, mock_journaler,
++- std::bind(&invoke_replay_ready, _1));
+++ std::bind(&invoke_replay_ready, std::placeholders::_1));
++
++ ::journal::MockReplayEntry mock_replay_entry;
++ MockJournalReplay mock_journal_replay;
++ expect_try_pop_front(mock_image_ctx, mock_journaler, true, mock_replay_entry);
++ expect_replay_process(mock_journal_replay);
++ expect_try_pop_front(mock_image_ctx, mock_journaler, false, mock_replay_entry,
++- std::bind(&invoke_replay_complete, _1, 0));
+++ std::bind(&invoke_replay_complete, std::placeholders::_1, 0));
++ expect_stop_replay(mock_journaler);
++ expect_shut_down_replay(mock_image_ctx, mock_journal_replay, -EINVAL);
++ expect_flush_commit_position(mock_journaler);
++@@ -669,7 +668,7 @@
++ expect_get_max_append_size(mock_journaler, 1 << 16);
++ expect_start_replay(
++ mock_image_ctx, mock_journaler,
++- std::bind(&invoke_replay_complete, _1, 0));
+++ std::bind(&invoke_replay_complete, std::placeholders::_1, 0));
++
++ expect_stop_replay(mock_journaler);
++ expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0);
++@@ -702,7 +701,7 @@
++ expect_get_max_append_size(mock_journaler, 1 << 16);
++ expect_start_replay(
++ mock_image_ctx, mock_journaler,
++- std::bind(&invoke_replay_ready, _1));
+++ std::bind(&invoke_replay_ready, std::placeholders::_1));
++
++ ::journal::MockReplayEntry mock_replay_entry;
++ MockJournalReplay mock_journal_replay;
++@@ -720,7 +719,7 @@
++ expect_get_max_append_size(mock_journaler, 1 << 16);
++ expect_start_replay(
++ mock_image_ctx, mock_journaler,
++- std::bind(&invoke_replay_complete, _1, 0));
+++ std::bind(&invoke_replay_complete, std::placeholders::_1, 0));
++ expect_stop_replay(mock_journaler);
++ expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0);
++ expect_flush_commit_position(mock_journaler);
++@@ -752,7 +751,7 @@
++ expect_get_max_append_size(mock_journaler, 1 << 16);
++ expect_start_replay(
++ mock_image_ctx, mock_journaler,
++- std::bind(&invoke_replay_complete, _1, 0));
+++ std::bind(&invoke_replay_complete, std::placeholders::_1, 0));
++
++ MockJournalReplay mock_journal_replay;
++ expect_stop_replay(mock_journaler);
++@@ -786,7 +785,7 @@
++
++ expect_start_replay(
++ mock_image_ctx, mock_journaler,
++- std::bind(&invoke_replay_ready, _1));
+++ std::bind(&invoke_replay_ready, std::placeholders::_1));
++
++ ::journal::MockReplayEntry mock_replay_entry;
++ MockJournalReplay mock_journal_replay;
++@@ -813,7 +812,7 @@
++ expect_get_max_append_size(mock_journaler, 1 << 16);
++ expect_start_replay(
++ mock_image_ctx, mock_journaler, {
++- std::bind(&invoke_replay_complete, _1, 0)
+++ std::bind(&invoke_replay_complete, std::placeholders::_1, 0)
++ });
++
++ expect_stop_replay(mock_journaler);
++@@ -868,14 +867,14 @@
++ expect_get_max_append_size(mock_journaler, 1 << 16);
++ expect_start_replay(
++ mock_image_ctx, mock_journaler,
++- std::bind(&invoke_replay_ready, _1));
+++ std::bind(&invoke_replay_ready, std::placeholders::_1));
++
++ ::journal::MockReplayEntry mock_replay_entry;
++ MockJournalReplay mock_journal_replay;
++ expect_try_pop_front(mock_image_ctx, mock_journaler, true, mock_replay_entry);
++ expect_replay_process(mock_journal_replay);
++ expect_try_pop_front(mock_image_ctx, mock_journaler, false, mock_replay_entry,
++- std::bind(&invoke_replay_complete, _1, 0));
+++ std::bind(&invoke_replay_complete, std::placeholders::_1, 0));
++ expect_stop_replay(mock_journaler);
++
++ Context *on_flush = nullptr;
++@@ -892,7 +891,7 @@
++ expect_get_max_append_size(mock_journaler, 1 << 16);
++ expect_start_replay(
++ mock_image_ctx, mock_journaler,
++- std::bind(&invoke_replay_complete, _1, 0));
+++ std::bind(&invoke_replay_complete, std::placeholders::_1, 0));
++
++ expect_stop_replay(mock_journaler);
++ expect_shut_down_replay(mock_image_ctx, mock_journal_replay, 0);
++--- a/src/rbd_replay/Replayer.cc
+++++ b/src/rbd_replay/Replayer.cc
++@@ -350,7 +350,7 @@
++ dout(DEPGRAPH_LEVEL) << "Finished waiting for " << dep.id << " after " << micros << " microseconds" << dendl;
++ // Apparently the nanoseconds constructor is optional:
++ // http://www.boost.org/doc/libs/1_46_0/doc/html/date_time/details.html#compile_options
++- boost::system_time sub_release_time(action_completed_time + boost::posix_time::microseconds(dep.time_delta * m_latency_multiplier / 1000));
+++ boost::system_time sub_release_time(action_completed_time + boost::posix_time::microseconds(long(dep.time_delta * m_latency_multiplier / 1000)));
++ if (sub_release_time > release_time) {
++ release_time = sub_release_time;
++ }
--- /dev/null
--- /dev/null
++--- a/src/init-ceph.in
+++++ b/src/init-ceph.in
++@@ -293,7 +293,7 @@
++
++ if [ "$command" = "start" -o "$command" = "onestart" ]; then
++ if [ -n "$pid_file" ]; then
++- do_cmd "mkdir -p "`dirname $pid_file`
+++ do_cmd "install -d -m0770 -o ceph -g ceph "`dirname $pid_file`
++ cmd="$cmd --pid-file $pid_file"
++ fi
++
--- /dev/null
--- /dev/null
++--- a/src/rocksdb/build_tools/build_detect_platform
+++++ b/src/rocksdb/build_tools/build_detect_platform
++@@ -451,6 +451,10 @@
++ fi
++ fi
++
+++if [ "$TARGET_ARCHITECTURE" = "mips" ] ; then
+++ PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -latomic"
+++fi
+++
++ PLATFORM_CCFLAGS="$PLATFORM_CCFLAGS $COMMON_FLAGS"
++ PLATFORM_CXXFLAGS="$PLATFORM_CXXFLAGS $COMMON_FLAGS"
++
++--- /dev/null
+++++ b/cmake/modules/CheckAtomic.cmake
++@@ -0,0 +1,67 @@
+++# Checks if atomic operations are supported natively or if linking against
+++# libatomic is needed.
+++
+++# Check inspired by LLVMs cmake/modules/CheckAtomic.cmake
+++
+++INCLUDE(CheckCXXSourceCompiles)
+++INCLUDE(CheckLibraryExists)
+++
+++function(check_working_cxx_atomics varname)
+++ set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
+++ set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -std=c++11")
+++ CHECK_CXX_SOURCE_COMPILES("
+++#include <atomic>
+++std::atomic<int> x;
+++int main() {
+++ return x;
+++}
+++" ${varname})
+++ set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS})
+++endfunction(check_working_cxx_atomics)
+++
+++function(check_working_cxx_atomics64 varname)
+++ set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
+++ set(CMAKE_REQUIRED_FLAGS "-std=c++11 ${CMAKE_REQUIRED_FLAGS}")
+++ CHECK_CXX_SOURCE_COMPILES("
+++#include <atomic>
+++#include <cstdint>
+++std::atomic<uint64_t> x (0);
+++int main() {
+++ uint64_t i = x.load(std::memory_order_relaxed);
+++ return 0;
+++}
+++" ${varname})
+++ set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS})
+++endfunction(check_working_cxx_atomics64)
+++
+++# Check if atomics work without libatomic
+++check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITHOUT_LIB)
+++
+++if(NOT HAVE_CXX_ATOMICS_WITHOUT_LIB)
+++ check_library_exists(atomic __atomic_fetch_add_4 "" HAVE_LIBATOMIC)
+++ if( HAVE_LIBATOMIC )
+++ list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic")
+++ check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITH_LIB)
+++ if (NOT HAVE_CXX_ATOMICS_WITH_LIB)
+++ message(FATAL_ERROR "Host compiler must support std::atomic!")
+++ endif()
+++ else()
+++ message(FATAL_ERROR "Host compiler appears to require libatomic, but cannot find it.")
+++ endif()
+++endif()
+++
+++# Check if 64bit atomics work without libatomic
+++check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITHOUT_LIB)
+++
+++if(NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB)
+++ check_library_exists(atomic __atomic_load_8 "" HAVE_CXX_LIBATOMICS64)
+++ if(HAVE_CXX_LIBATOMICS64)
+++ list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic")
+++ check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITH_LIB)
+++ if (NOT HAVE_CXX_ATOMICS64_WITH_LIB)
+++ message(FATAL_ERROR "Host compiler must support std::atomic!")
+++ endif()
+++ else()
+++ message(FATAL_ERROR "Host compiler appears to require libatomic, but cannot find it.")
+++ endif()
+++endif()
++--- a/src/CMakeLists.txt
+++++ b/src/CMakeLists.txt
++@@ -158,6 +158,12 @@
++ set(C_STANDARD_REQUIRED ON)
++ endif()
++
+++# check if linking against libatomic is necessary
+++include(CheckAtomic)
+++if(HAVE_CXX_ATOMIC_WITH_LIB OR HAVE_CXX_ATOMICS64_WITH_LIB)
+++ set(ATOMIC_LIBRARIES atomic)
+++endif()
+++
++ ## Handle diagnostics color if compiler supports them.
++ CHECK_C_COMPILER_FLAG("-fdiagnostics-color=always"
++ COMPILER_SUPPORTS_DIAGNOSTICS_COLOR)
++@@ -655,10 +661,10 @@
++ endif(HAVE_ARMV8_CRC)
++
++ add_library(common STATIC ${ceph_common_objs})
++-target_link_libraries(common ${ceph_common_deps})
+++target_link_libraries(common ${ceph_common_deps} ${ATOMIC_LIBRARIES})
++
++ add_library(ceph-common SHARED ${ceph_common_objs})
++-target_link_libraries(ceph-common ${ceph_common_deps})
+++target_link_libraries(ceph-common ${ceph_common_deps} ${ATOMIC_LIBRARIES})
++ # appease dpkg-shlibdeps
++ set_target_properties(ceph-common PROPERTIES
++ SOVERSION 0
++@@ -853,7 +859,7 @@
++ add_library(rocksdb STATIC IMPORTED)
++ add_dependencies(rocksdb rocksdb_ext)
++ set_property(TARGET rocksdb PROPERTY IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/rocksdb/librocksdb.a")
++- set(ROCKSDB_LIBRARIES rocksdb)
+++ set(ROCKSDB_LIBRARIES rocksdb ${ATOMIC_LIBRARIES})
++
++ endif(NOT WITH_SYSTEM_ROCKSDB)
++
++--- a/src/client/CMakeLists.txt
+++++ b/src/client/CMakeLists.txt
++@@ -10,4 +10,4 @@
++ posix_acl.cc
++ Delegation.cc)
++ add_library(client STATIC ${libclient_srcs})
++-target_link_libraries(client osdc)
+++target_link_libraries(client osdc ${ATOMIC_LIBRARIES})
++--- a/src/kv/CMakeLists.txt
+++++ b/src/kv/CMakeLists.txt
++@@ -11,7 +11,7 @@
++ add_library(kv STATIC $<TARGET_OBJECTS:kv_objs>)
++ target_include_directories(kv_objs BEFORE PUBLIC ${ROCKSDB_INCLUDE_DIR})
++ target_include_directories(kv BEFORE PUBLIC ${ROCKSDB_INCLUDE_DIR})
++-target_link_libraries(kv ${LEVELDB_LIBRARIES} ${ROCKSDB_LIBRARIES} ${ALLOC_LIBS} ${SNAPPY_LIBRARIES} ${ZLIB_LIBRARIES})
+++target_link_libraries(kv ${LEVELDB_LIBRARIES} ${ROCKSDB_LIBRARIES} ${ATOMIC_LIBRARIES} ${ALLOC_LIBS} ${SNAPPY_LIBRARIES} ${ZLIB_LIBRARIES})
++
++ # rocksdb detects bzlib and lz4 in its Makefile, which forces us to do the same.
++ find_package(BZip2 QUIET)
++--- a/src/dmclock/sim/src/CMakeLists.txt
+++++ b/src/dmclock/sim/src/CMakeLists.txt
++@@ -36,7 +36,7 @@
++
++ add_dependencies(dmc_sim dmclock)
++
++-target_link_libraries(ssched_sim LINK_PRIVATE pthread)
++-target_link_libraries(dmc_sim LINK_PRIVATE pthread $<TARGET_FILE:dmclock>)
+++target_link_libraries(ssched_sim LINK_PRIVATE pthread ${ATOMIC_LIBRARIES})
+++target_link_libraries(dmc_sim LINK_PRIVATE pthread $<TARGET_FILE:dmclock> ${ATOMIC_LIBRARIES})
++
++ add_custom_target(dmclock-sims DEPENDS ssched_sim dmc_sim)
++--- a/src/dmclock/test/CMakeLists.txt
+++++ b/src/dmclock/test/CMakeLists.txt
++@@ -28,10 +28,15 @@
++ LINK_PRIVATE $<TARGET_FILE:dmclock>
++ pthread
++ $<TARGET_FILE:gtest>
++- $<TARGET_FILE:gtest_main>)
+++ $<TARGET_FILE:gtest_main>
+++ ${ATOMICS_LIBRARIES})
++ else()
++ target_link_libraries(dmclock-tests
++- LINK_PRIVATE $<TARGET_FILE:dmclock> pthread ${GTEST_LIBRARIES} ${GTEST_MAIN_LIBRARIES})
+++ LINK_PRIVATE $<TARGET_FILE:dmclock>
+++ pthread
+++ ${GTEST_LIBRARIES}
+++ ${GTEST_MAIN_LIBRARIES}
+++ ${ATMOIC_LIBRARIES})
++ endif()
++
++ add_dependencies(dmclock-tests dmclock)
++--- a/src/journal/CMakeLists.txt
+++++ b/src/journal/CMakeLists.txt
++@@ -11,4 +11,4 @@
++ ObjectRecorder.cc
++ Utils.cc)
++ add_library(journal STATIC ${journal_srcs})
++-target_link_libraries(journal cls_journal_client)
+++target_link_libraries(journal cls_journal_client ${ATOMIC_LIBRARIES})
++--- a/src/librados/CMakeLists.txt
+++++ b/src/librados/CMakeLists.txt
++@@ -9,7 +9,7 @@
++ $<TARGET_OBJECTS:librados_objs>
++ $<TARGET_OBJECTS:common_buffer_obj>)
++ target_link_libraries(rados_a osdc ceph-common cls_lock_client
++- ${BLKID_LIBRARIES} ${CRYPTO_LIBS} ${EXTRALIBS})
+++ ${BLKID_LIBRARIES} ${CRYPTO_LIBS} ${EXTRALIBS} ${ATOMIC_LIBRARIES})
++ if(WITH_LTTNG)
++ add_dependencies(librados_api_obj librados-tp)
++ endif()
++@@ -20,7 +20,7 @@
++ $<TARGET_OBJECTS:common_buffer_obj>)
++ # LINK_PRIVATE instead of PRIVATE is used to backward compatibility with cmake 2.8.11
++ target_link_libraries(librados LINK_PRIVATE osdc ceph-common cls_lock_client
++- ${BLKID_LIBRARIES} ${CRYPTO_LIBS} ${EXTRALIBS})
+++ ${BLKID_LIBRARIES} ${CRYPTO_LIBS} ${EXTRALIBS} ${ATOMIC_LIBRARIES})
++ set_target_properties(librados PROPERTIES
++ OUTPUT_NAME rados
++ VERSION 2.0.0
++--- a/src/librbd/CMakeLists.txt
+++++ b/src/librbd/CMakeLists.txt
++@@ -125,7 +125,8 @@
++ ceph-common
++ pthread
++ ${CMAKE_DL_LIBS}
++- ${EXTRALIBS})
+++ ${EXTRALIBS}
+++ ${ATOMIC_LIBRARIES})
++ if(HAVE_UDEV)
++ target_link_libraries(librbd LINK_PRIVATE
++ udev)
++--- a/src/os/CMakeLists.txt
+++++ b/src/os/CMakeLists.txt
++@@ -100,7 +100,7 @@
++ add_dependencies(os objectstore-tp)
++ endif()
++
++-target_link_libraries(os kv)
+++target_link_libraries(os kv ${ATOMIC_LIBRARIES})
++
++ add_dependencies(os compressor_plugins)
++ add_dependencies(os crypto_plugins)
++@@ -110,7 +110,7 @@
++ add_executable(ceph-bluestore-tool
++ bluestore/bluestore_tool.cc)
++ target_link_libraries(ceph-bluestore-tool
++- os global)
+++ os global ${ATOMIC_LIBRARIES})
++ install(TARGETS ceph-bluestore-tool
++ DESTINATION bin)
++ endif()
++--- a/src/osd/CMakeLists.txt
+++++ b/src/osd/CMakeLists.txt
++@@ -43,7 +43,7 @@
++ $<TARGET_OBJECTS:cls_references_objs>
++ $<TARGET_OBJECTS:global_common_objs>
++ $<TARGET_OBJECTS:heap_profiler_objs>)
++-target_link_libraries(osd ${LEVELDB_LIBRARIES} dmclock ${CMAKE_DL_LIBS} ${ALLOC_LIBS})
+++target_link_libraries(osd ${LEVELDB_LIBRARIES} dmclock ${CMAKE_DL_LIBS} ${ALLOC_LIBS} ${ATOMIC_LIBRARIES})
++ if(WITH_LTTNG)
++ add_dependencies(osd osd-tp pg-tp)
++ endif()
++--- a/src/rgw/CMakeLists.txt
+++++ b/src/rgw/CMakeLists.txt
++@@ -153,7 +153,7 @@
++ cls_replica_log_client cls_user_client ceph-common common_utf8 global
++ ${CURL_LIBRARIES}
++ ${EXPAT_LIBRARIES}
++- ${OPENLDAP_LIBRARIES} ${CRYPTO_LIBS})
+++ ${OPENLDAP_LIBRARIES} ${CRYPTO_LIBS} ${ATMOIC_LIBRARIES})
++
++ if (WITH_CURL_OPENSSL)
++ target_link_libraries(rgw_a ${OPENSSL_LIBRARIES})
++@@ -186,7 +186,7 @@
++ cls_version_client cls_replica_log_client cls_user_client
++ global ${FCGI_LIBRARY} ${LIB_RESOLV}
++ ${CURL_LIBRARIES} ${EXPAT_LIBRARIES} ${BLKID_LIBRARIES}
++- ${ALLOC_LIBS})
+++ ${ALLOC_LIBS} ${ATMOIC_LIBRARIES})
++ # radosgw depends on cls libraries at runtime, but not as link dependencies
++ add_dependencies(radosgw cls_rgw cls_lock cls_refcount
++ cls_log cls_statelog cls_timeindex
++@@ -202,7 +202,7 @@
++ cls_log_client cls_statelog_client cls_timeindex_client
++ cls_version_client cls_replica_log_client cls_user_client
++ global ${FCGI_LIBRARY} ${LIB_RESOLV}
++- ${CURL_LIBRARIES} ${EXPAT_LIBRARIES} ${SSL_LIBRARIES} ${BLKID_LIBRARIES})
+++ ${CURL_LIBRARIES} ${EXPAT_LIBRARIES} ${SSL_LIBRARIES} ${BLKID_LIBRARIES} ${ATMOIC_LIBRARIES})
++ install(TARGETS radosgw-admin DESTINATION bin)
++
++ set(radosgw_es_srcs
++@@ -213,7 +213,7 @@
++ cls_log_client cls_statelog_client cls_timeindex_client
++ cls_version_client cls_replica_log_client cls_user_client
++ global ${FCGI_LIBRARY} ${LIB_RESOLV}
++- ${CURL_LIBRARIES} ${EXPAT_LIBRARIES} ${SSL_LIBRARIES} ${BLKID_LIBRARIES})
+++ ${CURL_LIBRARIES} ${EXPAT_LIBRARIES} ${SSL_LIBRARIES} ${BLKID_LIBRARIES} ${ATMOIC_LIBRARIES})
++ install(TARGETS radosgw-es DESTINATION bin)
++
++ set(radosgw_token_srcs
++@@ -231,7 +231,7 @@
++ cls_log_client cls_statelog_client cls_timeindex_client
++ cls_version_client cls_replica_log_client cls_user_client
++ global ${FCGI_LIBRARY} ${LIB_RESOLV}
++- ${CURL_LIBRARIES} ${EXPAT_LIBRARIES})
+++ ${CURL_LIBRARIES} ${EXPAT_LIBRARIES} ${ATMOIC_LIBRARIES})
++ install(TARGETS radosgw-object-expirer DESTINATION bin)
++
++ set(librgw_srcs
++@@ -253,7 +253,8 @@
++ global
++ ${LIB_RESOLV}
++ ${CURL_LIBRARIES}
++- ${EXPAT_LIBRARIES})
+++ ${EXPAT_LIBRARIES}
+++ ${ATMOIC_LIBRARIES})
++ set_target_properties(rgw PROPERTIES OUTPUT_NAME rgw VERSION 2.0.0
++ SOVERSION 2)
++ install(TARGETS rgw DESTINATION ${CMAKE_INSTALL_LIBDIR})
++--- a/src/test/CMakeLists.txt
+++++ b/src/test/CMakeLists.txt
++@@ -410,7 +410,7 @@
++ set_target_properties(ceph_perf_local PROPERTIES COMPILE_FLAGS
++ ${PERF_LOCAL_FLAGS})
++ endif()
++-target_link_libraries(ceph_perf_local os global ${UNITTEST_LIBS})
+++target_link_libraries(ceph_perf_local os global ${UNITTEST_LIBS} ${ATMOIC_LIBRARIES})
++
++ # ceph_xattr_bench
++ add_executable(ceph_xattr_bench
++@@ -483,6 +483,7 @@
++ ${EXTRALIBS}
++ ${BLKID_LIBRARIES}
++ ${CMAKE_DL_LIBS}
+++ ${ATMOIC_LIBRARIES}
++ )
++ install(TARGETS
++ ceph_test_stress_watch
++--- a/src/test/common/CMakeLists.txt
+++++ b/src/test/common/CMakeLists.txt
++@@ -43,7 +43,7 @@
++ target_link_libraries(unittest_prioritized_queue global ${BLKID_LIBRARIES})
++
++ # unittest_mclock_priority_queue
++-add_executable(unittest_mclock_priority_queue
+++add_executable(unittest_mclock_priority_queue EXCLUDE_FROM_ALL
++ test_mclock_priority_queue.cc
++ )
++ add_ceph_unittest(unittest_mclock_priority_queue ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest_mclock_priority_queue)
++@@ -101,7 +101,7 @@
++ $<TARGET_OBJECTS:unit-main>
++ )
++ add_ceph_unittest(unittest_throttle ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest_throttle)
++-target_link_libraries(unittest_throttle global)
+++target_link_libraries(unittest_throttle global ${ATMOIC_LIBRARIES})
++
++ # unittest_lru
++ add_executable(unittest_lru
++--- a/src/test/libcephfs/CMakeLists.txt
+++++ b/src/test/libcephfs/CMakeLists.txt
++@@ -17,6 +17,7 @@
++ ${UNITTEST_LIBS}
++ ${EXTRALIBS}
++ ${CMAKE_DL_LIBS}
+++ ${ATMOIC_LIBRARIES}
++ )
++ install(TARGETS ceph_test_libcephfs
++ DESTINATION ${CMAKE_INSTALL_BINDIR})
++--- a/src/test/librados_test_stub/CMakeLists.txt
+++++ b/src/test/librados_test_stub/CMakeLists.txt
++@@ -8,4 +8,4 @@
++ TestRadosClient.cc
++ TestWatchNotify.cc)
++ add_library(rados_test_stub STATIC ${librados_test_stub_srcs})
++-
+++target_link_libraries(rados_test_stub ${ATMOIC_LIBRARIES})
++--- a/src/test/msgr/CMakeLists.txt
+++++ b/src/test/msgr/CMakeLists.txt
++@@ -5,7 +5,7 @@
++ )
++ set_target_properties(ceph_test_async_driver PROPERTIES COMPILE_FLAGS
++ ${UNITTEST_CXX_FLAGS})
++-target_link_libraries(ceph_test_async_driver os global ${BLKID_LIBRARIES} ${CMAKE_DL_LIBS} ${UNITTEST_LIBS})
+++target_link_libraries(ceph_test_async_driver os global ${BLKID_LIBRARIES} ${CMAKE_DL_LIBS} ${UNITTEST_LIBS} ${ATMOIC_LIBRARIES})
++
++ # ceph_test_msgr
++ add_executable(ceph_test_msgr
++@@ -13,7 +13,7 @@
++ )
++ set_target_properties(ceph_test_msgr PROPERTIES COMPILE_FLAGS
++ ${UNITTEST_CXX_FLAGS})
++-target_link_libraries(ceph_test_msgr os global ${BLKID_LIBRARIES} ${CMAKE_DL_LIBS} ${UNITTEST_LIBS})
+++target_link_libraries(ceph_test_msgr os global ${BLKID_LIBRARIES} ${CMAKE_DL_LIBS} ${UNITTEST_LIBS} ${ATMOIC_LIBRARIES})
++
++ # ceph_test_async_networkstack
++ add_executable(ceph_test_async_networkstack
++@@ -22,7 +22,7 @@
++ )
++ set_target_properties(ceph_test_async_networkstack PROPERTIES COMPILE_FLAGS
++ ${UNITTEST_CXX_FLAGS})
++-target_link_libraries(ceph_test_async_networkstack global ${CRYPTO_LIBS} ${BLKID_LIBRARIES} ${CMAKE_DL_LIBS} ${UNITTEST_LIBS})
+++target_link_libraries(ceph_test_async_networkstack global ${CRYPTO_LIBS} ${BLKID_LIBRARIES} ${CMAKE_DL_LIBS} ${UNITTEST_LIBS} ${ATMOIC_LIBRARIES})
++
++ #ceph_perf_msgr_server
++ add_executable(ceph_perf_msgr_server perf_msgr_server.cc)
++@@ -34,7 +34,7 @@
++ add_executable(ceph_perf_msgr_client perf_msgr_client.cc)
++ set_target_properties(ceph_perf_msgr_client PROPERTIES COMPILE_FLAGS
++ ${UNITTEST_CXX_FLAGS})
++-target_link_libraries(ceph_perf_msgr_client os global ${UNITTEST_LIBS})
+++target_link_libraries(ceph_perf_msgr_client os global ${UNITTEST_LIBS} ${ATMOIC_LIBRARIES})
++
++ # test_userspace_event
++ if(HAVE_DPDK)
++--- a/src/test/objectstore/CMakeLists.txt
+++++ b/src/test/objectstore/CMakeLists.txt
++@@ -59,6 +59,7 @@
++ ${EXTRALIBS}
++ ${BLKID_LIBRARIES}
++ ${CMAKE_DL_LIBS}
+++ ${ATMOIC_LIBRARIES}
++ )
++
++ # ceph_test_filestore_idempotent
++--- a/src/test/osdc/CMakeLists.txt
+++++ b/src/test/osdc/CMakeLists.txt
++@@ -8,6 +8,7 @@
++ global
++ ${EXTRALIBS}
++ ${CMAKE_DL_LIBS}
+++ ${ATMOIC_LIBRARIES}
++ )
++ install(TARGETS ceph_test_objectcacher_stress
++ DESTINATION ${CMAKE_INSTALL_BINDIR})
++--- a/src/tools/rbd/CMakeLists.txt
+++++ b/src/tools/rbd/CMakeLists.txt
++@@ -47,7 +47,7 @@
++ rbd_types
++ journal
++ ceph-common global
++- ${BLKID_LIBRARIES} ${CMAKE_DL_LIBS})
+++ ${BLKID_LIBRARIES} ${CMAKE_DL_LIBS} ${ATMOIC_LIBRARIES})
++ if(WITH_KRBD)
++ target_link_libraries(rbd
++ krbd)
++--- a/src/tools/rbd_mirror/CMakeLists.txt
+++++ b/src/tools/rbd_mirror/CMakeLists.txt
++@@ -41,6 +41,7 @@
++ service_daemon/Types.cc)
++ add_library(rbd_mirror_internal STATIC
++ ${rbd_mirror_internal})
+++target_link_libraries(rbd_mirror_internal ${ATMOIC_LIBRARIES})
++
++ add_executable(rbd-mirror
++ main.cc)
++--- a/src/test/system/CMakeLists.txt
+++++ b/src/test/system/CMakeLists.txt
++@@ -10,6 +10,7 @@
++ st_rados_watch.cc
++ st_rados_notify.cc)
++ add_library(systest STATIC ${libsystest_srcs})
+++target_link_libraries(systest ${ATMOIC_LIBRARIES})
++
++ # test_rados_list_parallel
++ add_executable(ceph_test_rados_list_parallel
--- /dev/null
--- /dev/null
++Description: Mark intention fallthroughs for i386 codepaths
++Author: James Page <james.page@ubuntu.com>
++Forwarded: https://github.com/facebook/rocksdb/pull/2700
++
++--- a/src/rocksdb/util/murmurhash.cc
+++++ b/src/rocksdb/util/murmurhash.cc
++@@ -113,8 +113,8 @@ unsigned int MurmurHash2 ( const void *
++
++ switch(len)
++ {
++- case 3: h ^= data[2] << 16;
++- case 2: h ^= data[1] << 8;
+++ case 3: h ^= data[2] << 16; // fallthrough
+++ case 2: h ^= data[1] << 8; // fallthrough
++ case 1: h ^= data[0];
++ h *= m;
++ };
--- /dev/null
--- /dev/null
++## Backported / Upstream
++0010-fix-various-spelling-errors.patch
++0013-remove-ceph.com-ref-to-favicon.patch
++0013-fix-unittest_mclock_priority_queue-not-build-when-ma.patch
++
++## Debian
++virtualenv-never-download.patch
++libatomic.patch
++rocksdb-fallthrough-i386.patch
++0001-CoreLocalArray-class.patch
++0002-core-local-array-type-conversions.patch
++0003-Core-local-statistics.patch
++fix-var-run-perms-sysvinit.patch
++# Ubuntu: FTBFS on armhf
++armhf-ftbfs.patch
++boost-1.67-fixes.patch
++softfp-armel.patch
--- /dev/null
--- /dev/null
++--- a/cmake/modules/SIMDExt.cmake
+++++ b/cmake/modules/SIMDExt.cmake
++@@ -71,6 +71,9 @@
++ CHECK_C_COMPILER_FLAG(-mfpu=neon HAVE_ARM_NEON)
++ if(HAVE_ARM_NEON)
++ set(SIMD_COMPILE_FLAGS "${SIMD_COMPILE_FLAGS} -mfpu=neon")
+++ if(CMAKE_LIBRARY_ARCHITECTURE STREQUAL "arm-linux-gnueabi")
+++ set(SIMD_COMPILE_FLAGS "${SIMD_COMPILE_FLAGS} -mfloat-abi=softfp")
+++ endif()
++ endif()
++
++ elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "i386|i686|amd64|x86_64|AMD64")
--- /dev/null
--- /dev/null
++Last-Update: 2014-10-31
++Forwarded: no
++Author: Bastian Blank <waldi@debian.org>
++Description: Ask virtualenv to never download anything
++
++--- a/src/test/run-cli-tests
+++++ b/src/test/run-cli-tests
++@@ -30,7 +30,7 @@ if [ ! -e "$CRAM_BIN" ]; then
++ # patched cram to support that. See upstream ticket at
++ # https://bitbucket.org/brodie/cram/issue/9/allow-read-only-directories-for-t
++ # -- tv@inktank.com
++- virtualenv "$VENV" && $VENV/bin/pip --log "$VENV"/log.txt install "$SRCDIR/downloads/cram-0.5.0ceph.2011-01-14.tar.gz"
+++ virtualenv --system-site-packages "$VENV" && $VENV/bin/pip --log "$VENV"/log.txt install "$SRCDIR/downloads/cram-0.5.0ceph.2011-01-14.tar.gz"
++ fi
++
++ SRCDIR_ABS="$(readlink -f "$SRCDIR")"
--- /dev/null
- usr/lib/python2*/dist-packages/ceph_volume_client.py*
++usr/lib/python2*/dist-packages/ceph_volume_client.py
+usr/lib/python2*/dist-packages/cephfs-*.egg-info
+usr/lib/python2*/dist-packages/cephfs.so
--- /dev/null
--- /dev/null
++../src/init-radosgw
--- /dev/null
--- /dev/null
++# Ceph service files define their own targets which are then WantedBy
++# multi-user.target
++systemd-service-file-refers-to-unusual-wantedby-target lib/systemd/system/ceph-radosgw@.service ceph-radosgw.target
--- /dev/null
--- /dev/null
++rm_conffile /etc/bash_completion.d/radosgw-admin 10.2.5-1~
++rm_conffile /etc/init/radosgw-all-starter.conf 10.2.5-1~
++rm_conffile /etc/init/radosgw-all.conf 10.2.5-1~
++rm_conffile /etc/init/radosgw-instance.conf 10.2.5-1~
--- /dev/null
- [ -x /sbin/start ] && start radosgw-all || :
-
- if ! dpkg-statoverride --list /var/lib/ceph/radosgw >/dev/null
- then
- chown $SERVER_USER:$SERVER_GROUP /var/lib/ceph/radosgw
+#!/bin/sh
+# vim: set noet ts=8:
+# postinst script for radosgw
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+#
+# postinst configure <most-recently-configured-version>
+# old-postinst abort-upgrade <new-version>
+# conflictor's-postinst abort-remove in-favour <package> <new-version>
+# postinst abort-remove
+# deconfigured's-postinst abort-deconfigure in-favour <failed-install-package> <version> [<removing conflicting-package> <version>]
+#
+# The current action is to simply remove the mistakenly-added
+# /etc/init/ceph.conf file; this could be done in any of these cases,
+# although technically it will leave the system in a different state
+# than the original install that included that file. So instead we
+# only remove on "configure", since that's the only time we know we're
+# successful in installing a newer package than the erroneous version.
+
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+[ -f "/etc/default/ceph" ] && . /etc/default/ceph
+[ -z "$SERVER_USER" ] && SERVER_USER=ceph
+[ -z "$SERVER_GROUP" ] && SERVER_GROUP=ceph
+
+case "$1" in
+ configure)
++ if ! dpkg-statoverride --list /var/lib/ceph/radosgw >/dev/null; then
++ chown $SERVER_USER:$SERVER_GROUP /var/lib/ceph/radosgw
+ fi
+ ;;
+ abort-upgrade|abort-remove|abort-deconfigure)
+ :
+ ;;
+
+ *)
+ echo "postinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
++if dpkg-maintscript-helper supports mv_conffile 2>/dev/null; then
++ dpkg-maintscript-helper mv_conffile \
++ /etc/init/radosgw.conf /etc/init/radosgw-instance.conf \
++ 0.72.1-3~ radosgw -- "$@"
++fi
++
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
+
--- /dev/null
--- /dev/null
++#!/bin/sh
++
++set -e
++
++if dpkg-maintscript-helper supports mv_conffile 2>/dev/null; then
++ dpkg-maintscript-helper mv_conffile \
++ /etc/init/radosgw.conf /etc/init/radosgw-instance.conf \
++ 0.72.1-3~ radosgw -- "$@"
++fi
++
++#DEBHELPER#
++
++exit 0
--- /dev/null
--- /dev/null
++#!/bin/sh
++
++set -e
++
++if dpkg-maintscript-helper supports mv_conffile 2>/dev/null; then
++ dpkg-maintscript-helper mv_conffile \
++ /etc/init/radosgw.conf /etc/init/radosgw-instance.conf \
++ 0.72.1-3~ radosgw -- "$@"
++fi
++
++#DEBHELPER#
++
++exit 0
--- /dev/null
- [ -x /sbin/stop ] && stop radosgw-all || true
+#!/bin/sh
+# vim: set noet ts=8:
+
+set -e
+
+case "$1" in
+ remove)
+ invoke-rc.d radosgw stop || {
+ RESULT=$?
+ if [ $RESULT != 100 ]; then
+ exit $RESULT
+ fi
+ }
+ ;;
+
+ *)
+ ;;
+esac
+
+#DEBHELPER#
+
+exit 0
--- /dev/null
--- /dev/null
++# Ceph service files define their own targets which are then WantedBy
++# multi-user.target
++systemd-service-file-refers-to-unusual-wantedby-target lib/systemd/system/ceph-rbd-mirror@.service ceph-rbd-mirror.target
--- /dev/null
--- /dev/null
++usr/bin/rest-bench
--- /dev/null
- export DH_VERBOSE=1
- export DESTDIR=$(CURDIR)/debian/tmp
+#!/usr/bin/make -f
+# -*- makefile -*-
- extraopts += -DUSE_CRYPTOPP=OFF -DWITH_OCF=ON -DWITH_LTTNG=ON -DWITH_PYTHON3=ON
++#export DH_VERBOSE=1
++
++# Set multiarch triplet
++DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH)
++
++# Initialize empty flag variables and export them
++export DEB_CFLAGS_MAINT_APPEND =
++export DEB_CXXFLAGS_MAINT_APPEND =
++export DEB_CPPFLAGS_MAINT_APPEND =
++
++# Reduce size of debug symbols to fix FTBFS due to the
++# 2GB/3GB address space limits on 32bit
++DEB_HOST_ARCH_BITS ?= $(shell dpkg-architecture -qDEB_HOST_ARCH_BITS)
++ifeq (32,$(DEB_HOST_ARCH_BITS))
++ DEB_CFLAGS_MAINT_APPEND += -g1
++ DEB_CXXFLAGS_MAINT_APPEND += -g1
++endif
++
++# minimise needless linking
++export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
++
++# Enable hardening
++export DEB_BUILD_MAINT_OPTIONS = hardening=+all
+
+export DEB_HOST_ARCH ?= $(shell dpkg-architecture -qDEB_HOST_ARCH)
+
- # assumes that ceph is exmpt from multiarch support, so we override the libdir.
- extraopts += -DCMAKE_INSTALL_LIBDIR=/usr/lib
++export JAVA_HOME=/usr/lib/jvm/default-java
++## Set JAVAC to prevent FTBFS due to incorrect use of 'gcj' if found (see "m4/ac_prog_javac.m4").
++export JAVAC=javac
++
++extraopts += -DWITH_OCF=ON
++extraopts += -DWITH_DEBUG=ON
+extraopts += -DWITH_CEPHFS_JAVA=ON
- extraopts += -DCMAKE_INSTALL_SYSCONFDIR=/etc
- ifneq (,$(filter parallel=%,$(DEB_BUILD_OPTIONS)))
- NUMJOBS = $(patsubst parallel=%,%,$(filter parallel=%,$(DEB_BUILD_OPTIONS)))
- extraopts += -DBOOST_J=$(NUMJOBS)
- endif
++## do not build with lttng just yet, see #765842.
++extraopts += -DWITH_LTTNG=OFF
+extraopts += -DCMAKE_INSTALL_LIBEXECDIR=/usr/lib
- ifeq ($(DEB_HOST_ARCH), armel)
- # armel supports ARMv4t or above instructions sets.
- # libatomic-ops is only usable with Ceph for ARMv6 or above.
- extraopts += -DWITH_ATOMIC_OPS=OFF
- endif
+
- ifneq (,$(filter $(DEB_HOST_ARCH), arm armel armhf arm64 i386 amd64 mips mipsel powerpc ppc64))
- # libboost_context only support the archs above
++# rocksdb is not packaged by anyone. build it if we can.
++extraopts += -DWITH_SYSTEM_ROCKSDB=OFF
++
++# Build Python 3 bindings
++extraopts += -DWITH_PYTHON3=ON
++
++# Use system boost
++extraopts += -DWITH_SYSTEM_BOOST=ON
+
- %:
- dh $@ --buildsystem=cmake --with javahelper,python2,python3,systemd --parallel
-
- override_dh_auto_configure:
- env | sort
- dh_auto_configure --buildsystem=cmake -- $(extraopts) $(CEPH_EXTRA_CMAKE_ARGS)
-
- override_dh_auto_build:
- dh_auto_build --buildsystem=cmake
- cp src/init-radosgw debian/radosgw.init
-
- override_dh_auto_clean:
- dh_auto_clean --buildsystem=cmake
- rm -f debian/radosgw.init debian/ceph.logrotate
-
- override_dh_auto_install:
- dh_auto_install --buildsystem=cmake --destdir=$(DESTDIR)
- install -D -m 644 udev/50-rbd.rules $(DESTDIR)/lib/udev/rules.d/50-rbd.rules
- install -D -m 644 udev/95-ceph-osd.rules $(DESTDIR)/lib/udev/rules.d/95-ceph-osd.rules
- install -D -m 644 udev/60-ceph-by-parttypeuuid.rules $(DESTDIR)/lib/udev/rules.d/60-ceph-by-parttypeuuid.rules
- install -D -m 644 src/etc-rbdmap $(DESTDIR)/etc/ceph/rbdmap
- install -D -m 644 etc/sysctl/90-ceph-osd.conf $(DESTDIR)/etc/sysctl.d/30-ceph-osd.conf
++# Not all architectures support libboost-context and libboost-coroutine
++# Disabling boost-context also disables the RADOS GW Beast frontend
++ifneq (,$(filter $(DEB_HOST_ARCH), i386 amd64 armel armhf arm64 mips mipsel powerpc ppc64el))
+ extraopts += -DWITH_BOOST_CONTEXT=ON
+else
+ extraopts += -DWITH_BOOST_CONTEXT=OFF
+endif
+
- # doc/changelog is a directory, which confuses dh_installchangelogs
- override_dh_installchangelogs:
- dh_installchangelogs --exclude doc/changelog
++# Setup max parallel builds based on architecture
++ifeq ($(DEB_HOST_ARCH), arm64)
++ NUMJOBS=1
++else ifneq (,$(filter parallel=%,$(DEB_BUILD_OPTIONS)))
++ NUMJOBS=$(patsubst parallel=%,%,$(filter parallel=%,$(DEB_BUILD_OPTIONS)))
++else
++ NUMJOBS=2
++endif
+
- override_dh_installdocs:
- dh_installdocs -a --all ChangeLog
++# Set _FILE_OFFSET_BITS=64 globally. While this is set by the build system
++# through an automake variable, configure does not know about this and thus
++# fails when testing xfs.h on 32bit systems.
++DEB_CPPFLAGS_MAINT_APPEND += -D_FILE_OFFSET_BITS=64
++
++# Build fix for mips/mipsel
++# The first two variables work around a compiler bug which leads to virtual
++# memory exhaustion while compiling the testsuite. See Debian bug #849657 and
++# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79030
++ifneq (,$(filter $(DEB_HOST_ARCH), mips mipsel))
++ DEB_CFLAGS_MAINT_APPEND += --param ggc-min-expand=5
++ DEB_CXXFLAGS_MAINT_APPEND += --param ggc-min-expand=5
++endif
+
- override_dh_installlogrotate:
- cp src/logrotate.conf debian/ceph-common.logrotate
- dh_installlogrotate -pceph-common
++%:
++ dh $@ --buildsystem=cmake --with javahelper,python2,python3 --max-parallel=$(NUMJOBS)
+
- # dh_installinit is only set up to handle one upstart script
- # per package, so do this ourselves
- install -d -m0755 debian/ceph-base/etc/init
- install -m0644 src/upstart/ceph-all.conf debian/ceph-base/etc/init
- install -d -m0755 debian/ceph-osd/etc/init
- install -m0644 src/upstart/ceph-osd*.conf debian/ceph-osd/etc/init
- install -m0644 src/upstart/ceph-disk.conf debian/ceph-osd/etc/init
- install -d -m0755 debian/ceph-mon/etc/init
- install -m0644 src/upstart/ceph-mon*.conf debian/ceph-mon/etc/init
- install -d -m0755 debian/ceph-common/etc/init
- install -m0644 src/upstart/rbdmap.conf debian/ceph-common/etc/init
- install -d -m0755 debian/ceph-mds/etc/init
- install -m0644 src/upstart/ceph-mds*.conf debian/ceph-mds/etc/init
- install -d -m0755 debian/ceph-mgr/etc/init
- install -m0644 src/upstart/ceph-mgr*.conf debian/ceph-mgr/etc/init
- install -d -m0755 debian/radosgw/etc/init
- install -m0644 src/upstart/radosgw*.conf debian/radosgw/etc/init
- install -d -m0755 debian/rbd-mirror/etc/init
- install -m0644 src/upstart/ceph-rbd-mirror*.conf debian/rbd-mirror/etc/init
++override_dh_auto_configure:
++ dh_auto_configure --buildsystem=cmake -- $(extraopts)
++
++override_dh_install:
++ # NOTE: ensure that any versioned erasure coding test code is dropped
++ # from the package install - package ships unversioned modules.
++ rm -f $(CURDIR)/debian/tmp/usr/lib/*/ceph/erasure-code/libec_*.so.*
++ find $(CURDIR)/debian/tmp/usr/lib/*/ceph/erasure-code -type l -delete || :
++ # Remove RUNPATH from JNI libraries. I did not find a way to disable this
++ # using the upstream build system. Pointers welcome.
++ # The /usr/lib/*/ceph directory needs to be preserved to find libceph-common.
++ chrpath --replace /usr/lib/$(DEB_HOST_MULTIARCH)/ceph debian/tmp/usr/lib/*/libcephfs_jni.so.1.0.0
++ dh_install
+
+override_dh_installinit:
- install -d -m0755 debian/ceph-common/lib/systemd/system
- install -m0644 systemd/ceph.target debian/ceph-common/lib/systemd/system
- install -d -m0755 debian/ceph-common/etc/default
- install -m0644 etc/default/ceph debian/ceph-common/etc/default/
++ dh_installinit -pceph-common --no-start --name=rbdmap
++ # The /etc/init.d/ceph SysV init script is already installed by the upstream
++ # build system.
++ dh_installinit -pceph-base --no-start --name=ceph --only-scripts
++ dh_installinit --no-start --remaining
+ # install the systemd stuff manually since we have funny service names
-
++ # and need to update the paths in all of the files post install
++ # systemd:ceph-common
+ install -d -m0755 debian/ceph-common/usr/lib/tmpfiles.d
+ install -m 0644 -D systemd/ceph.tmpfiles.d debian/ceph-common/usr/lib/tmpfiles.d/ceph.conf
- install -d -m0755 debian/ceph-osd/lib/systemd/system
++ install -d -m0755 debian/ceph-common/lib/systemd/system
++ install -m 0644 systemd/rbdmap.service debian/ceph-common/lib/systemd/system
++ # systemd:ceph-base
+ install -d -m0755 debian/ceph-base/lib/systemd/system
++ install -m0644 systemd/ceph.target debian/ceph-common/lib/systemd/system
++ # systemd:ceph-mon
+ install -d -m0755 debian/ceph-mon/lib/systemd/system
- install -m0644 systemd/ceph-volume@.service debian/ceph-osd/lib/systemd/system
- install -m0644 systemd/rbdmap.service debian/ceph-common/lib/systemd/system
- sed -i s./etc/sysconfig/./etc/default/.g debian/ceph-mon/lib/systemd/system/ceph-mon@.service
+ install -m0644 systemd/ceph-mon@.service debian/ceph-mon/lib/systemd/system
++ sed -i s./etc/sysconfig/./etc/default/.g debian/ceph-mon/lib/systemd/system/ceph-mon@.service
++ install -m0644 systemd/ceph-mon.target debian/ceph-mon/lib/systemd/system
++ # systemd:ceph-osd
++ install -d -m0755 debian/ceph-osd/lib/systemd/system
+ install -m0644 systemd/ceph-osd@.service debian/ceph-osd/lib/systemd/system
+ install -m0644 systemd/ceph-disk@.service debian/ceph-osd/lib/systemd/system
- sed -i s./etc/sysconfig/./etc/default/.g debian/ceph-osd/lib/systemd/system/ceph-volume@.service
- install -m0644 systemd/ceph-mon.target debian/ceph-mon/lib/systemd/system
+ sed -i s./etc/sysconfig/./etc/default/.g debian/ceph-osd/lib/systemd/system/ceph-osd@.service
+ sed -i s./etc/sysconfig/./etc/default/.g debian/ceph-osd/lib/systemd/system/ceph-disk@.service
-
+ install -m0644 systemd/ceph-osd.target debian/ceph-osd/lib/systemd/system
-
- install -d -m0755 debian/ceph-fuse/lib/systemd/system
- install -m0644 systemd/ceph-fuse@.service debian/ceph-fuse/lib/systemd/system
- sed -i s./etc/sysconfig/./etc/default/.g debian/ceph-fuse/lib/systemd/system/ceph-fuse@.service
- install -m0644 systemd/ceph-fuse.target debian/ceph-fuse/lib/systemd/system
-
- install -d -m0755 debian/ceph-mgr/lib/systemd/system
- install -m0644 systemd/ceph-mgr@.service debian/ceph-mgr/lib/systemd/system
- sed -i s./etc/sysconfig/./etc/default/.g debian/ceph-mgr/lib/systemd/system/ceph-mgr@.service
- install -m0644 systemd/ceph-mgr.target debian/ceph-mgr/lib/systemd/system
-
++ # systemd:ceph-mds
+ install -d -m0755 debian/ceph-mds/lib/systemd/system
+ install -m0644 systemd/ceph-mds@.service debian/ceph-mds/lib/systemd/system
+ sed -i s./etc/sysconfig/./etc/default/.g debian/ceph-mds/lib/systemd/system/ceph-mds@.service
+ install -m0644 systemd/ceph-mds.target debian/ceph-mds/lib/systemd/system
-
++ # systemd:radosgw
+ install -d -m0755 debian/radosgw/lib/systemd/system
+ install -m0644 systemd/ceph-radosgw@.service debian/radosgw/lib/systemd/system
+ sed -i s./etc/sysconfig/./etc/default/.g debian/radosgw/lib/systemd/system/ceph-radosgw@.service
+ install -m0644 systemd/ceph-radosgw.target debian/radosgw/lib/systemd/system
-
- dh_installinit -p ceph-base --name ceph --no-start
- dh_installinit -p radosgw --no-start
-
- # NOTE: execute systemd helpers so they pickup dh_install'ed units and targets
- dh_systemd_enable
- dh_systemd_start --no-restart-on-upgrade
++ # disable SysV init file for systemd
++ ln -s /dev/null debian/radosgw/lib/systemd/system/radosgw.service
++ # systemd:rbd-mirror
+ install -d -m0755 debian/rbd-mirror/lib/systemd/system
+ install -m0644 systemd/ceph-rbd-mirror@.service debian/rbd-mirror/lib/systemd/system
+ sed -i s./etc/sysconfig/./etc/default/.g debian/rbd-mirror/lib/systemd/system/ceph-rbd-mirror@.service
+ install -m0644 systemd/ceph-rbd-mirror.target debian/rbd-mirror/lib/systemd/system
- override_dh_strip:
- dh_strip -pceph-mds --dbg-package=ceph-mds-dbg
- dh_strip -pceph-fuse --dbg-package=ceph-fuse-dbg
- dh_strip -pceph-mgr --dbg-package=ceph-mgr-dbg
- dh_strip -pceph-mon --dbg-package=ceph-mon-dbg
- dh_strip -pceph-osd --dbg-package=ceph-osd-dbg
- dh_strip -pceph-base --dbg-package=ceph-base-dbg
- dh_strip -pceph-fuse --dbg-package=ceph-fuse-dbg
- dh_strip -prbd-fuse --dbg-package=rbd-fuse-dbg
- dh_strip -prbd-mirror --dbg-package=rbd-mirror-dbg
- dh_strip -prbd-nbd --dbg-package=rbd-nbd-dbg
- dh_strip -pceph-common --dbg-package=ceph-common-dbg
- dh_strip -plibrados2 --dbg-package=librados2-dbg
- dh_strip -plibradosstriper1 --dbg-package=libradosstriper1-dbg
- dh_strip -plibrbd1 --dbg-package=librbd1-dbg
- dh_strip -plibcephfs2 --dbg-package=libcephfs2-dbg
- dh_strip -plibrgw2 --dbg-package=librgw2-dbg
- dh_strip -pradosgw --dbg-package=radosgw-dbg
- dh_strip -pceph-test --dbg-package=ceph-test-dbg
- dh_strip -ppython-rados --dbg-package=python-rados-dbg
- dh_strip -ppython3-rados --dbg-package=python3-rados-dbg
- dh_strip -ppython-rbd --dbg-package=python-rbd-dbg
- dh_strip -ppython3-rbd --dbg-package=python3-rbd-dbg
- dh_strip -ppython-rgw --dbg-package=python-rgw-dbg
- dh_strip -ppython3-rgw --dbg-package=python3-rgw-dbg
- dh_strip -ppython-cephfs --dbg-package=python-cephfs-dbg
- dh_strip -ppython3-cephfs --dbg-package=python3-cephfs-dbg
-
- override_dh_shlibdeps:
- dh_shlibdeps -a --exclude=erasure-code --exclude=rados-classes --exclude=compressor
-
- override_dh_python2:
- for binding in rados cephfs rbd rgw; do \
- dh_python2 -p python-$$binding; \
- done
- dh_python2 -p ceph-common
- dh_python2 -p ceph-base
- dh_python2 -p ceph-osd
- dh_python2 -p ceph-mgr
-
- override_dh_python3:
- for binding in rados cephfs rbd rgw; do \
- dh_python3 -p python3-$$binding; \
- done
- dh_python3 -p python3-ceph-argparse
-
- # do not run tests
- override_dh_auto_test:
++ # Ensure Debian/Ubuntu specific systemd units are NOT automatically enabled and started
++ # Enable systemd targets only
++ dh_systemd_enable -Xceph-mon.service -Xceph-osd.service -X ceph-mds.service
++ # Start systemd targets only
++ dh_systemd_start --no-restart-on-upgrade -Xceph-mon.service -Xceph-osd.service -X ceph-mds.service
+
+override_dh_systemd_enable:
+ # systemd enable done as part of dh_installinit
+
+override_dh_systemd_start:
+ # systemd start done as part of dh_installinit
+
- .PHONY: override_dh_autoreconf override_dh_auto_configure override_dh_auto_build override_dh_auto_clean override_dh_auto_install override_dh_installdocs override_dh_installlogrotate override_dh_installinit override_dh_systemd_start override_dh_strip override_dh_auto_test
++override_dh_makeshlibs:
++ # exclude jni libraries in libcephfs-jni to avoid pointless ldconfig
++ # calls in maintainer scripts; exclude private erasure-code plugins.
++ dh_makeshlibs -V -X/usr/lib/jni -X/usr/lib/$(DEB_HOST_MULTIARCH)/ceph/erasure-code \
++ -X/usr/lib/$(DEB_HOST_MULTIARCH)/ceph/compressor \
++ -X/usr/lib/$(DEB_HOST_MULTIARCH)/rados-classes \
+
++override_dh_auto_test:
++ -dh_auto_test -v || cat src/test-suite.log
++
++# can be removed once migrated to debhelper compat level 12 where --list-missing is
++# the default
++override_dh_missing:
++ dh_missing --list-missing
++
++override_dh_clean:
++ # While these files look silly they are really part of the upstream source
++ # tarball and should thus be kept until upstream fixes this.
++ dh_clean --exclude=src/civetweb/examples/websocket_client/ssl/server.key.orig \
++ --exclude=src/civetweb/resources/cert/client.key.orig \
++ --exclude=src/civetweb/resources/cert/server.key.orig \
++ --exclude=src/erasure-code/jerasure/jerasure/Examples/makefile.orig \
++ --exclude=src/erasure-code/jerasure/jerasure/include/config.h.in~ \
++ --exclude=src/erasure-code/jerasure/jerasure/makefile.orig \
++ etc/sysctl/90-ceph-osd.conf
++
++.PHONY: override_dh_auto_configure override_dh_installinit override_dh_makeshlibs override_dh_auto_test override_dh_missing
--- /dev/null
- 1.0
++3.0 (quilt)
--- /dev/null
--- /dev/null
++# Source under the JSON license has been removed from the source tarball
++license-problem-json-evil src/rapidjson/license.txt
++
++# These are false positives, they are just long lines in HTML/JS code and there is
++# no indication that there is any other prefered form of modification.
++source-is-missing src/rocksdb/docs/_includes/*.html *
++source-is-missing src/civetweb/src/third_party/duktape-1.3.0/src-separate/duk_initjs_min.js
++source-is-missing debian/missing-sources/src/pybind/mgr/dashboard/static/libs/Chart.js/2.4.0/Chart.js *
++source-is-missing debian/missing-sources/src/pybind/mgr/dashboard/static/libs/rivets/0.9.6/rivets.bundled.js *
--- /dev/null
--- /dev/null
++#!/bin/sh
++# autopkgtest check: Build and run a program against librados2 to
++# validate that headers are installed and libraries exists
++
++set -e
++
++WORKDIR=$(mktemp -d)
++trap "rm -rf $WORKDIR" 0 INT QUIT ABRT PIPE TERM
++cd $WORKDIR
++cat <<EOF > radostest.c
++#include <rados/librados.h>
++
++int
++main(void)
++{
++ int err;
++ rados_t cluster;
++
++ err = rados_create(&cluster, NULL);
++ if (err < 0) {
++ return (1);
++ }
++ return(0);
++}
++EOF
++
++gcc -o radostest radostest.c -lrados
++echo "build: OK"
++[ -x radostest ]
++./radostest
++echo "run: OK"
--- /dev/null
--- /dev/null
++#!/bin/sh
++# autopkgtest check: Build and run a program against librbd1 to
++# validate that headers are installed and libraries exists
++
++set -e
++
++WORKDIR=$(mktemp -d)
++trap "rm -rf $WORKDIR" 0 INT QUIT ABRT PIPE TERM
++cd $WORKDIR
++cat <<EOF > rbdtest.c
++#include <rbd/librbd.h>
++
++int
++main(void)
++{
++ return(0);
++}
++EOF
++
++gcc -o rbdtest rbdtest.c -lrbd
++echo "build: OK"
++[ -x rbdtest ]
++./rbdtest
++echo "run: OK"
--- /dev/null
--- /dev/null
++#!/bin/bash
++
++set -e
++
++CLIENTS=('ceph')
++
++for client in "${CLIENTS[@]}"; do
++ echo -n "Testing client $client: "
++ $client -v 2>&1 > /dev/null
++ echo "OK"
++done
--- /dev/null
--- /dev/null
++Tests: ceph-client build-rados build-rbd python-ceph python3-ceph
++Depends: ceph-common, librbd-dev, librados-dev, python-ceph, python3-ceph, build-essential
++Restrictions: needs-root
--- /dev/null
--- /dev/null
++#!/usr/bin/python
++
++# Test that Ceph Python modules can be imported OK
++import rbd
++import rados
++import rgw
++import cephfs
++import ceph_volume_client
++import ceph_argparse
++import ceph_daemon
++
++print "python-ceph: OK"
--- /dev/null
--- /dev/null
++#!/usr/bin/python3
++
++# Test that Ceph Python 3 modules can be imported OK
++import warnings
++# Ignore deprecation warning when importing from collections
++# this is fixed upstream in Nautilus and may get backported.
++warnings.filterwarnings('ignore', 'Using or importing the ABCs from \'collections\' instead of from \'collections.abc\' is deprecated, and in 3.8 it will stop working')
++import rbd
++import rados
++import rgw
++import cephfs
++import ceph_volume_client
++import ceph_argparse
++import ceph_daemon
++
++print("python3-ceph: OK")
--- /dev/null
- version=3
- http://ceph.com/download/ceph-(\d.*)\.tar\.gz
++version=4
++opts="dversionmangle=s/\+dfsg\d*$//, \
++ repacksuffix=+dfsg1 \
++ compression=xz" \
++ https://download.ceph.com/tarballs/ceph-(\d.*)\.tar\.gz