--- /dev/null
+.TH 0AD "6" "April 7, 2012"
+.SH NAME
+0ad \- A real-time strategy game of ancient warfare
+.SH SYNOPSIS
+.B 0ad
+.RI [ options ]
+.SH DESCRIPTION
+0 A.D. is a free, open-source, cross-platform real-time strategy game
+of ancient warfare. As the military leader of an ancient civilisation,
+you must gather the resources you need to raise a military force
+capable of dominating your enemies.
+.SH OPTIONS
+.SS Basic gameplay
+.TP
+.B \-autostart
+Load a map instead of showing main menu (see below).
+.TP
+.B \-editor
+Launch the Atlas scenario editor.
+.TP
+.B \-mod NAME
+Start the game using NAME mod.
+.TP
+.B \-quickstart
+Load faster (disables audio and some system info logging).
+.SS Autostart
+.TP
+.B \-autostart=NAME
+Map NAME for scenario, or rms name for random map.
+.TP
+.B \-autostart-ai=PLAYER:AI
+Adds named AI to the given PLAYER (e.g. 2:testbot).
+.SS Multiplayer
+.TP
+.B \-autostart-playername=NAME
+Multiplayer local player NAME (default 'anonymous').
+.TP
+.B \-autostart-host
+Multiplayer host mode.
+.TP
+.B \-autostart-players=NUMBER
+Multiplayer host: NUMBER of client players (default 2).
+.TP
+.B \-autostart-client
+Multiplayer client mode.
+.TP
+.B \-autostart-ip=IP
+Multiplayer client: connect to this host IP.
+.SS Random maps only
+.TP
+.B \-autostart-random
+Random map.
+.TP
+.B \-autostart-random=SEED
+Random map with SEED value (default 0, use \-1 for random).
+.TP
+.B \-autostart-size=TILES
+Random map SIZE in tiles (default 192).
+.TP
+.B \-autostart-players=NUMBER
+NUMBER of players on random map.
+.SS Configuration
+.TP
+.B \-conf:KEY=VALUE
+Set a config value (overrides the contents of system.cfg).
+.TP
+.B \-g=F
+Set the gamma correction to 'F' (default 1.0).
+.TP
+.B \-nosound
+Disable audio.
+.TP
+.B \-onlyPublicFiles
+Force game to use only the public (default) mod.
+.TP
+.B \-shadows
+Enable shadows.
+.TP
+.B \-vsync
+Enable VSync, i.e. lock FPS to monitor refresh rate.
+.TP
+.B \-xres=N
+Set screen X resolution to 'N'.
+.TP
+.B \-yres=N
+Set screen Y resolution to 'N'.
+.SS Advanced / diagnostic
+.TP
+.B \-dumpSchema
+Creates a file entity.rng in the working directory, containing
+complete entity XML schema, used by various analysis tools.
+.TP
+.B \-replay=PATH
+Non-visual replay of a previous game, used for analysis purposes.
+PATH is system path to commands.txt containing simulation log.
+.TP
+.B \-writableRoot
+Store runtime game data in root data directory (only use if you
+have write permissions on that directory).
+.SS Archive builder
+.TP
+.B \-archivebuild=PATH
+System PATH of the base directory containing mod data to be
+archived/precached.
+.TP
+.B \-archivebuild-output=PATH
+System PATH to output of the resulting .zip archive (use with
+archivebuild).
+.TP
+.B \-archivebuild-compress
+Enable deflate compression in the .zip (no zip compression by
+default since it hurts compression of release packages).
+
+.SH "FILES"
+.TP
+.B ~/.config/0ad/config/local.cfg
+User specific settings. You may put in this file any user specific
+settings using the same syntax of the default settings file.
+.PP
+0 A.D. also uses the
+.B ~/.cache/0ad/
+,
+.B ~/.config/0ad/
+and
+.B ~/.local/share/0ad/
+directories for other user specific files, such as texture cache,
+screenshots and saved games.
+
+.SH SEE ALSO
+.TP
+.B http://wildfiregames.com/0ad/
+0 A.D. main web site.
+.TP
+.B http://trac.wildfiregames.com/
+0 A.D. wiki and developer web site.
+
+.SH AUTHOR
+0ad was written by the 0 A.D. Team.
+.PP
+This manual page was written by Fabio Pedretti <fabio.ped@libero.it>,
+for the Debian project (and may be used by others). Permission is
+granted to copy, distribute and/or modify this document under the
+terms of the GNU General Public License, Version 2 or any later version
+published by the Free Software Foundation.
+.PP
+On Debian systems, the complete text of the GNU General Public License
+can be found in /usr/share/common-licenses/GPL-2.
--- /dev/null
+0ad (0.0.23.1-5) unstable; urgency=medium
+
+ * Fix FTBFS with gcc-10. (Closes: #956967)
+ * Avoid build-dep on unversioned python2. (Closes: #967118)
+ * Cherrypick patch from upstream to add workaround for L3 cache detection on
+ Ryzen 3000 series CPUs. (Closes: #949699)
+ * Update to dh compat level 12.
+ * Update Standards version to 4.5.0.
+ * Add upstream metadata file.
+
+ -- Vincent Cheng <vcheng@debian.org> Tue, 18 Aug 2020 02:48:00 -0700
+
+0ad (0.0.23.1-4) unstable; urgency=medium
+
+ * Team upload.
+ * Re-upload source-only to enable migration to testing.
+
+ -- Bruno Kleinert <fuddl@debian.org> Mon, 05 Aug 2019 18:56:58 +0200
+
+0ad (0.0.23.1-3) unstable; urgency=medium
+
+ * Team upload.
+ * Build against GTK 3 version of wxWidget. Replaced build dependency
+ libwxgtk3.0-dev by libwxgtk3.0-gtk3-dev. (Closes: #933456)
+ * Added NEWS.Debian to document a workaround for a known issue caused by the
+ GTK 3 variant of wxWidget.
+
+ -- Bruno Kleinert <fuddl@debian.org> Sat, 03 Aug 2019 05:18:37 +0200
+
+0ad (0.0.23.1-2) unstable; urgency=medium
+
+ * d/control: version 0.0.23.1 also needs a new 0ad-data
+
+ -- Ludovic Rousseau <rousseau@debian.org> Wed, 09 Jan 2019 10:33:09 +0100
+
+0ad (0.0.23.1-1) unstable; urgency=medium
+
+ * New upstream release.
+ - Re-release of 0 A.D. Alpha 23 Ken Wood
+ https://play0ad.com/re-release-of-0-a-d-alpha-23-ken-wood/
+ * d/control: change Depends: to use 0.0.23 since 0ad-data is unchanged.
+ * Fix "Alpha 23 Lobby lag and other multiplayer issues" (Closes: #905470)
+
+ -- Ludovic Rousseau <rousseau@debian.org> Fri, 04 Jan 2019 11:18:19 +0100
+
+0ad (0.0.23-1) unstable; urgency=medium
+
+ * New upstream release.
+ - Remove enable-hardening-relro.patch and armhf-wchar-signedness.patch;
+ obsolete, applied upstream.
+ - Remove create_empty_dirs.patch and move logic into debian/rules.
+ * Add new build-dep on libsodium-dev (>= 1.0.14).
+ * Restore all supported archs as of 0ad/0.0.21-2, i.e. arm64 and kfreebsd-*.
+ * Update Standards version to 4.1.4, no changes required.
+
+ -- Vincent Cheng <vcheng@debian.org> Mon, 21 May 2018 23:38:30 -0700
+
+0ad (0.0.22-4) unstable; urgency=medium
+
+ * Create empty directory not stored in git
+ * Add description to a quilt patch
+ * Enable parallel build
+ * Use debhelper version 10 (instead of 9)
+ * Migrate the repository from SVN to GIT
+
+ -- Ludovic Rousseau <rousseau@debian.org> Wed, 10 Jan 2018 16:27:16 +0100
+
+0ad (0.0.22-3.1) unstable; urgency=medium
+
+ * Non-maintainer upload with maintainers permission.
+ * Add armhf back to architecture list.
+ * Fix "0ad FTBFS with on armhf with gcc 7: error: call of overloaded
+ 'abs(unsigned int)' is ambiguous" (Closes: #879071)
+
+ -- Peter Michael Green <plugwash@debian.org> Tue, 21 Nov 2017 00:15:10 +0000
+
+0ad (0.0.22-3) unstable; urgency=medium
+
+ * remove support of kfreebsd-amd64 and kfreebsd-i386 since auto test fails
+ for 0ad alpha 22 and then 0ad FTBFS.
+ * debian/source/lintian-overrides: remove unused-override
+ outdated-autotools-helper-file.
+ Reported by lintian.
+ * debian/rules: enable hardening=+all
+
+ -- Ludovic Rousseau <rousseau@debian.org> Sat, 04 Nov 2017 11:04:26 +0100
+
+0ad (0.0.22-2) unstable; urgency=medium
+
+ * Fix "0ad FTBFS with on armhf with gcc 7: error: call of overloaded
+ 'abs(unsigned int)' is ambiguous" by removing support of armhf
+ (Closes: #879071)
+ * remove support of arm64 because of FTBFS in spidermonkey/mozjs-38.0.0
+ https://buildd.debian.org/status/fetch.php?pkg=0ad&arch=arm64&ver=0.0.22-1&stamp=1508351579&raw=0
+
+ -- Ludovic Rousseau <rousseau@debian.org> Fri, 27 Oct 2017 16:22:15 +0200
+
+0ad (0.0.22-1) unstable; urgency=medium
+
+ * New upstream release.
+ * Fix "New Release: 0 A.D. Alpha 22 Venustas" (Closes: #872654)
+ * Export SHELL to make mozjs-38 build using debuild(1)
+
+ -- Ludovic Rousseau <rousseau@debian.org> Wed, 18 Oct 2017 15:33:17 +0200
+
+0ad (0.0.21-2) unstable; urgency=medium
+
+ * Fix FTBFS by installing missing files with dh_install.
+
+ -- Vincent Cheng <vcheng@debian.org> Sat, 12 Nov 2016 22:57:56 -0800
+
+0ad (0.0.21-1) unstable; urgency=medium
+
+ * New upstream release.
+ - Drop debian/patches/fix-gcc6-segfault.patch, obsolete.
+ - Refresh remaining patches.
+
+ -- Vincent Cheng <vcheng@debian.org> Sat, 12 Nov 2016 16:46:11 -0800
+
+0ad (0.0.20-3) unstable; urgency=medium
+
+ * Add debian/patches/fix-gcc6-segfault.patch to fix spidermonkey segfault
+ when built with gcc6. (Closes: #835176)
+ * Temporarily disable test suite on arm64 to fix arch-specific FTBFS.
+ * Call dh_strip --dbgsym-migration to cleanly migrate from the old -dbg
+ package to -dbgsym.
+
+ -- Vincent Cheng <vcheng@debian.org> Wed, 07 Sep 2016 22:44:22 -0700
+
+0ad (0.0.20-2) unstable; urgency=medium
+
+ * Team upload.
+ * Drop 0ad-dbg package and use the automatic -dbgsym package instead.
+ * d/rules: Remove override for dh_builddeb because xz is the default
+ compression.
+ * Declare compliance with Debian Policy 3.9.8.
+ * Build-depend on python and fix FTBFS.
+ Thanks to Lucas Nussbaum for the report. (Closes: #832870)
+ * Ensure that 0ad can be built twice in a row by updating dh_auto_clean
+ target. Thanks to Peter Green for the patch.
+
+ -- Markus Koschany <apo@debian.org> Sun, 21 Aug 2016 04:54:55 +0200
+
+0ad (0.0.20-1) unstable; urgency=medium
+
+ * New upstream release.
+ - Drop build-dep on libjpeg-dev.
+ - Refresh patches.
+ * Remove debian/menu file as per tech-ctte decision in #741573.
+ * Update Standards version to 3.9.7, no changes required.
+ * Update jQuery/JavaScript related lintian overrides.
+
+ -- Vincent Cheng <vcheng@debian.org> Sun, 03 Apr 2016 19:42:21 -0700
+
+0ad (0.0.19-1) unstable; urgency=medium
+
+ * New upstream release. (Closes: #807447)
+ - Add support for arm64. (Closes: #790306)
+ - Replace dependency on libsdl1.2-dev with libsdl2-dev (>= 2.0.2).
+ - Remove gcc-5.1.patch, applied upstream.
+ - Refresh remaining patches.
+ * Fix typo in override target in d/rules.
+ * Update watch file.
+
+ -- Vincent Cheng <vcheng@debian.org> Sun, 20 Dec 2015 19:56:20 -0800
+
+0ad (0.0.18-2) unstable; urgency=medium
+
+ [ Logan Rosen ]
+ * debian/patches/gcc-5.1.patch: Pull patch from upstream Git to fix build
+ with GCC 5.1.
+
+ -- Vincent Cheng <vcheng@debian.org> Fri, 28 Aug 2015 20:46:34 -0700
+
+0ad (0.0.18-1) unstable; urgency=medium
+
+ * New upstream release.
+ - Refresh patches.
+ - Replace build-dep on libmozjs-24-dev with libnspr4-dev.
+
+ -- Vincent Cheng <vcheng@debian.org> Wed, 18 Mar 2015 16:58:45 -0700
+
+0ad (0.0.17-1) unstable; urgency=medium
+
+ * New upstream release.
+ - Drop debian/patches/{support_miniupnpc_1.9.patch,fix_gcc4.9_ftbfs.patch}:
+ applied upstream.
+ - Add debian/patches/allow-build-with-root.patch.
+ - Add debian/patches/fix-bindir.patch. (LP: #1380737)
+ - Refresh remaining patches.
+ * Update Standards version to 3.9.6, no changes required.
+
+ -- Vincent Cheng <vcheng@debian.org> Mon, 13 Oct 2014 20:01:57 -0700
+
+0ad (0.0.16-4) unstable; urgency=medium
+
+ * Add debian/patches/fix_gcc4.9_ftbfs.patch to fix FTBFS when running the
+ test suite when built with gcc 4.9. (Closes: #746822)
+
+ -- Vincent Cheng <vcheng@debian.org> Tue, 24 Jun 2014 21:21:36 -0700
+
+0ad (0.0.16-3) unstable; urgency=medium
+
+ * Add debian/patches/support_miniupnpc_1.9.patch to fix FTBFS when built
+ with miniupnpc >= 1.9. (Closes: #751224)
+ * Tighten build-dep on libminiupnpc-dev (>= 1.6).
+
+ -- Vincent Cheng <vcheng@debian.org> Wed, 11 Jun 2014 19:46:46 -0700
+
+0ad (0.0.16-2) unstable; urgency=medium
+
+ * Remove armel from Architecture list in debian/control.
+
+ -- Vincent Cheng <vcheng@debian.org> Sun, 18 May 2014 19:14:10 -0700
+
+0ad (0.0.16-1) unstable; urgency=medium
+
+ * New upstream release.
+ - Drop debian/patches/fix-kfreebsd-ftbfs.patch; applied upstream.
+ * Avoid repacking tarball by including missing jquery sources in Debian diff,
+ see debian/missing-sources/jquery-1.8.{0,3}.js. Alternative way of fixing
+ #735349.
+ - Update debian/copyright to include missing license entries for the
+ various javascript libraries contained in the source tarball.
+ - Revert changes made to debian/control in 0ad/0.0.15+dfsg-3, i.e.:
+ revert: debian/control: Hardcode versions of 0ad-data{,-common} to
+ depend on to workaround the fact that 0.0.15+dfsg is strictly
+ greater than 0.0.15.
+ * Build-dep on libgloox-dev (>= 1.0.9) to ensure that 0ad is built with
+ newer enough gloox to avoid lobby connectivity issues.
+ * Add new build-deps: libicu-dev, libmozjs-24-dev.
+
+ -- Vincent Cheng <vcheng@debian.org> Sat, 17 May 2014 16:30:39 -0700
+
+0ad (0.0.15+dfsg-3) unstable; urgency=medium
+
+ * Repack tarball to remove minified javascript files without source in
+ source/tools/jsdebugger/. (Closes: #735349)
+ - debian/control: Hardcode versions of 0ad-data{,-common} to depend on to
+ workaround the fact that 0.0.15+dfsg is strictly greater than 0.0.15.
+ * Update debian/copyright to include text of IBM Common Public License.
+ * Update email address.
+
+ -- Vincent Cheng <vcheng@debian.org> Tue, 18 Feb 2014 23:21:11 -0800
+
+0ad (0.0.15-2) unstable; urgency=medium
+
+ * Add debian/patches/fix-kfreebsd-ftbfs.patch to fix FTBFS on kfreebsd.
+
+ -- Vincent Cheng <Vincentc1208@gmail.com> Fri, 27 Dec 2013 15:03:44 -0800
+
+0ad (0.0.15-1) unstable; urgency=low
+
+ * New upstream release.
+ - Remove fix-arm-ftbfs.patch, fixed upstream.
+ * Add build-dep on libgloox-dev and libminiupnpc-dev.
+ * Append '~' to libnvtt-dev build dependency to ease backporting.
+ * Prefer libwxgtk3.0-dev over libwxgtk2.8-dev as build-dep (keep latter as
+ alternate build-dep to ease backports).
+ * Update Standards version to 3.9.5, no changes required.
+
+ -- Vincent Cheng <Vincentc1208@gmail.com> Wed, 25 Dec 2013 17:41:29 -0800
+
+0ad (0.0.14-3) unstable; urgency=low
+
+ * Add debian/patches/fix-arm-ftbfs.patch to fix FTBFS on armel and armhf.
+
+ -- Vincent Cheng <Vincentc1208@gmail.com> Tue, 17 Sep 2013 01:08:10 -0700
+
+0ad (0.0.14-2) unstable; urgency=low
+
+ * Tighten build dependency on libnvtt-dev to >= 2.0.8-1+dfsg-4 to ensure
+ that amd64 builds of 0ad do not FTBFS on certain hardware. See #713966
+ for details.
+ * Build 0ad for armel and armhf.
+
+ -- Vincent Cheng <Vincentc1208@gmail.com> Fri, 06 Sep 2013 23:00:24 -0700
+
+0ad (0.0.14-1) unstable; urgency=low
+
+ * New upstream release.
+ - Remove fix-bindir.patch, boost-libnames.patch; applied/fixed upstream.
+ - Refresh remaining patches.
+
+ -- Vincent Cheng <Vincentc1208@gmail.com> Thu, 05 Sep 2013 20:02:20 -0700
+
+0ad (0.0.13-2) unstable; urgency=low
+
+ * Add boost-libnames.patch to fix FTBFS with boost 1.53. (Closes: #709570)
+
+ -- Vincent Cheng <Vincentc1208@gmail.com> Fri, 24 May 2013 01:47:14 -0700
+
+0ad (0.0.13-1) unstable; urgency=low
+
+ * New upstream release.
+ - Refresh patches.
+ * Add icon to Debian menu entry. (Closes: #703254)
+
+ -- Vincent Cheng <Vincentc1208@gmail.com> Tue, 02 Apr 2013 19:40:54 -0700
+
+0ad (0.0.12-1) unstable; urgency=low
+
+ [ Vincent Cheng ]
+ * New upstream release.
+ - Remove debian/patches/fix-kfreebsd-ftbfs.patch; applied upstream.
+ - Refresh remaining patches.
+ * Add depends on 0ad-data-common.
+ * Add lintian overrides for:
+ - outdated-autotools-helper-file (caused by config.{guess,sub} in a
+ convenience copy of libenet, which we don't use).
+ - version-substvar-for-external-package (not a problem since 0ad and
+ 0ad-data uploaded at same time, and no other mechanism provided by
+ dpkg-gencontrol for packages that want to depend on versioned packages
+ built from a separate source package).
+ * Update Standards version to 3.9.4, no changes required.
+ * Enable verbose build.
+
+ [ Ansgar Burchardt ]
+ * debian/control: Remove DM-Upload-Allowed.
+
+ -- Vincent Cheng <Vincentc1208@gmail.com> Tue, 12 Feb 2013 19:24:59 -0800
+
+0ad (0.0.11-1) unstable; urgency=low
+
+ * New upstream release. (Alpha 11 Kronos)
+ - Upstream disabled FAM support as of svn r12550, so remove build-depends
+ on libgamin-dev, and remove depends on gamin | fam. (Closes: #679087)
+ - Refresh existing patches.
+ * Add fallback to launcher script when /usr/games is not in $PATH.
+ (Closes: #679033)
+ * Restrict list of architectures to i386, amd64, and kfreebsd-{i386,amd64}.
+ (Closes: #683282)
+ * Update debian/watch file.
+ * Add DMUA field in debian/control with agreement of sponsor.
+
+ -- Vincent Cheng <Vincentc1208@gmail.com> Sat, 08 Sep 2012 02:43:25 -0700
+
+0ad (0~r11863-2) unstable; urgency=low
+
+ * Add stricter dependency on 0ad-data. (Closes: #673526)
+
+ -- Vincent Cheng <Vincentc1208@gmail.com> Sun, 20 May 2012 01:48:59 -0700
+
+0ad (0~r11863-1) unstable; urgency=low
+
+ * New upstream release.
+ - Refresh patches.
+ * Add debian/patches/enable-hardening-relro.patch to build using the
+ "read-only relocation" link flag, as suggested by lintian.
+ * Add build-depends on libxcursor-dev.
+
+ -- Vincent Cheng <Vincentc1208@gmail.com> Wed, 16 May 2012 23:50:12 -0700
+
+0ad (0~r11339-2) unstable; urgency=low
+
+ * Add debian/patches/fix-kfreebsd-ftbfs.patch to fix FTBFS on kfreebsd.
+ * Update debian/0ad.6.
+ * Add build-depends on dpkg-dev (>= 1.15.5) for xz compression support.
+ * Change build-depends on libjpeg8-dev to libjpeg-dev.
+ * Change build-depends on libpng12-dev to libpng-dev. (Closes: #668453)
+ * Add alternate build-depends on libcurl4-dev.
+ * Add alternate dependency on fam. (Closes: #668353)
+ * Override dh_auto_clean to clean source properly. (Closes: #668686)
+
+ -- Vincent Cheng <Vincentc1208@gmail.com> Thu, 26 Apr 2012 19:12:26 -0700
+
+0ad (0~r11339-1) unstable; urgency=low
+
+ * Initial release (Closes: #594800)
+
+ -- Vincent Cheng <Vincentc1208@gmail.com> Thu, 29 Mar 2012 16:13:33 -0700
--- /dev/null
+Source: 0ad
+Section: games
+Priority: optional
+Maintainer: Debian Games Team <pkg-games-devel@lists.alioth.debian.org>
+Uploaders:
+ Vincent Cheng <vcheng@debian.org>,
+ Ludovic Rousseau <rousseau@debian.org>
+Build-Depends:
+ autoconf,
+ debhelper-compat (= 12),
+ dpkg-dev (>= 1.15.5),
+ libboost-dev,
+ libboost-filesystem-dev,
+ libcurl4-gnutls-dev | libcurl4-dev,
+ libenet-dev (>= 1.3),
+ libgloox-dev (>= 1.0.10),
+ libicu-dev,
+ libminiupnpc-dev (>= 1.6),
+ libnspr4-dev,
+ libnvtt-dev (>= 2.0.8-1+dfsg-4~),
+ libogg-dev,
+ libopenal-dev,
+ libpng-dev,
+ libsdl2-dev (>= 2.0.2),
+ libsodium-dev (>= 1.0.14),
+ libvorbis-dev,
+ libwxgtk3.0-gtk3-dev,
+ libxcursor-dev,
+ libxml2-dev,
+ pkg-config,
+ python2,
+ python3,
+ zlib1g-dev
+Standards-Version: 4.5.0
+Homepage: http://play0ad.com/
+Vcs-Git: https://salsa.debian.org/games-team/0ad.git
+Vcs-Browser: https://salsa.debian.org/games-team/0ad
+Rules-Requires-Root: no
+
+Package: 0ad
+Architecture: amd64 arm64 armhf i386 kfreebsd-amd64 kfreebsd-i386
+Pre-Depends: dpkg (>= 1.15.6~)
+Depends:
+ 0ad-data (>= ${source:Upstream-Version}),
+ 0ad-data (<= ${source:Version}),
+ 0ad-data-common (>= ${source:Upstream-Version}),
+ 0ad-data-common (<= ${source:Version}),
+ ${misc:Depends},
+ ${shlibs:Depends}
+Description: Real-time strategy game of ancient warfare
+ 0 A.D. (pronounced "zero ey-dee") is a free, open-source, cross-platform
+ real-time strategy (RTS) game of ancient warfare. In short, it is a
+ historically-based war/economy game that allows players to relive or rewrite
+ the history of Western civilizations, focusing on the years between 500 B.C.
+ and 500 A.D. The project is highly ambitious, involving state-of-the-art 3D
+ graphics, detailed artwork, sound, and a flexible and powerful custom-built
+ game engine.
--- /dev/null
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: 0ad
+Source: http://releases.wildfiregames.com/
+
+Files: *
+Copyright: 2010 Wildfire Games
+License: GPL-2.0+
+
+Files: debian/*
+Copyright: 2010-2012 Rico Tzschichholz <ricotz@ubuntu.com>
+ 2012-2014 Vincent Cheng <vcheng@debian.org>
+License: GPL-2.0+
+
+Files: debian/0ad.6
+Copyright: 2010 Fabio Pedretti <fabio.ped@libero.it>
+License: GPL-2.0+
+
+Files: source/lib/*
+Copyright: 2010 Wildfire Games
+License: Expat
+
+Files: source/ps/Preprocessor.h
+ source/ps/Preprocessor.cpp
+Copyright: 2000-2009 Torus Knot Software Ltd
+License: Expat
+
+Files: source/third_party/*
+Copyright: 2004-2011 Sergey Lyubka
+License: Expat
+
+Files: source/tools/fontbuilder2/Packer.py
+Copyright: 2002-2009 Nuclex Development Labs
+License: CPL
+
+Files: source/tools/templatesanalyzer/tablefilter/*
+Copyright: 2014-2016 Max Guglielmi
+License: Expat
+
+Files: build/premake/*
+Copyright: 2002-2009 Jason Perkins and the Premake project
+License: BSD-3-clause
+
+Files: libraries/source/cxxtest-4.4/*
+Copyright: 2004 Erez Volk
+ 2004 Jared Grubb
+ 2004 Kevin Fitch
+License: LGPL-2.1+
+
+Files: libraries/source/fcollada/*
+Copyright: 2005-2007 Feeling Software Inc.
+ 2005-2007 Sony Computer Entertainment America
+License: Expat
+
+Files: libraries/source/fcollada/include/FCDocument/*
+ libraries/source/fcollada/include/FUtils/*
+ libraries/source/fcollada/src/FCollada/FCDocument/*
+ libraries/source/fcollada/src/FCollada/FUtils/*
+ libraries/source/fcollada/src/FColladaPlugins/FArchiveXML/*
+Copyright: 2005-2006 Autodesk Media Entertainment
+ 2005-2007 Feeling Software Inc.
+ 2005-2007 Sony Computer Entertainment America
+License: Expat
+
+Files: libraries/source/nvtt/*
+Copyright: 2007 NVIDIA Corporation
+License: Expat
+
+Files: libraries/source/nvtt/src/src/nvtt/squish/*
+Copyright: 2006 Simon Brown <si@sjbrown.co.uk>
+License: Expat
+
+Files: libraries/source/nvtt/src/src/nvcore/poshlib/posh.c
+ libraries/source/nvtt/src/src/nvcore/poshlib/posh.h
+Copyright: 2004 Brian Hook
+License: BSD-3-clause
+
+Files: libraries/source/nvtt/src/src/nvcore/BitArray.h
+ libraries/source/nvtt/src/src/nvcore/Containers.h
+ libraries/source/nvtt/src/src/nvcore/Debug.cpp
+ libraries/source/nvtt/src/src/nvcore/Debug.h
+ libraries/source/nvtt/src/src/nvcore/Library.h
+ libraries/source/nvtt/src/src/nvcore/Memory.cpp
+ libraries/source/nvtt/src/src/nvcore/Memory.h
+ libraries/source/nvtt/src/src/nvcore/Prefetch.h
+ libraries/source/nvtt/src/src/nvcore/Ptr.h
+ libraries/source/nvtt/src/src/nvcore/StrLib.cpp
+ libraries/source/nvtt/src/src/nvcore/StrLib.h
+ libraries/source/nvtt/src/src/nvcore/Stream.h
+ libraries/source/nvtt/src/src/nvcore/TextReader.cpp
+ libraries/source/nvtt/src/src/nvcore/TextReader.h
+ libraries/source/nvtt/src/src/nvcore/TextWriter.cpp
+ libraries/source/nvtt/src/src/nvcore/TextWriter.h
+ libraries/source/nvtt/src/src/nvcore/Tokenizer.cpp
+ libraries/source/nvtt/src/src/nvcore/Tokenizer.h
+ libraries/source/nvtt/src/src/nvcore/nvcore.h
+ libraries/source/nvtt/src/src/nvimage/ColorBlock.cpp
+ libraries/source/nvtt/src/src/nvimage/ColorBlock.h
+ libraries/source/nvtt/src/src/nvimage/Filter.cpp
+ libraries/source/nvtt/src/src/nvimage/Filter.h
+ libraries/source/nvtt/src/src/nvimage/FloatImage.cpp
+ libraries/source/nvtt/src/src/nvimage/FloatImage.h
+ libraries/source/nvtt/src/src/nvimage/HoleFilling.cpp
+ libraries/source/nvtt/src/src/nvimage/HoleFilling.h
+ libraries/source/nvtt/src/src/nvimage/Image.cpp
+ libraries/source/nvtt/src/src/nvimage/Image.h
+ libraries/source/nvtt/src/src/nvimage/ImageIO.cpp
+ libraries/source/nvtt/src/src/nvimage/ImageIO.h
+ libraries/source/nvtt/src/src/nvimage/NormalMipmap.cpp
+ libraries/source/nvtt/src/src/nvimage/NormalMipmap.h
+ libraries/source/nvtt/src/src/nvimage/PsdFile.h
+ libraries/source/nvtt/src/src/nvimage/Quantize.cpp
+ libraries/source/nvtt/src/src/nvimage/Quantize.h
+ libraries/source/nvtt/src/src/nvimage/TgaFile.h
+ libraries/source/nvtt/src/src/nvimage/nvimage.h
+ libraries/source/nvtt/src/src/nvmath/Basis.cpp
+ libraries/source/nvtt/src/src/nvmath/Basis.h
+ libraries/source/nvtt/src/src/nvmath/Box.h
+ libraries/source/nvtt/src/src/nvmath/Color.h
+ libraries/source/nvtt/src/src/nvmath/Matrix.h
+ libraries/source/nvtt/src/src/nvmath/Montecarlo.cpp
+ libraries/source/nvtt/src/src/nvmath/Montecarlo.h
+ libraries/source/nvtt/src/src/nvmath/Plane.cpp
+ libraries/source/nvtt/src/src/nvmath/Plane.h
+ libraries/source/nvtt/src/src/nvmath/Quaternion.h
+ libraries/source/nvtt/src/src/nvmath/Random.cpp
+ libraries/source/nvtt/src/src/nvmath/Random.h
+ libraries/source/nvtt/src/src/nvmath/SphericalHarmonic.cpp
+ libraries/source/nvtt/src/src/nvmath/SphericalHarmonic.h
+ libraries/source/nvtt/src/src/nvmath/Triangle.cpp
+ libraries/source/nvtt/src/src/nvmath/Triangle.h
+ libraries/source/nvtt/src/src/nvmath/Vector.h
+ libraries/source/nvtt/src/src/nvmath/nvmath.h
+Copyright: Ignacio Castano <castanyo@yahoo.es>
+License: public-domain
+ The license is not specified beyond public-domain.
+ The line from the source code is the following:
+ "This code is in the public domain -- castanyo@yahoo.es"
+
+Files: libraries/source/spidermonkey/*
+Copyright: 1998-2011 Mozilla Corporation
+License: MPL-1.1 and GPL-2.0 and LGPL-2.1
+
+Files: libraries/source/valgrind/*
+Copyright: 2003-2009 Josef Weidendorfer
+ 2000-2009 Julian Seward
+License: Expat
+
+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:
+ * 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 <organization> 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 <COPYRIGHT HOLDER> 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.
+
+License: CPL
+ THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON PUBLIC
+ LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM
+ CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+ .
+ 1. DEFINITIONS
+ .
+ "Contribution" means:
+ .
+ a) in the case of the initial Contributor, the initial code and
+ documentation distributed under this Agreement, and
+ .
+ b) in the case of each subsequent Contributor:
+ .
+ i) changes to the Program, and
+ .
+ ii) additions to the Program;
+ .
+ where such changes and/or additions to the Program originate from and are
+ distributed by that particular Contributor. A Contribution 'originates' from a
+ Contributor if it was added to the Program by such Contributor itself or anyone
+ acting on such Contributor's behalf. Contributions do not include additions to
+ the Program which: (i) are separate modules of software distributed in
+ conjunction with the Program under their own license agreement, and (ii) are not
+ derivative works of the Program.
+ .
+ "Contributor" means any person or entity that distributes the Program.
+ .
+ "Licensed Patents " mean patent claims licensable by a Contributor which are
+ necessarily infringed by the use or sale of its Contribution alone or when
+ combined with the Program.
+ .
+ "Program" means the Contributions distributed in accordance with this Agreement.
+ .
+ "Recipient" means anyone who receives the Program under this Agreement,
+ including all Contributors.
+ .
+ 2. GRANT OF RIGHTS
+ .
+ a) Subject to the terms of this Agreement, each Contributor hereby grants
+ Recipient a non-exclusive, worldwide, royalty-free copyright license to
+ reproduce, prepare derivative works of, publicly display, publicly perform,
+ distribute and sublicense the Contribution of such Contributor, if any, and such
+ derivative works, in source code and object code form.
+ .
+ b) Subject to the terms of this Agreement, each Contributor hereby grants
+ Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed
+ Patents to make, use, sell, offer to sell, import and otherwise transfer the
+ Contribution of such Contributor, if any, in source code and object code form.
+ This patent license shall apply to the combination of the Contribution and the
+ Program if, at the time the Contribution is added by the Contributor, such
+ addition of the Contribution causes such combination to be covered by the
+ Licensed Patents. The patent license shall not apply to any other combinations
+ which include the Contribution. No hardware per se is licensed hereunder.
+ .
+ c) Recipient understands that although each Contributor grants the licenses
+ to its Contributions set forth herein, no assurances are provided by any
+ Contributor that the Program does not infringe the patent or other intellectual
+ property rights of any other entity. Each Contributor disclaims any liability to
+ Recipient for claims brought by any other entity based on infringement of
+ intellectual property rights or otherwise. As a condition to exercising the
+ rights and licenses granted hereunder, each Recipient hereby assumes sole
+ responsibility to secure any other intellectual property rights needed, if any.
+ For example, if a third party patent license is required to allow Recipient to
+ distribute the Program, it is Recipient's responsibility to acquire that license
+ before distributing the Program.
+ .
+ d) Each Contributor represents that to its knowledge it has sufficient
+ copyright rights in its Contribution, if any, to grant the copyright license set
+ forth in this Agreement.
+ .
+ 3. REQUIREMENTS
+ .
+ A Contributor may choose to distribute the Program in object code form under its
+ own license agreement, provided that:
+ .
+ a) it complies with the terms and conditions of this Agreement; and
+ .
+ b) its license agreement:
+ .
+ i) effectively disclaims on behalf of all Contributors all warranties and
+ conditions, express and implied, including warranties or conditions of title and
+ non-infringement, and implied warranties or conditions of merchantability and
+ fitness for a particular purpose;
+ .
+ ii) effectively excludes on behalf of all Contributors all liability for
+ damages, including direct, indirect, special, incidental and consequential
+ damages, such as lost profits;
+ .
+ iii) states that any provisions which differ from this Agreement are offered
+ by that Contributor alone and not by any other party; and
+ .
+ iv) states that source code for the Program is available from such
+ Contributor, and informs licensees how to obtain it in a reasonable manner on or
+ through a medium customarily used for software exchange.
+ .
+ When the Program is made available in source code form:
+ .
+ a) it must be made available under this Agreement; and
+ .
+ b) a copy of this Agreement must be included with each copy of the Program.
+ .
+ Contributors may not remove or alter any copyright notices contained within the
+ Program.
+ .
+ Each Contributor must identify itself as the originator of its Contribution, if
+ any, in a manner that reasonably allows subsequent Recipients to identify the
+ originator of the Contribution.
+ .
+ 4. COMMERCIAL DISTRIBUTION
+ .
+ Commercial distributors of software may accept certain responsibilities with
+ respect to end users, business partners and the like. While this license is
+ intended to facilitate the commercial use of the Program, the Contributor who
+ includes the Program in a commercial product offering should do so in a manner
+ which does not create potential liability for other Contributors. Therefore, if
+ a Contributor includes the Program in a commercial product offering, such
+ Contributor ("Commercial Contributor") hereby agrees to defend and indemnify
+ every other Contributor ("Indemnified Contributor") against any losses, damages
+ and costs (collectively "Losses") arising from claims, lawsuits and other legal
+ actions brought by a third party against the Indemnified Contributor to the
+ extent caused by the acts or omissions of such Commercial Contributor in
+ connection with its distribution of the Program in a commercial product
+ offering. The obligations in this section do not apply to any claims or Losses
+ relating to any actual or alleged intellectual property infringement. In order
+ to qualify, an Indemnified Contributor must: a) promptly notify the Commercial
+ Contributor in writing of such claim, and b) allow the Commercial Contributor to
+ control, and cooperate with the Commercial Contributor in, the defense and any
+ related settlement negotiations. The Indemnified Contributor may participate in
+ any such claim at its own expense.
+ .
+ For example, a Contributor might include the Program in a commercial product
+ offering, Product X. That Contributor is then a Commercial Contributor. If that
+ Commercial Contributor then makes performance claims, or offers warranties
+ related to Product X, those performance claims and warranties are such
+ Commercial Contributor's responsibility alone. Under this section, the
+ Commercial Contributor would have to defend claims against the other
+ Contributors related to those performance claims and warranties, and if a court
+ requires any other Contributor to pay any damages as a result, the Commercial
+ Contributor must pay those damages.
+ .
+ 5. NO WARRANTY
+ .
+ EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
+ IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE,
+ NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each
+ Recipient is solely responsible for determining the appropriateness of using and
+ distributing the Program and assumes all risks associated with its exercise of
+ rights under this Agreement, including but not limited to the risks and costs of
+ program errors, compliance with applicable laws, damage to or loss of data,
+ programs or equipment, and unavailability or interruption of operations.
+ .
+ 6. DISCLAIMER OF LIABILITY
+ .
+ EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY
+ CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST
+ PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS
+ GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ .
+ 7. GENERAL
+ .
+ If any provision of this Agreement is invalid or unenforceable under applicable
+ law, it shall not affect the validity or enforceability of the remainder of the
+ terms of this Agreement, and without further action by the parties hereto, such
+ provision shall be reformed to the minimum extent necessary to make such
+ provision valid and enforceable.
+ .
+ If Recipient institutes patent litigation against a Contributor with respect to
+ a patent applicable to software (including a cross-claim or counterclaim in a
+ lawsuit), then any patent licenses granted by that Contributor to such Recipient
+ under this Agreement shall terminate as of the date such litigation is filed. In
+ addition, if Recipient institutes patent litigation against any entity
+ (including a cross-claim or counterclaim in a lawsuit) alleging that the Program
+ itself (excluding combinations of the Program with other software or hardware)
+ infringes such Recipient's patent(s), then such Recipient's rights granted under
+ Section 2(b) shall terminate as of the date such litigation is filed.
+ .
+ All Recipient's rights under this Agreement shall terminate if it fails to
+ comply with any of the material terms or conditions of this Agreement and does
+ not cure such failure in a reasonable period of time after becoming aware of
+ such noncompliance. If all Recipient's rights under this Agreement terminate,
+ Recipient agrees to cease use and distribution of the Program as soon as
+ reasonably practicable. However, Recipient's obligations under this Agreement
+ and any licenses granted by Recipient relating to the Program shall continue and
+ survive.
+ .
+ Everyone is permitted to copy and distribute copies of this Agreement, but in
+ order to avoid inconsistency the Agreement is copyrighted and may only be
+ modified in the following manner. The Agreement Steward reserves the right to
+ publish new versions (including revisions) of this Agreement from time to time.
+ No one other than the Agreement Steward has the right to modify this Agreement.
+ IBM is the initial Agreement Steward. IBM may assign the responsibility to serve
+ as the Agreement Steward to a suitable separate entity. Each new version of the
+ Agreement will be given a distinguishing version number. The Program (including
+ Contributions) may always be distributed subject to the version of the Agreement
+ under which it was received. In addition, after a new version of the Agreement
+ is published, Contributor may elect to distribute the Program (including its
+ Contributions) under the new version. Except as expressly stated in Sections
+ 2(a) and 2(b) above, Recipient receives no rights or licenses to the
+ intellectual property of any Contributor under this Agreement, whether
+ expressly, by implication, estoppel or otherwise. All rights in the Program not
+ expressly granted under this Agreement are reserved.
+ .
+ This Agreement is governed by the laws of the State of New York and the
+ intellectual property laws of the United States of America. No party to this
+ Agreement will bring a legal action under this Agreement more than one year
+ after the cause of action arose. Each party waives its rights to a jury trial in
+ any resulting litigation.
+
+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
+
+License: GPL-2.0
+ This package is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2 of
+ the License, as published by the Free Software Foundation.
+ .
+ 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: GPL-2.0+
+ This package 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 of the License, 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 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/
+ 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 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/
+ LGPL-2.1".
+
+License: MPL-1.1
+ Mozilla Public License Version 1.1
+ .
+ 1. Definitions.
+ .
+ 1.0.1. "Commercial Use"
+ means distribution or otherwise making the Covered Code available
+ to a third party.
+ 1.1. "Contributor"
+ means each entity that creates or contributes to the creation of
+ Modifications.
+ 1.2. "Contributor Version"
+ means the combination of the Original Code, prior Modifications
+ used by a Contributor, and the Modifications made by that
+ particular Contributor.
+ 1.3. "Covered Code"
+ means the Original Code or Modifications or the combination of
+ the Original Code and Modifications, in each case including
+ portions thereof.
+ 1.4. "Electronic Distribution Mechanism"
+ means a mechanism generally accepted in the software development
+ community for the electronic transfer of data.
+ 1.5. "Executable"
+ means Covered Code in any form other than Source Code.
+ 1.6. "Initial Developer"
+ means the individual or entity identified as the Initial
+ Developer in the Source Code notice required by Exhibit A.
+ 1.7. "Larger Work"
+ means a work which combines Covered Code or portions thereof with
+ code not governed by the terms of this License.
+ 1.8. "License"
+ means this document.
+ 1.8.1. "Licensable"
+ means having the right to grant, to the maximum extent possible,
+ whether at the time of the initial grant or subsequently
+ acquired, any and all of the rights conveyed herein.
+ 1.9. "Modifications"
+ means any addition to or deletion from the substance or structure
+ of either the Original Code or any previous Modifications. When
+ Covered Code is released as a series of files, a Modification is:
+ a. Any addition to or deletion from the contents of a file
+ containing Original Code or previous Modifications.
+ b. Any new file that contains any part of the Original Code or
+ previous Modifications.
+ 1.10. "Original Code"
+ means Source Code of computer software code which is described in
+ the Source Code notice required by Exhibit A as Original Code,
+ and which, at the time of its release under this License is not
+ already Covered Code governed by this License.
+ 1.10.1. "Patent Claims"
+ means any patent claim(s), now owned or hereafter acquired,
+ including without limitation, method, process, and apparatus
+ claims, in any patent Licensable by grantor.
+ 1.11. "Source Code"
+ means the preferred form of the Covered Code for making
+ modifications to it, including all modules it contains, plus any
+ associated interface definition files, scripts used to control
+ compilation and installation of an Executable, or source code
+ differential comparisons against either the Original Code or
+ another well known, available Covered Code of the Contributor's
+ choice. The Source Code can be in a compressed or archival form,
+ provided the appropriate decompression or de-archiving software
+ is widely available for no charge.
+ 1.12. "You" (or "Your")
+ means an individual or a legal entity exercising rights under,
+ and complying with all of the terms of, this License or a future
+ version of this License issued under Section 6.1. For legal
+ entities, "You" includes any entity which controls, is controlled
+ by, or is under common control with You. For purposes of this
+ definition, "control" means (a) the power, direct or indirect, to
+ cause the direction or management of such entity, whether by
+ contract or otherwise, or (b) ownership of more than fifty
+ percent (50%) of the outstanding shares or beneficial ownership
+ of such entity.
+ .
+ 2. Source Code License.
+ .
+ 2.1. The Initial Developer Grant.
+ .
+ The Initial Developer hereby grants You a world-wide, royalty-free,
+ non-exclusive license, subject to third party intellectual property
+ claims:
+ .
+ a. under intellectual property rights (other than patent or
+ trademark) Licensable by Initial Developer to use, reproduce,
+ modify, display, perform, sublicense and distribute the Original
+ Code (or portions thereof) with or without Modifications, and/or
+ as part of a Larger Work; and
+ b. under Patents Claims infringed by the making, using or selling of
+ Original Code, to make, have made, use, practice, sell, and offer
+ for sale, and/or otherwise dispose of the Original Code (or
+ portions thereof).
+ c. the licenses granted in this Section 2.1 (a) and (b) are
+ effective on the date Initial Developer first distributes
+ Original Code under the terms of this License.
+ d. Notwithstanding Section 2.1 (b) above, no patent license is
+ granted: 1) for code that You delete from the Original Code; 2)
+ separate from the Original Code; or 3) for infringements caused
+ by: i) the modification of the Original Code or ii) the
+ combination of the Original Code with other software or devices.
+ .
+ 2.2. Contributor Grant.
+ .
+ Subject to third party intellectual property claims, each Contributor
+ hereby grants You a world-wide, royalty-free, non-exclusive license
+ .
+ a. under intellectual property rights (other than patent or
+ trademark) Licensable by Contributor, to use, reproduce, modify,
+ display, perform, sublicense and distribute the Modifications
+ created by such Contributor (or portions thereof) either on an
+ unmodified basis, with other Modifications, as Covered Code and/
+ or as part of a Larger Work; and
+ b. under Patent Claims infringed by the making, using, or selling of
+ Modifications made by that Contributor either alone and/or in
+ combination with its Contributor Version (or portions of such
+ combination), to make, use, sell, offer for sale, have made, and/
+ or otherwise dispose of: 1) Modifications made by that
+ Contributor (or portions thereof); and 2) the combination of
+ Modifications made by that Contributor with its Contributor
+ Version (or portions of such combination).
+ c. the licenses granted in Sections 2.2 (a) and 2.2 (b) are
+ effective on the date Contributor first makes Commercial Use of
+ the Covered Code.
+ d. Notwithstanding Section 2.2 (b) above, no patent license is
+ granted: 1) for any code that Contributor has deleted from the
+ Contributor Version; 2) separate from the Contributor Version; 3)
+ for infringements caused by: i) third party modifications of
+ Contributor Version or ii) the combination of Modifications made
+ by that Contributor with other software (except as part of the
+ Contributor Version) or other devices; or 4) under Patent Claims
+ infringed by Covered Code in the absence of Modifications made by
+ that Contributor.
+ .
+ 3. Distribution Obligations.
+ .
+ 3.1. Application of License.
+ .
+ The Modifications which You create or to which You contribute are
+ governed by the terms of this License, including without limitation
+ Section 2.2. The Source Code version of Covered Code may be
+ distributed only under the terms of this License or a future version
+ of this License released under Section 6.1, and You must include a
+ copy of this License with every copy of the Source Code You
+ distribute. You may not offer or impose any terms on any Source Code
+ version that alters or restricts the applicable version of this
+ License or the recipients' rights hereunder. However, You may include
+ an additional document offering the additional rights described in
+ Section 3.5.
+ .
+ 3.2. Availability of Source Code.
+ .
+ Any Modification which You create or to which You contribute must be
+ made available in Source Code form under the terms of this License
+ either on the same media as an Executable version or via an accepted
+ Electronic Distribution Mechanism to anyone to whom you made an
+ Executable version available; and if made available via Electronic
+ Distribution Mechanism, must remain available for at least twelve
+ (12) months after the date it initially became available, or at least
+ six (6) months after a subsequent version of that particular
+ Modification has been made available to such recipients. You are
+ responsible for ensuring that the Source Code version remains
+ available even if the Electronic Distribution Mechanism is maintained
+ by a third party.
+ .
+ 3.3. Description of Modifications.
+ .
+ You must cause all Covered Code to which You contribute to contain a
+ file documenting the changes You made to create that Covered Code and
+ the date of any change. You must include a prominent statement that
+ the Modification is derived, directly or indirectly, from Original
+ Code provided by the Initial Developer and including the name of the
+ Initial Developer in (a) the Source Code, and (b) in any notice in an
+ Executable version or related documentation in which You describe the
+ origin or ownership of the Covered Code.
+ .
+ 3.4. Intellectual Property Matters
+ .
+ (a) Third Party Claims
+ .
+ If Contributor has knowledge that a license under a third party's
+ intellectual property rights is required to exercise the rights
+ granted by such Contributor under Sections 2.1 or 2.2, Contributor
+ must include a text file with the Source Code distribution titled
+ "LEGAL" which describes the claim and the party making the claim in
+ sufficient detail that a recipient will know whom to contact. If
+ Contributor obtains such knowledge after the Modification is made
+ available as described in Section 3.2, Contributor shall promptly
+ modify the LEGAL file in all copies Contributor makes available
+ thereafter and shall take other steps (such as notifying appropriate
+ mailing lists or newsgroups) reasonably calculated to inform those
+ who received the Covered Code that new knowledge has been obtained.
+ .
+ (b) Contributor APIs
+ .
+ If Contributor's Modifications include an application programming
+ interface and Contributor has knowledge of patent licenses which are
+ reasonably necessary to implement that API, Contributor must also
+ include this information in the legal file.
+ .
+ (c) Representations.
+ .
+ Contributor represents that, except as disclosed pursuant to Section
+ 3.4 (a) above, Contributor believes that Contributor's Modifications
+ are Contributor's original creation(s) and/or Contributor has
+ sufficient rights to grant the rights conveyed by this License.
+ .
+ 3.5. Required Notices.
+ .
+ You must duplicate the notice in Exhibit A in each file of the Source
+ Code. If it is not possible to put such notice in a particular Source
+ Code file due to its structure, then You must include such notice in
+ a location (such as a relevant directory) where a user would be
+ likely to look for such a notice. If You created one or more
+ Modification(s) You may add your name as a Contributor to the notice
+ described in Exhibit A. You must also duplicate this License in any
+ documentation for the Source Code where You describe recipients'
+ rights or ownership rights relating to Covered Code. You may choose
+ to offer, and to charge a fee for, warranty, support, indemnity or
+ liability obligations to one or more recipients of Covered Code.
+ However, You may do so only on Your own behalf, and not on behalf of
+ the Initial Developer or any Contributor. You must make it absolutely
+ clear than any such warranty, support, indemnity or liability
+ obligation is offered by You alone, and You hereby agree to indemnify
+ the Initial Developer and every Contributor for any liability
+ incurred by the Initial Developer or such Contributor as a result of
+ warranty, support, indemnity or liability terms You offer.
+ .
+ 3.6. Distribution of Executable Versions.
+ .
+ You may distribute Covered Code in Executable form only if the
+ requirements of Sections 3.1, 3.2, 3.3, 3.4 and 3.5 have been met for
+ that Covered Code, and if You include a notice stating that the
+ Source Code version of the Covered Code is available under the terms
+ of this License, including a description of how and where You have
+ fulfilled the obligations of Section 3.2. The notice must be
+ conspicuously included in any notice in an Executable version,
+ related documentation or collateral in which You describe recipients'
+ rights relating to the Covered Code. You may distribute the
+ Executable version of Covered Code or ownership rights under a
+ license of Your choice, which may contain terms different from this
+ License, provided that You are in compliance with the terms of this
+ License and that the license for the Executable version does not
+ attempt to limit or alter the recipient's rights in the Source Code
+ version from the rights set forth in this License. If You distribute
+ the Executable version under a different license You must make it
+ absolutely clear that any terms which differ from this License are
+ offered by You alone, not by the Initial Developer or any
+ Contributor. You hereby agree to indemnify the Initial Developer and
+ every Contributor for any liability incurred by the Initial Developer
+ or such Contributor as a result of any such terms You offer.
+ .
+ 3.7. Larger Works.
+ .
+ You may create a Larger Work by combining Covered Code with other
+ code not governed by the terms of this License and distribute the
+ Larger Work as a single product. In such a case, You must make sure
+ the requirements of this License are fulfilled for the Covered Code.
+ .
+ 4. Inability to Comply Due to Statute or Regulation.
+ .
+ If it is impossible for You to comply with any of the terms of this
+ License with respect to some or all of the Covered Code due to
+ statute, judicial order, or regulation then You must: (a) comply with
+ the terms of this License to the maximum extent possible; and (b)
+ describe the limitations and the code they affect. Such description
+ must be included in the legal file described in Section 3.4 and must
+ be included with all distributions of the Source Code. Except to the
+ extent prohibited by statute or regulation, such description must be
+ sufficiently detailed for a recipient of ordinary skill to be able to
+ understand it.
+ .
+ 5. Application of this License.
+ .
+ This License applies to code to which the Initial Developer has
+ attached the notice in Exhibit A and to related Covered Code.
+ .
+ 6. Versions of the License.
+ .
+ 6.1. New Versions
+ .
+ Netscape Communications Corporation ("Netscape") may publish revised
+ and/or new versions of the License from time to time. Each version
+ will be given a distinguishing version number.
+ .
+ 6.2. Effect of New Versions
+ .
+ Once Covered Code has been published under a particular version of
+ the License, You may always continue to use it under the terms of
+ that version. You may also choose to use such Covered Code under the
+ terms of any subsequent version of the License published by Netscape.
+ No one other than Netscape has the right to modify the terms
+ applicable to Covered Code created under this License.
+ .
+ 6.3. Derivative Works
+ .
+ If You create or use a modified version of this License (which you
+ may only do in order to apply it to code which is not already Covered
+ Code governed by this License), You must (a) rename Your license so
+ that the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", "MPL",
+ "NPL" or any confusingly similar phrase do not appear in your license
+ (except to note that your license differs from this License) and (b)
+ otherwise make it clear that Your version of the license contains
+ terms which differ from the Mozilla Public License and Netscape
+ Public License. (Filling in the name of the Initial Developer,
+ Original Code or Contributor in the notice described in Exhibit A
+ shall not of themselves be deemed to be modifications of this
+ License.)
+ .
+ 7. Disclaimer of warranty
+ .
+ Covered code is provided under this license on an "as is" basis,
+ without warranty of any kind, either expressed or implied, including,
+ without limitation, warranties that the covered code is free of
+ defects, merchantable, fit for a particular purpose or
+ non-infringing. The entire risk as to the quality and performance of
+ the covered code is with you. Should any covered code prove defective
+ in any respect, you (not the initial developer or any other
+ contributor) assume the cost of any necessary servicing, repair or
+ correction. This disclaimer of warranty constitutes an essential part
+ of this license. No use of any covered code is authorized hereunder
+ except under this disclaimer.
+ .
+ 8. Termination
+ .
+ 8.1. This License and the rights granted hereunder will terminate
+ automatically if You fail to comply with terms herein and fail to
+ cure such breach within 30 days of becoming aware of the breach. All
+ sublicenses to the Covered Code which are properly granted shall
+ survive any termination of this License. Provisions which, by their
+ nature, must remain in effect beyond the termination of this License
+ shall survive.
+ .
+ 8.2. If You initiate litigation by asserting a patent infringement
+ claim (excluding declatory judgment actions) against Initial
+ Developer or a Contributor (the Initial Developer or Contributor
+ against whom You file such action is referred to as "Participant")
+ alleging that:
+ .
+ a. such Participant's Contributor Version directly or indirectly
+ infringes any patent, then any and all rights granted by such
+ Participant to You under Sections 2.1 and/or 2.2 of this License
+ shall, upon 60 days notice from Participant terminate
+ prospectively, unless if within 60 days after receipt of notice
+ You either: (i) agree in writing to pay Participant a mutually
+ agreeable reasonable royalty for Your past and future use of
+ Modifications made by such Participant, or (ii) withdraw Your
+ litigation claim with respect to the Contributor Version against
+ such Participant. If within 60 days of notice, a reasonable
+ royalty and payment arrangement are not mutually agreed upon in
+ writing by the parties or the litigation claim is not withdrawn,
+ the rights granted by Participant to You under Sections 2.1 and/
+ or 2.2 automatically terminate at the expiration of the 60 day
+ notice period specified above.
+ b. any software, hardware, or device, other than such Participant's
+ Contributor Version, directly or indirectly infringes any patent,
+ then any rights granted to You by such Participant under Sections
+ 2.1(b) and 2.2(b) are revoked effective as of the date You first
+ made, used, sold, distributed, or had made, Modifications made by
+ that Participant.
+ .
+ 8.3. If You assert a patent infringement claim against Participant
+ alleging that such Participant's Contributor Version directly or
+ indirectly infringes any patent where such claim is resolved (such as
+ by license or settlement) prior to the initiation of patent
+ infringement litigation, then the reasonable value of the licenses
+ granted by such Participant under Sections 2.1 or 2.2 shall be taken
+ into account in determining the amount or value of any payment or
+ license.
+ .
+ 8.4. In the event of termination under Sections 8.1 or 8.2 above, all
+ end user license agreements (excluding distributors and resellers)
+ which have been validly granted by You or any distributor hereunder
+ prior to termination shall survive termination.
+ .
+ 9. Limitation of liability
+ .
+ Under no circumstances and under no legal theory, whether tort
+ (including negligence), contract, or otherwise, shall you, the
+ initial developer, any other contributor, or any distributor of
+ covered code, or any supplier of any of such parties, be liable to
+ any person for any indirect, special, incidental, or consequential
+ damages of any character including, without limitation, damages for
+ loss of goodwill, work stoppage, computer failure or malfunction, or
+ any and all other commercial damages or losses, even if such party
+ shall have been informed of the possibility of such damages. This
+ limitation of liability shall not apply to liability for death or
+ personal injury resulting from such party's negligence to the extent
+ applicable law prohibits such limitation. Some jurisdictions do not
+ allow the exclusion or limitation of incidental or consequential
+ damages, so this exclusion and limitation may not apply to you.
+ .
+ 10. U.S. government end users
+ .
+ The Covered Code is a "commercial item," as that term is defined in
+ 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer
+ software" and "commercial computer software documentation," as such
+ terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48
+ C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June
+ 1995), all U.S. Government End Users acquire Covered Code with only
+ those rights set forth herein.
+ .
+ 11. Miscellaneous
+ .
+ This License represents the complete agreement concerning subject
+ matter hereof. If any provision of this License is held to be
+ unenforceable, such provision shall be reformed only to the extent
+ necessary to make it enforceable. This License shall be governed by
+ California law provisions (except to the extent applicable law, if
+ any, provides otherwise), excluding its conflict-of-law provisions.
+ With respect to disputes in which at least one party is a citizen of,
+ or an entity chartered or registered to do business in the United
+ States of America, any litigation relating to this License shall be
+ subject to the jurisdiction of the Federal Courts of the Northern
+ District of California, with venue lying in Santa Clara County,
+ California, with the losing party responsible for costs, including
+ without limitation, court costs and reasonable attorneys' fees and
+ expenses. The application of the United Nations Convention on
+ Contracts for the International Sale of Goods is expressly excluded.
+ Any law or regulation which provides that the language of a contract
+ shall be construed against the drafter shall not apply to this
+ License.
+ .
+ 12. Responsibility for claims
+ .
+ As between Initial Developer and the Contributors, each party is
+ responsible for claims and damages arising, directly or indirectly,
+ out of its utilization of rights under this License and You agree to
+ work with Initial Developer and Contributors to distribute such
+ responsibility on an equitable basis. Nothing herein is intended or
+ shall be deemed to constitute any admission of liability.
+ .
+ 13. Multiple-licensed code
+ .
+ Initial Developer may designate portions of the Covered Code as
+ "Multiple-Licensed". "Multiple-Licensed" means that the Initial
+ Developer permits you to utilize portions of the Covered Code under
+ Your choice of the MPL or the alternative licenses, if any, specified
+ by the Initial Developer in the file described in Exhibit A.
+ .
+ Exhibit A - Mozilla Public License.
+ .
+ "The contents of this file are subject to the Mozilla Public License
+ Version 1.1 (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.mozilla.org/MPL/
+ .
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ License for the specific language governing rights and limitations
+ under the License.
+ .
+ The Original Code is ______________________________________.
+ .
+ The Initial Developer of the Original Code is ________________________.
+ Portions created by ______________________ are Copyright (C) ______
+ _______________________. All Rights Reserved.
+ .
+ Contributor(s): ______________________________________.
+ .
+ Alternatively, the contents of this file may be used under the terms
+ of the _____ license (the "[___] License"), in which case the
+ provisions of [______] License are applicable instead of those
+ above. If you wish to allow use of your version of this file only
+ under the terms of the [____] License and not to allow others to use
+ your version of this file under the MPL, indicate your decision by
+ deleting the provisions above and replace them with the notice and
+ other provisions required by the [___] License. If you do not delete
+ the provisions above, a recipient may use your version of this file
+ under either the MPL or the [___] License."
+ .
+ NOTE: The text of this Exhibit A may differ slightly from the text of
+ the notices in the Source Code files of the Original Code. You should
+ use the text of this Exhibit A rather than the text found in the
+ Original Code Source Code for Your Modifications.
--- /dev/null
+README.txt
--- /dev/null
+binaries/data/l10n usr/share/games/0ad/
+binaries/system/libAtlasUI.so usr/lib/games/0ad/
+binaries/system/libCollada.so usr/lib/games/0ad/
+binaries/system/libmozjs38-ps-release.so usr/lib/games/0ad/
+binaries/system/ActorEditor usr/lib/games/0ad/
+binaries/system/pyrogenesis usr/games/
+build/resources/0ad.appdata.xml usr/share/appdata/
+build/resources/0ad.desktop usr/share/applications/
+build/resources/0ad.png usr/share/pixmaps/
+usr/games/0ad
--- /dev/null
+debian/0ad.6
+debian/pyrogenesis.6
--- /dev/null
+This directory provides the non-minified versions of the embedded JQuery files
+in the orig source tarball, in order to comply with Debian Policy. (#735349)
+
+The minified javascript files for which this directory provides the
+non-minified versions are as follows. Each of the lines below indicate the
+minified file, followed by the non-minified source file(s) present in this
+directory.
+
+source/tools/replayprofile/jquery.flot.js:
+ jquery.colorhelpers.js
+source/tools/replayprofile/jquery.flot.navigate.js:
+ jquery.event.drag.js
+ jquery.mousewheel.js
+
+source/tools/templatesanalyzer/tablefilter/tablefilter.js:
+source/tools/templatesanalyzer/tablefilter/tf-1.js:
+ tablefilter/*.js
+
+(Tablefilter 0.0.11: https://github.com/koalyptus/TableFilter/tree/4f2316a4b57021a41edd10d3b98e7f8159642392)
+
+
--- /dev/null
+/* Plugin for jQuery for working with colors.
+ *
+ * Version 1.0.
+ *
+ * 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() work in-place instead of returning
+ * new objects.
+ */
+
+(function() {
+ jQuery.color = {};
+
+ // construct color object with some convenient chainable helpers
+ jQuery.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 jQuery.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"
+ jQuery.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 (!jQuery.nodeName(elem.get(0), "body"));
+
+ // catch Safari's way of signalling transparent
+ if (c == "rgba(0, 0, 0, 0)")
+ c = "transparent";
+
+ return jQuery.color.parse(c);
+ }
+
+ // parse CSS color string (like "rgb(10, 32, 43)" or "#fff"),
+ // returns color object
+ jQuery.color.parse = function (str) {
+ var res, m = jQuery.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 = jQuery.trim(str).toLowerCase();
+ if (name == "transparent")
+ return m(255, 255, 255, 0);
+ else {
+ res = lookupColors[name];
+ 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]
+ };
+})();
--- /dev/null
+/*! \r
+jquery.event.drag.js ~ v1.5 ~ Copyright (c) 2008, Three Dub Media (http://threedubmedia.com) \r
+Liscensed under the MIT License ~ http://threedubmedia.googlecode.com/files/MIT-LICENSE.txt\r
+*/\r
+;(function($){ // secure $ jQuery alias\r
+/*******************************************************************************************/\r
+// Created: 2008-06-04 | Updated: 2009-03-24\r
+/*******************************************************************************************/\r
+// Events: drag, dragstart, dragend\r
+/*******************************************************************************************/\r
+\r
+// jquery method\r
+$.fn.drag = function( fn1, fn2, fn3 ){\r
+ if ( fn2 ) this.bind('dragstart', fn1 ); // 2+ args\r
+ if ( fn3 ) this.bind('dragend', fn3 ); // 3 args\r
+ return !fn1 ? this.trigger('drag') // 0 args\r
+ : this.bind('drag', fn2 ? fn2 : fn1 ); // 1+ args\r
+ };\r
+\r
+// local refs\r
+var $event = $.event, $special = $event.special,\r
+\r
+// special event configuration\r
+drag = $special.drag = {\r
+ not: ':input', // don't begin to drag on event.targets that match this selector\r
+ distance: 0, // distance dragged before dragstart\r
+ which: 1, // mouse button pressed to start drag sequence\r
+ dragging: false, // hold the active target element\r
+ setup: function( data ){\r
+ data = $.extend({ \r
+ distance: drag.distance, \r
+ which: drag.which, \r
+ not: drag.not\r
+ }, data || {});\r
+ data.distance = squared( data.distance ); // x² + y² = distance²\r
+ $event.add( this, "mousedown", handler, data );\r
+ if ( this.attachEvent ) this.attachEvent("ondragstart", dontStart ); // prevent image dragging in IE...\r
+ },\r
+ teardown: function(){\r
+ $event.remove( this, "mousedown", handler );\r
+ if ( this === drag.dragging ) drag.dragging = drag.proxy = false; // deactivate element\r
+ selectable( this, true ); // enable text selection\r
+ if ( this.detachEvent ) this.detachEvent("ondragstart", dontStart ); // prevent image dragging in IE...\r
+ }\r
+ };\r
+ \r
+// prevent normal event binding...\r
+$special.dragstart = $special.dragend = { setup:function(){}, teardown:function(){} };\r
+\r
+// handle drag-releatd DOM events\r
+function handler ( event ){ \r
+ var elem = this, returned, data = event.data || {};\r
+ // mousemove or mouseup\r
+ if ( data.elem ){ \r
+ // update event properties...\r
+ elem = event.dragTarget = data.elem; // drag source element\r
+ event.dragProxy = drag.proxy || elem; // proxy element or source\r
+ event.cursorOffsetX = data.pageX - data.left; // mousedown offset\r
+ event.cursorOffsetY = data.pageY - data.top; // mousedown offset\r
+ event.offsetX = event.pageX - event.cursorOffsetX; // element offset\r
+ event.offsetY = event.pageY - event.cursorOffsetY; // element offset\r
+ }\r
+ // mousedown, check some initial props to avoid the switch statement\r
+ else if ( drag.dragging || ( data.which>0 && event.which!=data.which ) || \r
+ $( event.target ).is( data.not ) ) return;\r
+ // handle various events\r
+ switch ( event.type ){\r
+ // mousedown, left click, event.target is not restricted, init dragging\r
+ case 'mousedown':\r
+ $.extend( data, $( elem ).offset(), { \r
+ elem: elem, target: event.target,\r
+ pageX: event.pageX, pageY: event.pageY\r
+ }); // store some initial attributes\r
+ $event.add( document, "mousemove mouseup", handler, data );\r
+ selectable( elem, false ); // disable text selection\r
+ drag.dragging = null; // pending state\r
+ return false; // prevents text selection in safari \r
+ // mousemove, check distance, start dragging\r
+ case !drag.dragging && 'mousemove': \r
+ if ( squared( event.pageX-data.pageX ) \r
+ + squared( event.pageY-data.pageY ) // x² + y² = distance²\r
+ < data.distance ) break; // distance tolerance not reached\r
+ event.target = data.target; // force target from "mousedown" event (fix distance issue)\r
+ returned = hijack( event, "dragstart", elem ); // trigger "dragstart", return proxy element\r
+ if ( returned !== false ){ // "dragstart" not rejected\r
+ drag.dragging = elem; // activate element\r
+ drag.proxy = event.dragProxy = $( returned || elem )[0]; // set proxy\r
+ }\r
+ // mousemove, dragging\r
+ case 'mousemove': \r
+ if ( drag.dragging ){\r
+ returned = hijack( event, "drag", elem ); // trigger "drag" \r
+ if ( $special.drop ){ // manage drop events\r
+ $special.drop.allowed = ( returned !== false ); // prevent drop\r
+ $special.drop.handler( event ); // "dropstart", "dropend"\r
+ }\r
+ if ( returned !== false ) break; // "drag" not rejected, stop \r
+ event.type = "mouseup"; // helps "drop" handler behave\r
+ }\r
+ // mouseup, stop dragging\r
+ case 'mouseup': \r
+ $event.remove( document, "mousemove mouseup", handler ); // remove page events\r
+ if ( drag.dragging ){\r
+ if ( $special.drop ) $special.drop.handler( event ); // "drop"\r
+ hijack( event, "dragend", elem ); // trigger "dragend" \r
+ }\r
+ selectable( elem, true ); // enable text selection\r
+ drag.dragging = drag.proxy = data.elem = false; // deactivate element\r
+ break;\r
+ } \r
+ return true;\r
+ };\r
+\r
+// set event type to custom value, and handle it\r
+function hijack ( event, type, elem ){\r
+ event.type = type; // force the event type\r
+ var result = $.event.handle.call( elem, event );\r
+ return result===false ? false : result || event.result;\r
+ };\r
+ \r
+// return the value squared \r
+function squared ( value ){ return Math.pow( value, 2 ); };\r
+\r
+// suppress default dragstart IE events...\r
+function dontStart(){ return ( drag.dragging === false ); }; \r
+\r
+// toggles text selection attributes \r
+function selectable ( elem, bool ){ \r
+ if ( !elem ) return; // maybe element was removed ? \r
+ elem.unselectable = bool ? "off" : "on"; // IE\r
+ elem.onselectstart = function(){ return bool; }; // IE\r
+ //if ( document.selection && document.selection.empty ) document.selection.empty(); // IE\r
+ if ( elem.style ) elem.style.MozUserSelect = bool ? "" : "none"; // FF\r
+ }; \r
+ \r
+/*******************************************************************************************/\r
+})( jQuery ); // confine scope
\ No newline at end of file
--- /dev/null
+/*! Copyright (c) 2010 Brandon Aaron (http://brandonaaron.net)
+ * Licensed under the MIT License (LICENSE.txt).
+ *
+ * Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers.
+ * Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix.
+ * Thanks to: Seamus Leahy for adding deltaX and deltaY
+ *
+ * Version: 3.0.3
+ *
+ * Requires: 1.2.2+
+ */
+
+(function($) {
+
+var types = ['DOMMouseScroll', 'mousewheel'];
+
+$.event.special.mousewheel = {
+ setup: function() {
+ if ( this.addEventListener ) {
+ for ( var i=types.length; i; ) {
+ this.addEventListener( types[--i], handler, false );
+ }
+ } else {
+ this.onmousewheel = handler;
+ }
+ },
+
+ teardown: function() {
+ if ( this.removeEventListener ) {
+ for ( var i=types.length; i; ) {
+ this.removeEventListener( types[--i], handler, false );
+ }
+ } else {
+ this.onmousewheel = null;
+ }
+ }
+};
+
+$.fn.extend({
+ mousewheel: function(fn) {
+ return fn ? this.bind("mousewheel", fn) : this.trigger("mousewheel");
+ },
+
+ unmousewheel: function(fn) {
+ return this.unbind("mousewheel", fn);
+ }
+});
+
+
+function handler(event) {
+ var orgEvent = event, args = [].slice.call( arguments, 1 ), delta = 0, returnValue = true, deltaX = 0, deltaY = 0;
+
+ event = $.event.fix(event || window.event);
+ event.type = "mousewheel";
+
+ // Old school scrollwheel delta
+ if ( event.wheelDelta ) { delta = event.wheelDelta/120; }
+ if ( event.detail ) { delta = -event.detail/3; }
+
+ // New school multidimensional scroll (touchpads) deltas
+ deltaY = delta;
+
+ // Gecko
+ if ( orgEvent.axis !== undefined && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) {
+ deltaY = 0;
+ deltaX = -1*delta;
+ }
+
+ // Webkit
+ if ( orgEvent.wheelDeltaY !== undefined ) { deltaY = orgEvent.wheelDeltaY/120; }
+ if ( orgEvent.wheelDeltaX !== undefined ) { deltaX = -1*orgEvent.wheelDeltaX/120; }
+
+ // Add event and delta to the front of the arguments
+ args.unshift(event, delta, deltaX, deltaY);
+
+ return $.event.handle.apply(this, args);
+}
+
+})(jQuery);
\ No newline at end of file
--- /dev/null
+/**
+ * Array utilities
+ */
+
+import Str from './string';
+
+export default {
+ has: function(arr, val, caseSensitive){
+ let sCase = caseSensitive===undefined ? false : caseSensitive;
+ for (var i=0; i<arr.length; i++){
+ if(Str.matchCase(arr[i].toString(), sCase) == val){
+ return true;
+ }
+ }
+ return false;
+ }
+};
--- /dev/null
+/**
+ * Cookie utilities
+ */
+
+export default {
+
+ write(name, value, hours){
+ let expire = '';
+ if(hours){
+ expire = new Date((new Date()).getTime() + hours * 3600000);
+ expire = '; expires=' + expire.toGMTString();
+ }
+ document.cookie = name + '=' + escape(value) + expire;
+ },
+
+ read(name){
+ let cookieValue = '',
+ search = name + '=';
+ if(document.cookie.length > 0){
+ let cookie = document.cookie,
+ offset = cookie.indexOf(search);
+ if(offset !== -1){
+ offset += search.length;
+ let end = cookie.indexOf(';', offset);
+ if(end === -1){
+ end = cookie.length;
+ }
+ cookieValue = unescape(cookie.substring(offset, end));
+ }
+ }
+ return cookieValue;
+ },
+
+ remove(name){
+ this.write(name, '', -1);
+ },
+
+ valueToArray(name, separator){
+ if(!separator){
+ separator = ',';
+ }
+ //reads the cookie
+ let val = this.read(name);
+ //creates an array with filters' values
+ let arr = val.split(separator);
+ return arr;
+ },
+
+ getValueByIndex(name, index, separator){
+ if(!separator){
+ separator = ',';
+ }
+ //reads the cookie
+ let val = this.valueToArray(name, separator);
+ return val[index];
+ }
+
+};
--- /dev/null
+/**
+ * Date utilities
+ */
+
+export default {
+ isValid(dateStr, format){
+ if(!format) {
+ format = 'DMY';
+ }
+ format = format.toUpperCase();
+ if(format.length != 3) {
+ if(format==='DDMMMYYYY'){
+ let d = this.format(dateStr, format);
+ dateStr = d.getDate() +'/'+ (d.getMonth()+1) +'/'+
+ d.getFullYear();
+ format = 'DMY';
+ }
+ }
+ if((format.indexOf('M') === -1) || (format.indexOf('D') === -1) ||
+ (format.indexOf('Y') === -1)){
+ format = 'DMY';
+ }
+ let reg1, reg2;
+ // If the year is first
+ if(format.substring(0, 1) == 'Y') {
+ reg1 = /^\d{2}(\-|\/|\.)\d{1,2}\1\d{1,2}$/;
+ reg2 = /^\d{4}(\-|\/|\.)\d{1,2}\1\d{1,2}$/;
+ } else if(format.substring(1, 2) == 'Y') { // If the year is second
+ reg1 = /^\d{1,2}(\-|\/|\.)\d{2}\1\d{1,2}$/;
+ reg2 = /^\d{1,2}(\-|\/|\.)\d{4}\1\d{1,2}$/;
+ } else { // The year must be third
+ reg1 = /^\d{1,2}(\-|\/|\.)\d{1,2}\1\d{2}$/;
+ reg2 = /^\d{1,2}(\-|\/|\.)\d{1,2}\1\d{4}$/;
+ }
+ // If it doesn't conform to the right format (with either a 2 digit year
+ // or 4 digit year), fail
+ if(reg1.test(dateStr) === false && reg2.test(dateStr) === false) {
+ return false;
+ }
+ // Split into 3 parts based on what the divider was
+ let parts = dateStr.split(RegExp.$1);
+ let mm, dd, yy;
+ // Check to see if the 3 parts end up making a valid date
+ if(format.substring(0, 1) === 'M'){
+ mm = parts[0];
+ } else if(format.substring(1, 2) === 'M'){
+ mm = parts[1];
+ } else {
+ mm = parts[2];
+ }
+ if(format.substring(0, 1) === 'D'){
+ dd = parts[0];
+ } else if(format.substring(1, 2) === 'D'){
+ dd = parts[1];
+ } else {
+ dd = parts[2];
+ }
+ if(format.substring(0, 1) === 'Y'){
+ yy = parts[0];
+ } else if(format.substring(1, 2) === 'Y'){
+ yy = parts[1];
+ } else {
+ yy = parts[2];
+ }
+ if(parseInt(yy, 10) <= 50){
+ yy = (parseInt(yy, 10) + 2000).toString();
+ }
+ if(parseInt(yy, 10) <= 99){
+ yy = (parseInt(yy, 10) + 1900).toString();
+ }
+ let dt = new Date(
+ parseInt(yy, 10), parseInt(mm, 10)-1, parseInt(dd, 10),
+ 0, 0, 0, 0);
+ if(parseInt(dd, 10) != dt.getDate()){
+ return false;
+ }
+ if(parseInt(mm, 10)-1 != dt.getMonth()){
+ return false;
+ }
+ return true;
+ },
+ format(dateStr, formatStr) {
+ if(!formatStr){
+ formatStr = 'DMY';
+ }
+ if(!dateStr || dateStr === ''){
+ return new Date(1001, 0, 1);
+ }
+ let oDate;
+ let parts;
+
+ switch(formatStr.toUpperCase()){
+ case 'DDMMMYYYY':
+ parts = dateStr.replace(/[- \/.]/g,' ').split(' ');
+ oDate = new Date(y2kDate(parts[2]),mmm2mm(parts[1])-1,parts[0]);
+ break;
+ case 'DMY':
+ /* jshint ignore:start */
+ parts = dateStr.replace(
+ /^(0?[1-9]|[12][0-9]|3[01])([- \/.])(0?[1-9]|1[012])([- \/.])((\d\d)?\d\d)$/,'$1 $3 $5').split(' ');
+ oDate = new Date(y2kDate(parts[2]),parts[1]-1,parts[0]);
+ /* jshint ignore:end */
+ break;
+ case 'MDY':
+ /* jshint ignore:start */
+ parts = dateStr.replace(
+ /^(0?[1-9]|1[012])([- \/.])(0?[1-9]|[12][0-9]|3[01])([- \/.])((\d\d)?\d\d)$/,'$1 $3 $5').split(' ');
+ oDate = new Date(y2kDate(parts[2]),parts[0]-1,parts[1]);
+ /* jshint ignore:end */
+ break;
+ case 'YMD':
+ /* jshint ignore:start */
+ parts = dateStr.replace(/^((\d\d)?\d\d)([- \/.])(0?[1-9]|1[012])([- \/.])(0?[1-9]|[12][0-9]|3[01])$/,'$1 $4 $6').split(' ');
+ oDate = new Date(y2kDate(parts[0]),parts[1]-1,parts[2]);
+ /* jshint ignore:end */
+ break;
+ default: //in case format is not correct
+ /* jshint ignore:start */
+ parts = dateStr.replace(/^(0?[1-9]|[12][0-9]|3[01])([- \/.])(0?[1-9]|1[012])([- \/.])((\d\d)?\d\d)$/,'$1 $3 $5').split(' ');
+ oDate = new Date(y2kDate(parts[2]),parts[1]-1,parts[0]);
+ /* jshint ignore:end */
+ break;
+ }
+ return oDate;
+ }
+};
+
+function y2kDate(yr){
+ if(yr === undefined){
+ return 0;
+ }
+ if(yr.length>2){
+ return yr;
+ }
+ let y;
+ //>50 belong to 1900
+ if(yr <= 99 && yr>50){
+ y = '19' + yr;
+ }
+ //<50 belong to 2000
+ if(yr<50 || yr === '00'){
+ y = '20' + yr;
+ }
+ return y;
+}
+
+function mmm2mm(mmm){
+ if(mmm === undefined){
+ return 0;
+ }
+ let mondigit;
+ let MONTH_NAMES = [
+ 'january','february','march','april','may','june','july',
+ 'august','september','october','november','december',
+ 'jan','feb','mar','apr','may','jun','jul','aug','sep','oct',
+ 'nov','dec'
+ ];
+ for(let m_i=0; m_i < MONTH_NAMES.length; m_i++){
+ let month_name = MONTH_NAMES[m_i];
+ if (mmm.toLowerCase() === month_name){
+ mondigit = m_i+1;
+ break;
+ }
+ }
+ if(mondigit > 11 || mondigit < 23){
+ mondigit = mondigit - 12;
+ }
+ if(mondigit < 1 || mondigit > 12){
+ return 0;
+ }
+ return mondigit;
+}
--- /dev/null
+/**
+ * DOM utilities
+ */
+
+export default {
+
+ /**
+ * Returns text + text of children of given node
+ * @param {NodeElement} node
+ * @return {String}
+ */
+ getText(node){
+ let s = node.textContent || node.innerText ||
+ node.innerHTML.replace(/<[^<>]+>/g, '');
+ s = s.replace(/^\s+/, '').replace(/\s+$/, '');
+ return s;
+ },
+
+ /**
+ * Creates an html element with given collection of attributes
+ * @param {String} tag a string of the html tag to create
+ * @param {Array} an undetermined number of arrays containing the with 2
+ * items, the attribute name and its value ['id','myId']
+ * @return {Object} created element
+ */
+ create(tag){
+ if(!tag || tag===''){
+ return;
+ }
+
+ let el = document.createElement(tag),
+ args = arguments;
+
+ if(args.length > 1){
+ for(let i=0; i<args.length; i++){
+ let argtype = typeof args[i];
+ if(argtype.toLowerCase() === 'object' && args[i].length === 2){
+ el.setAttribute(args[i][0], args[i][1]);
+ }
+ }
+ }
+ return el;
+ },
+
+ /**
+ * Returns a text node with given text
+ * @param {String} txt
+ * @return {Object}
+ */
+ text(txt){
+ return document.createTextNode(txt);
+ },
+
+ hasClass(ele, cls){
+ if(!ele){ return false; }
+
+ if(supportsClassList()){
+ return ele.classList.contains(cls);
+ }
+ return ele.className.match(new RegExp('(\\s|^)'+ cls +'(\\s|$)'));
+ },
+
+ addClass(ele, cls){
+ if(!ele){ return; }
+
+ if(supportsClassList()){
+ ele.classList.add(cls);
+ return;
+ }
+
+ if(ele.className === ''){
+ ele.className = cls;
+ }
+ else if(!this.hasClass(ele, cls)){
+ ele.className += ' ' + cls;
+ }
+ },
+
+ removeClass(ele, cls){
+ if(!ele){ return; }
+
+ if(supportsClassList()){
+ ele.classList.remove(cls);
+ return;
+ }
+ let reg = new RegExp('(\\s|^)'+ cls +'(\\s|$)', 'g');
+ ele.className = ele.className.replace(reg, '');
+ },
+
+ /**
+ * Creates and returns an option element
+ * @param {String} text option text
+ * @param {String} value option value
+ * @param {Boolean} isSel whether option is selected
+ * @return {Object} option element
+ */
+ createOpt(text, value, isSel){
+ let isSelected = isSel ? true : false,
+ opt = isSelected ?
+ this.create('option', ['value',value], ['selected','true']) :
+ this.create('option', ['value',value]);
+ opt.appendChild(this.text(text));
+ return opt;
+ },
+
+ /**
+ * Creates and returns a checklist item
+ * @param {Number} chkIndex index of check item
+ * @param {String} chkValue check item value
+ * @param {String} labelText check item label text
+ * @return {Object} li DOM element
+ */
+ createCheckItem(chkIndex, chkValue, labelText){
+ let li = this.create('li'),
+ label = this.create('label', ['for', chkIndex]),
+ check = this.create('input',
+ ['id', chkIndex],
+ ['name', chkIndex],
+ ['type', 'checkbox'],
+ ['value', chkValue]
+ );
+ label.appendChild(check);
+ label.appendChild(this.text(labelText));
+ li.appendChild(label);
+ li.label = label;
+ li.check = check;
+ return li;
+ },
+
+ id(_id){
+ return document.getElementById(_id);
+ },
+
+ tag(o, tagname){
+ return o.getElementsByTagName(tagname);
+ }
+};
+
+// HTML5 classList API
+function supportsClassList(){
+ return document.documentElement.classList;
+}
--- /dev/null
+/**
+ * DOM event utilities
+ */
+
+export default {
+ add(obj, type, func, capture){
+ if(obj.addEventListener){
+ obj.addEventListener(type, func, capture);
+ }
+ else if(obj.attachEvent){
+ obj.attachEvent('on'+type, func);
+ } else {
+ obj['on'+type] = func;
+ }
+ },
+ remove(obj, type, func, capture){
+ if(obj.detachEvent){
+ obj.detachEvent('on'+type,func);
+ }
+ else if(obj.removeEventListener){
+ obj.removeEventListener(type, func, capture);
+ } else {
+ obj['on'+type] = null;
+ }
+ },
+ stop(evt){
+ if(!evt){
+ evt = window.event;
+ }
+ if(evt.stopPropagation){
+ evt.stopPropagation();
+ } else {
+ evt.cancelBubble = true;
+ }
+ },
+ cancel(evt){
+ if(!evt){
+ evt = window.event;
+ }
+ if(evt.preventDefault) {
+ evt.preventDefault();
+ } else {
+ evt.returnValue = false;
+ }
+ },
+ target(evt){
+ return (evt && evt.target) || (window.event && window.event.srcElement);
+ },
+ keyCode(evt){
+ return evt.charCode ? evt.charCode :
+ (evt.keyCode ? evt.keyCode: (evt.which ? evt.which : 0));
+ }
+};
--- /dev/null
+import Dom from '../../dom';
+
+export default class AdapterEzEditTable {
+ /**
+ * Adapter module for ezEditTable, an external library providing advanced
+ * grid features (selection and edition):
+ * http://codecanyon.net/item/ezedittable-enhance-html-tables/2425123?ref=koalyptus
+ *
+ * @param {Object} tf TableFilter instance
+ */
+ constructor(tf, cfg){
+ // ezEditTable config
+ this.initialized = false;
+ this.desc = cfg.description || 'ezEditTable adapter';
+ this.filename = cfg.filename || 'ezEditTable.js';
+ this.vendorPath = cfg.vendor_path;
+ this.loadStylesheet = Boolean(cfg.load_stylesheet);
+ this.stylesheet = cfg.stylesheet || this.vendorPath + 'ezEditTable.css';
+ this.stylesheetName = cfg.stylesheet_name || 'ezEditTableCss';
+ this.err = 'Failed to instantiate EditTable object.\n"ezEditTable" ' +
+ 'dependency not found.';
+ // Enable the ezEditTable's scroll into view behaviour if grid layout on
+ cfg.scroll_into_view = cfg.scroll_into_view===false ?
+ false : tf.gridLayout;
+
+ this._ezEditTable = null;
+ this.cfg = cfg;
+ this.tf = tf;
+ }
+
+ /**
+ * Conditionally load ezEditTable library and set advanced grid
+ * @return {[type]} [description]
+ */
+ init(){
+ var tf = this.tf;
+ if(window.EditTable){
+ this._setAdvancedGrid();
+ } else {
+ var path = this.vendorPath + this.filename;
+ tf.import(this.filename, path, ()=> { this._setAdvancedGrid(); });
+ }
+ if(this.loadStylesheet && !tf.isImported(this.stylesheet, 'link')){
+ tf.import(this.stylesheetName, this.stylesheet, null, 'link');
+ }
+ }
+
+ /**
+ * Instantiate ezEditTable component for advanced grid features
+ */
+ _setAdvancedGrid(){
+ var tf = this.tf;
+
+ //start row for EditTable constructor needs to be calculated
+ var startRow,
+ cfg = this.cfg,
+ thead = Dom.tag(tf.tbl, 'thead');
+
+ //if thead exists and startRow not specified, startRow is calculated
+ //automatically by EditTable
+ if(thead.length > 0 && !cfg.startRow){
+ startRow = undefined;
+ }
+ //otherwise startRow config property if any or TableFilter refRow
+ else{
+ startRow = cfg.startRow || tf.refRow;
+ }
+
+ cfg.base_path = cfg.base_path || tf.basePath + 'ezEditTable/';
+ var editable = cfg.editable;
+ var selectable = cfg.selection;
+
+ if(selectable){
+ cfg.default_selection = cfg.default_selection || 'row';
+ }
+ //CSS Styles
+ cfg.active_cell_css = cfg.active_cell_css || 'ezETSelectedCell';
+
+ var _lastValidRowIndex = 0;
+ var _lastRowIndex = 0;
+
+ if(selectable){
+ //Row navigation needs to be calculated according to TableFilter's
+ //validRowsIndex array
+ var onAfterSelection = function(et, selectedElm, e){
+ var slc = et.Selection;
+ //Next valid filtered row needs to be selected
+ var doSelect = function(nextRowIndex){
+ if(et.defaultSelection === 'row'){
+ slc.SelectRowByIndex(nextRowIndex);
+ } else {
+ et.ClearSelections();
+ var cellIndex = selectedElm.cellIndex,
+ row = tf.tbl.rows[nextRowIndex];
+ if(et.defaultSelection === 'both'){
+ slc.SelectRowByIndex(nextRowIndex);
+ }
+ if(row){
+ slc.SelectCell(row.cells[cellIndex]);
+ }
+ }
+ //Table is filtered
+ if(tf.validRowsIndex.length !== tf.getRowsNb()){
+ var r = tf.tbl.rows[nextRowIndex];
+ if(r){
+ r.scrollIntoView(false);
+ }
+ if(cell){
+ if(cell.cellIndex === (tf.getCellsNb()-1) &&
+ tf.gridLayout){
+ tf.tblCont.scrollLeft = 100000000;
+ }
+ else if(cell.cellIndex===0 && tf.gridLayout){
+ tf.tblCont.scrollLeft = 0;
+ } else {
+ cell.scrollIntoView(false);
+ }
+ }
+ }
+ };
+
+ //table is not filtered
+ if(!tf.validRowsIndex){
+ return;
+ }
+ var validIndexes = tf.validRowsIndex,
+ validIdxLen = validIndexes.length,
+ row = et.defaultSelection !== 'row' ?
+ selectedElm.parentNode : selectedElm,
+ //cell for default_selection = 'both' or 'cell'
+ cell = selectedElm.nodeName==='TD' ? selectedElm : null,
+ keyCode = e !== undefined ? et.Event.GetKey(e) : 0,
+ isRowValid = validIndexes.indexOf(row.rowIndex) !== -1,
+ nextRowIndex,
+ paging = tf.feature('paging'),
+ //pgup/pgdown keys
+ d = (keyCode === 34 || keyCode === 33 ?
+ (paging && paging.pagingLength || et.nbRowsPerPage) :1);
+
+ //If next row is not valid, next valid filtered row needs to be
+ //calculated
+ if(!isRowValid){
+ //Selection direction up/down
+ if(row.rowIndex>_lastRowIndex){
+ //last row
+ if(row.rowIndex >= validIndexes[validIdxLen-1]){
+ nextRowIndex = validIndexes[validIdxLen-1];
+ } else {
+ var calcRowIndex = (_lastValidRowIndex + d);
+ if(calcRowIndex > (validIdxLen-1)){
+ nextRowIndex = validIndexes[validIdxLen-1];
+ } else {
+ nextRowIndex = validIndexes[calcRowIndex];
+ }
+ }
+ } else{
+ //first row
+ if(row.rowIndex <= validIndexes[0]){
+ nextRowIndex = validIndexes[0];
+ } else {
+ var v = validIndexes[_lastValidRowIndex - d];
+ nextRowIndex = v ? v : validIndexes[0];
+ }
+ }
+ _lastRowIndex = row.rowIndex;
+ doSelect(nextRowIndex);
+ } else {
+ //If filtered row is valid, special calculation for
+ //pgup/pgdown keys
+ if(keyCode!==34 && keyCode!==33){
+ _lastValidRowIndex = validIndexes.indexOf(row.rowIndex);
+ _lastRowIndex = row.rowIndex;
+ } else {
+ if(keyCode === 34){ //pgdown
+ //last row
+ if((_lastValidRowIndex + d) <= (validIdxLen-1)){
+ nextRowIndex = validIndexes[
+ _lastValidRowIndex + d];
+ } else {
+ nextRowIndex = [validIdxLen-1];
+ }
+ } else { //pgup
+ //first row
+ if((_lastValidRowIndex - d) <= validIndexes[0]){
+ nextRowIndex = validIndexes[0];
+ } else {
+ nextRowIndex = validIndexes[
+ _lastValidRowIndex - d];
+ }
+ }
+ _lastRowIndex = nextRowIndex;
+ _lastValidRowIndex = validIndexes.indexOf(nextRowIndex);
+ doSelect(nextRowIndex);
+ }
+ }
+ };
+
+ //Page navigation has to be enforced whenever selected row is out of
+ //the current page range
+ var onBeforeSelection = function(et, selectedElm){
+ var row = et.defaultSelection !== 'row' ?
+ selectedElm.parentNode : selectedElm;
+ if(tf.paging){
+ if(tf.feature('paging').nbPages > 1){
+ var paging = tf.feature('paging');
+ //page length is re-assigned in case it has changed
+ et.nbRowsPerPage = paging.pagingLength;
+ var validIndexes = tf.validRowsIndex,
+ validIdxLen = validIndexes.length,
+ pagingEndRow = parseInt(paging.startPagingRow, 10) +
+ parseInt(paging.pagingLength, 10);
+ var rowIndex = row.rowIndex;
+
+ if((rowIndex === validIndexes[validIdxLen-1]) &&
+ paging.currentPageNb!==paging.nbPages){
+ paging.setPage('last');
+ }
+ else if((rowIndex == validIndexes[0]) &&
+ paging.currentPageNb!==1){
+ paging.setPage('first');
+ }
+ else if(rowIndex > validIndexes[pagingEndRow-1] &&
+ rowIndex < validIndexes[validIdxLen-1]){
+ paging.setPage('next');
+ }
+ else if(
+ rowIndex < validIndexes[paging.startPagingRow] &&
+ rowIndex > validIndexes[0]){
+ paging.setPage('previous');
+ }
+ }
+ }
+ };
+
+ //Selected row needs to be visible when paging is activated
+ if(tf.paging){
+ tf.feature('paging').onAfterChangePage = function(paging){
+ var advGrid = paging.tf.extension('advancedGrid');
+ var et = advGrid._ezEditTable;
+ var slc = et.Selection;
+ var row = slc.GetActiveRow();
+ if(row){
+ row.scrollIntoView(false);
+ }
+ var cell = slc.GetActiveCell();
+ if(cell){
+ cell.scrollIntoView(false);
+ }
+ };
+ }
+
+ //Rows navigation when rows are filtered is performed with the
+ //EditTable row selection callback events
+ if(cfg.default_selection==='row'){
+ var fnB = cfg.on_before_selected_row;
+ cfg.on_before_selected_row = function(){
+ onBeforeSelection(arguments[0], arguments[1], arguments[2]);
+ if(fnB){
+ fnB.call(
+ null, arguments[0], arguments[1], arguments[2]);
+ }
+ };
+ var fnA = cfg.on_after_selected_row;
+ cfg.on_after_selected_row = function(){
+ onAfterSelection(arguments[0], arguments[1], arguments[2]);
+ if(fnA){
+ fnA.call(
+ null, arguments[0], arguments[1], arguments[2]);
+ }
+ };
+ } else {
+ var fnD = cfg.on_before_selected_cell;
+ cfg.on_before_selected_cell = function(){
+ onBeforeSelection(arguments[0], arguments[1], arguments[2]);
+ if(fnD){
+ fnD.call(
+ null, arguments[0], arguments[1], arguments[2]);
+ }
+ };
+ var fnC = cfg.on_after_selected_cell;
+ cfg.on_after_selected_cell = function(){
+ onAfterSelection(arguments[0], arguments[1], arguments[2]);
+ if(fnC){
+ fnC.call(
+ null, arguments[0], arguments[1], arguments[2]);
+ }
+ };
+ }
+ }
+ if(editable){
+ //Added or removed rows, TF rows number needs to be re-calculated
+ var fnE = cfg.on_added_dom_row;
+ cfg.on_added_dom_row = function(){
+ tf.nbFilterableRows++;
+ if(!tf.paging){
+ tf.feature('rowsCounter').refresh();
+ } else {
+ tf.nbRows++;
+ tf.nbVisibleRows++;
+ tf.nbFilterableRows++;
+ tf.paging=false;
+ tf.feature('paging').destroy();
+ tf.feature('paging').reset();
+ }
+ if(tf.alternateRows){
+ tf.feature('alternateRows').init();
+ }
+ if(fnE){
+ fnE.call(null, arguments[0], arguments[1], arguments[2]);
+ }
+ };
+ if(cfg.actions && cfg.actions['delete']){
+ var fnF = cfg.actions['delete'].on_after_submit;
+ cfg.actions['delete'].on_after_submit = function(){
+ tf.nbFilterableRows--;
+ if(!tf.paging){
+ tf.feature('rowsCounter').refresh();
+ } else {
+ tf.nbRows--;
+ tf.nbVisibleRows--;
+ tf.nbFilterableRows--;
+ tf.paging=false;
+ tf.feature('paging').destroy();
+ tf.feature('paging').reset(false);
+ }
+ if(tf.alternateRows){
+ tf.feature('alternateRows').init();
+ }
+ if(fnF){
+ fnF.call(null, arguments[0], arguments[1]);
+ }
+ };
+ }
+ }
+
+ try{
+ this._ezEditTable = new EditTable(tf.id, cfg, startRow);
+ this._ezEditTable.Init();
+ } catch(e) { throw new Error(this.err); }
+
+ this.initialized = true;
+ }
+
+ /**
+ * Reset advanced grid when previously removed
+ */
+ reset(){
+ var ezEditTable = this._ezEditTable;
+ if(ezEditTable){
+ if(this.cfg.selection){
+ ezEditTable.Selection.Set();
+ }
+ if(this.cfg.editable){
+ ezEditTable.Editable.Set();
+ }
+ }
+ }
+
+ /**
+ * Remove advanced grid
+ */
+ destroy(){
+ var ezEditTable = this._ezEditTable;
+ if(ezEditTable){
+ if(this.cfg.selection){
+ ezEditTable.Selection.ClearSelections();
+ ezEditTable.Selection.Remove();
+ }
+ if(this.cfg.editable){
+ ezEditTable.Editable.Remove();
+ }
+ }
+ this.initialized = false;
+ }
+}
--- /dev/null
+import AdapterEzEditTable from './adapterEzEditTable';
+
+export default AdapterEzEditTable;
\ No newline at end of file
--- /dev/null
+import Dom from '../../dom';
+import Str from '../../string';
+import Types from '../../types';
+
+export default class ColOps{
+
+ /**
+ * Column calculations
+ * @param {Object} tf TableFilter instance
+ */
+ constructor(tf, opts) {
+
+ //calls function before col operation
+ this.onBeforeOperation = Types.isFn(opts.on_before_operation) ?
+ opts.on_before_operation : null;
+ //calls function after col operation
+ this.onAfterOperation = Types.isFn(opts.on_after_operation) ?
+ opts.on_after_operation : null;
+
+ this.opts = opts;
+ this.tf = tf;
+ }
+
+ init(){
+ this.calc();
+ }
+
+ /**
+ * Calculates columns' values
+ * Configuration options are stored in 'opts' property
+ * - 'id' contains ids of elements showing result (array)
+ * - 'col' contains the columns' indexes (array)
+ * - 'operation' contains operation type (array, values: 'sum', 'mean',
+ * 'min', 'max', 'median', 'q1', 'q3')
+ * - 'write_method' array defines which method to use for displaying the
+ * result (innerHTML, setValue, createTextNode) - default: 'innerHTML'
+ * - 'tot_row_index' defines in which row results are displayed
+ * (integers array)
+ *
+ * - changes made by Nuovella:
+ * (1) optimized the routine (now it will only process each column once),
+ * (2) added calculations for the median, lower and upper quartile.
+ */
+ calc() {
+ var tf = this.tf;
+ if(!tf.isFirstLoad && !tf.hasGrid()){
+ return;
+ }
+
+ if(this.onBeforeOperation){
+ this.onBeforeOperation.call(null, tf);
+ }
+
+ var opts = this.opts,
+ labelId = opts.id,
+ colIndex = opts.col,
+ operation = opts.operation,
+ outputType = opts.write_method,
+ totRowIndex = opts.tot_row_index,
+ excludeRow = opts.exclude_row,
+ decimalPrecision = Types.isUndef(opts.decimal_precision) ?
+ 2 : opts.decimal_precision;
+
+ //nuovella: determine unique list of columns to operate on
+ var ucolIndex = [],
+ ucolMax = 0;
+ ucolIndex[ucolMax] = colIndex[0];
+
+ for(var ii=1; ii<colIndex.length; ii++){
+ var saved = 0;
+ //see if colIndex[ii] is already in the list of unique indexes
+ for(var jj=0; jj<=ucolMax; jj++){
+ if(ucolIndex[jj] === colIndex[ii]){
+ saved = 1;
+ }
+ }
+ //if not saved then, save the index;
+ if (saved === 0){
+ ucolMax++;
+ ucolIndex[ucolMax] = colIndex[ii];
+ }
+ }
+
+ if(Str.lower(typeof labelId)=='object' &&
+ Str.lower(typeof colIndex)=='object' &&
+ Str.lower(typeof operation)=='object'){
+ var rows = tf.tbl.rows,
+ colvalues = [];
+
+ for(var ucol=0; ucol<=ucolMax; ucol++){
+ //this retrieves col values
+ //use ucolIndex because we only want to pass through this loop
+ //once for each column get the values in this unique column
+ colvalues.push(
+ tf.getColValues(ucolIndex[ucol], false, true, excludeRow));
+
+ //next: calculate all operations for this column
+ var result,
+ nbvalues=0,
+ temp,
+ meanValue=0,
+ sumValue=0,
+ minValue=null,
+ maxValue=null,
+ q1Value=null,
+ medValue=null,
+ q3Value=null,
+ meanFlag=0,
+ sumFlag=0,
+ minFlag=0,
+ maxFlag=0,
+ q1Flag=0,
+ medFlag=0,
+ q3Flag=0,
+ theList=[],
+ opsThisCol=[],
+ decThisCol=[],
+ labThisCol=[],
+ oTypeThisCol=[],
+ mThisCol=-1;
+
+ for(var k=0; k<colIndex.length; k++){
+ if(colIndex[k] === ucolIndex[ucol]){
+ mThisCol++;
+ opsThisCol[mThisCol]=Str.lower(operation[k]);
+ decThisCol[mThisCol]=decimalPrecision[k];
+ labThisCol[mThisCol]=labelId[k];
+ oTypeThisCol = outputType !== undefined &&
+ Str.lower(typeof outputType)==='object' ?
+ outputType[k] : null;
+
+ switch(opsThisCol[mThisCol]){
+ case 'mean':
+ meanFlag=1;
+ break;
+ case 'sum':
+ sumFlag=1;
+ break;
+ case 'min':
+ minFlag=1;
+ break;
+ case 'max':
+ maxFlag=1;
+ break;
+ case 'median':
+ medFlag=1;
+ break;
+ case 'q1':
+ q1Flag=1;
+ break;
+ case 'q3':
+ q3Flag=1;
+ break;
+ }
+ }
+ }
+
+ for(var j=0; j<colvalues[ucol].length; j++){
+ //sort the list for calculation of median and quartiles
+ if((q1Flag==1)|| (q3Flag==1) || (medFlag==1)){
+ if (j<colvalues[ucol].length -1){
+ for(k=j+1; k<colvalues[ucol].length; k++) {
+ if(eval(colvalues[ucol][k]) <
+ eval(colvalues[ucol][j])){
+ temp = colvalues[ucol][j];
+ colvalues[ucol][j] = colvalues[ucol][k];
+ colvalues[ucol][k] = temp;
+ }
+ }
+ }
+ }
+ var cvalue = parseFloat(colvalues[ucol][j]);
+ theList[j] = parseFloat(cvalue);
+
+ if(!isNaN(cvalue)){
+ nbvalues++;
+ if(sumFlag===1 || meanFlag===1){
+ sumValue += parseFloat( cvalue );
+ }
+ if(minFlag===1){
+ if(minValue===null){
+ minValue = parseFloat( cvalue );
+ } else{
+ minValue = parseFloat( cvalue ) < minValue ?
+ parseFloat( cvalue ): minValue;
+ }
+ }
+ if(maxFlag===1){
+ if (maxValue===null){
+ maxValue = parseFloat( cvalue );
+ } else {
+ maxValue = parseFloat( cvalue ) > maxValue ?
+ parseFloat( cvalue ): maxValue;
+ }
+ }
+ }
+ }//for j
+ if(meanFlag===1){
+ meanValue = sumValue/nbvalues;
+ }
+ if(medFlag===1){
+ var aux = 0;
+ if(nbvalues%2 === 1){
+ aux = Math.floor(nbvalues/2);
+ medValue = theList[aux];
+ } else{
+ medValue =
+ (theList[nbvalues/2] + theList[((nbvalues/2)-1)])/2;
+ }
+ }
+ var posa;
+ if(q1Flag===1){
+ posa=0.0;
+ posa = Math.floor(nbvalues/4);
+ if(4*posa == nbvalues){
+ q1Value = (theList[posa-1] + theList[posa])/2;
+ } else {
+ q1Value = theList[posa];
+ }
+ }
+ if (q3Flag===1){
+ posa=0.0;
+ var posb=0.0;
+ posa = Math.floor(nbvalues/4);
+ if (4*posa === nbvalues){
+ posb = 3*posa;
+ q3Value = (theList[posb] + theList[posb-1])/2;
+ } else {
+ q3Value = theList[nbvalues-posa-1];
+ }
+ }
+
+ for(var i=0; i<=mThisCol; i++){
+ switch( opsThisCol[i] ){
+ case 'mean':
+ result=meanValue;
+ break;
+ case 'sum':
+ result=sumValue;
+ break;
+ case 'min':
+ result=minValue;
+ break;
+ case 'max':
+ result=maxValue;
+ break;
+ case 'median':
+ result=medValue;
+ break;
+ case 'q1':
+ result=q1Value;
+ break;
+ case 'q3':
+ result=q3Value;
+ break;
+ }
+
+ var precision = !isNaN(decThisCol[i]) ? decThisCol[i] : 2;
+
+ //if outputType is defined
+ if(oTypeThisCol && result){
+ result = result.toFixed( precision );
+
+ if(Dom.id(labThisCol[i])){
+ switch( Str.lower(oTypeThisCol) ){
+ case 'innerhtml':
+ if (isNaN(result) || !isFinite(result) ||
+ nbvalues===0){
+ Dom.id(labThisCol[i]).innerHTML = '.';
+ } else{
+ Dom.id(labThisCol[i]).innerHTML= result;
+ }
+ break;
+ case 'setvalue':
+ Dom.id( labThisCol[i] ).value = result;
+ break;
+ case 'createtextnode':
+ var oldnode = Dom.id(labThisCol[i])
+ .firstChild;
+ var txtnode = Dom.text(result);
+ Dom.id(labThisCol[i])
+ .replaceChild(txtnode, oldnode);
+ break;
+ }//switch
+ }
+ } else {
+ try{
+ if(isNaN(result) || !isFinite(result) ||
+ nbvalues===0){
+ Dom.id(labThisCol[i]).innerHTML = '.';
+ } else {
+ Dom.id(labThisCol[i]).innerHTML =
+ result.toFixed(precision);
+ }
+ } catch(e) {}//catch
+ }//else
+ }//for i
+
+ // row(s) with result are always visible
+ var totRow = totRowIndex && totRowIndex[ucol] ?
+ rows[totRowIndex[ucol]] : null;
+ if(totRow){
+ totRow.style.display = '';
+ }
+ }//for ucol
+ }//if typeof
+
+ if(this.onAfterOperation){
+ this.onAfterOperation.call(null, tf);
+ }
+ }
+
+ destroy(){}
+
+}
--- /dev/null
+import Dom from '../../dom';
+import Types from '../../types';
+import Event from '../../event';
+
+export default class ColsVisibility{
+
+ /**
+ * Columns Visibility extension
+ * @param {Object} tf TableFilter instance
+ * @param {Object} f Config
+ */
+ constructor(tf, f){
+
+ // Configuration object
+ var cfg = tf.config();
+
+ this.initialized = false;
+ this.name = f.name;
+ this.desc = f.description || 'Columns visibility manager';
+
+ //show/hide cols span element
+ this.spanEl = null;
+ //show/hide cols button element
+ this.btnEl = null;
+ //show/hide cols container div element
+ this.contEl = null;
+
+ //tick to hide or show column
+ this.tickToHide = f.tick_to_hide===false ? false : true;
+ //enables/disables cols manager generation
+ this.manager = f.manager===false ? false : true;
+ //only if external headers
+ this.headersTbl = f.headers_table || false;
+ //only if external headers
+ this.headersIndex = f.headers_index || 1;
+ //id of container element
+ this.contElTgtId = f.container_target_id || null;
+ //alternative headers text
+ this.headersText = f.headers_text || null;
+ //id of button container element
+ this.btnTgtId = f.btn_target_id || null;
+ //defines show/hide cols text
+ this.btnText = f.btn_text || 'Columns▼';
+ //defines show/hide cols button innerHtml
+ this.btnHtml = f.btn_html || null;
+ //defines css class for show/hide cols button
+ this.btnCssClass = f.btn_css_class || 'colVis';
+ //defines close link text
+ this.btnCloseText = f.btn_close_text || 'Close';
+ //defines close button innerHtml
+ this.btnCloseHtml = f.btn_close_html || null;
+ //defines css class for close button
+ this.btnCloseCssClass = f.btn_close_css_class || this.btnCssClass;
+ this.stylesheet = f.stylesheet || 'colsVisibility.css';
+ //span containing show/hide cols button
+ this.prfx = 'colVis_';
+ //defines css class span containing show/hide cols
+ this.spanCssClass = f.span_css_class || 'colVisSpan';
+ this.prfxCont = this.prfx + 'Cont_';
+ //defines css class div containing show/hide cols
+ this.contCssClass = f.cont_css_class || 'colVisCont';
+ //defines css class for cols list (ul)
+ this.listCssClass = cfg.list_css_class ||'cols_checklist';
+ //defines css class for list item (li)
+ this.listItemCssClass = cfg.checklist_item_css_class ||
+ 'cols_checklist_item';
+ //defines css class for selected list item (li)
+ this.listSlcItemCssClass = cfg.checklist_selected_item_css_class ||
+ 'cols_checklist_slc_item';
+ //text preceding columns list
+ this.text = f.text || (this.tickToHide ? 'Hide: ' : 'Show: ');
+ this.atStart = f.at_start || null;
+ this.enableHover = Boolean(f.enable_hover);
+ //enables select all option
+ this.enableTickAll = Boolean(f.enable_tick_all);
+ //text preceding columns list
+ this.tickAllText = f.tick_all_text || 'Select all:';
+
+ //array containing hidden columns indexes
+ this.hiddenCols = [];
+ this.tblHasColTag = (Dom.tag(tf.tbl,'col').length > 0);
+
+ //callback invoked just after cols manager is loaded
+ this.onLoaded = Types.isFn(f.on_loaded) ? f.on_loaded : null;
+ //calls function before cols manager is opened
+ this.onBeforeOpen = Types.isFn(f.on_before_open) ?
+ f.on_before_open : null;
+ //calls function after cols manager is opened
+ this.onAfterOpen = Types.isFn(f.on_after_open) ? f.on_after_open : null;
+ //calls function before cols manager is closed
+ this.onBeforeClose = Types.isFn(f.on_before_close) ?
+ f.on_before_close : null;
+ //calls function after cols manager is closed
+ this.onAfterClose = Types.isFn(f.on_after_close) ?
+ f.on_after_close : null;
+
+ //callback before col is hidden
+ this.onBeforeColHidden = Types.isFn(f.on_before_col_hidden) ?
+ f.on_before_col_hidden : null;
+ //callback after col is hidden
+ this.onAfterColHidden = Types.isFn(f.on_after_col_hidden) ?
+ f.on_after_col_hidden : null;
+ //callback before col is displayed
+ this.onBeforeColDisplayed = Types.isFn(f.on_before_col_displayed) ?
+ f.on_before_col_displayed : null;
+ //callback after col is displayed
+ this.onAfterColDisplayed = Types.isFn(f.on_after_col_displayed) ?
+ f.on_after_col_displayed : null;
+
+ //Grid layout compatibility
+ if(tf.gridLayout){
+ this.headersTbl = tf.feature('gridLayout').headTbl; //headers table
+ this.headersIndex = 0; //headers index
+ this.onAfterColDisplayed = function(){};
+ this.onAfterColHidden = function(){};
+ }
+
+ //Loads extension stylesheet
+ tf.import(f.name+'Style', tf.stylePath + this.stylesheet, null, 'link');
+
+ this.tf = tf;
+ }
+
+ toggle(){
+ var contDisplay = this.contEl.style.display;
+ var onBeforeOpen = this.onBeforeOpen;
+ var onBeforeClose = this.onBeforeClose;
+ var onAfterOpen = this.onAfterOpen;
+ var onAfterClose = this.onAfterClose;
+
+ if(onBeforeOpen && contDisplay !== 'inline'){
+ onBeforeOpen.call(null, this);
+ }
+ if(onBeforeClose && contDisplay === 'inline'){
+ onBeforeClose.call(null, this);
+ }
+
+ this.contEl.style.display = contDisplay === 'inline' ?
+ 'none' : 'inline';
+
+ if(onAfterOpen && contDisplay !== 'inline'){
+ onAfterOpen.call(null, this);
+ }
+ if(onAfterClose && contDisplay === 'inline'){
+ onAfterClose.call(null, this);
+ }
+ }
+
+ checkItem(lbl){
+ var li = lbl.parentNode;
+ if(!li || !lbl){
+ return;
+ }
+ var isChecked = lbl.firstChild.checked;
+ var colIndex = lbl.firstChild.getAttribute('id').split('_')[1];
+ colIndex = parseInt(colIndex, 10);
+ if(isChecked){
+ Dom.addClass(li, this.listSlcItemCssClass);
+ } else {
+ Dom.removeClass(li, this.listSlcItemCssClass);
+ }
+
+ var hide = false;
+ if((this.tickToHide && isChecked) || (!this.tickToHide && !isChecked)){
+ hide = true;
+ }
+ this.setHidden(colIndex, hide);
+ }
+
+ init(){
+ if(!this.manager){
+ return;
+ }
+ this.buildBtn();
+ this.buildManager();
+
+ this.initialized = true;
+ }
+
+ /**
+ * Build main button UI
+ */
+ buildBtn(){
+ if(this.btnEl){
+ return;
+ }
+ var tf = this.tf;
+ var span = Dom.create('span', ['id', this.prfx+tf.id]);
+ span.className = this.spanCssClass;
+
+ //Container element (rdiv or custom element)
+ if(!this.btnTgtId){
+ tf.setToolbar();
+ }
+ var targetEl = !this.btnTgtId ? tf.rDiv : Dom.id(this.btnTgtId);
+
+ if(!this.btnTgtId){
+ var firstChild = targetEl.firstChild;
+ firstChild.parentNode.insertBefore(span, firstChild);
+ } else {
+ targetEl.appendChild(span);
+ }
+
+ if(!this.btnHtml){
+ var btn = Dom.create('a', ['href','javascript:;']);
+ btn.className = this.btnCssClass;
+ btn.title = this.desc;
+
+ btn.innerHTML = this.btnText;
+ span.appendChild(btn);
+ if(!this.enableHover){
+ Event.add(btn, 'click', (evt)=> { this.toggle(evt); });
+ } else {
+ Event.add(btn, 'mouseover', (evt)=> { this.toggle(evt); });
+ }
+ } else { //Custom html
+ span.innerHTML = this.btnHtml;
+ var colVisEl = span.firstChild;
+ if(!this.enableHover){
+ Event.add(colVisEl, 'click', (evt)=> { this.toggle(evt); });
+ } else {
+ Event.add(colVisEl, 'mouseover', (evt)=> { this.toggle(evt); });
+ }
+ }
+
+ this.spanEl = span;
+ this.btnEl = this.spanEl.firstChild;
+
+ if(this.onLoaded){
+ this.onLoaded.call(null, this);
+ }
+ }
+
+ /**
+ * Build columns manager UI
+ */
+ buildManager(){
+ var tf = this.tf;
+
+ var container = !this.contElTgtId ?
+ Dom.create('div', ['id', this.prfxCont+tf.id]) :
+ Dom.id(this.contElTgtId);
+ container.className = this.contCssClass;
+
+ //Extension description
+ var extNameLabel = Dom.create('p');
+ extNameLabel.innerHTML = this.text;
+ container.appendChild(extNameLabel);
+
+ //Headers list
+ var ul = Dom.create('ul' ,['id', 'ul'+this.name+'_'+tf.id]);
+ ul.className = this.listCssClass;
+
+ var tbl = this.headersTbl ? this.headersTbl : tf.tbl;
+ var headerIndex = this.headersTbl ?
+ this.headersIndex : tf.getHeadersRowIndex();
+ var headerRow = tbl.rows[headerIndex];
+
+ //Tick all option
+ if(this.enableTickAll){
+ var li = Dom.createCheckItem(
+ 'col__'+tf.id, this.tickAllText, this.tickAllText);
+ Dom.addClass(li, this.listItemCssClass);
+ ul.appendChild(li);
+ li.check.checked = !this.tickToHide;
+
+ Event.add(li.check, 'click', ()=> {
+ for(var h = 0; h < headerRow.cells.length; h++){
+ var itm = Dom.id('col_'+h+'_'+tf.id);
+ if(itm && li.check.checked !== itm.checked){
+ itm.click();
+ itm.checked = li.check.checked;
+ }
+ }
+ });
+ }
+
+ for(var i = 0; i < headerRow.cells.length; i++){
+ var cell = headerRow.cells[i];
+ var cellText = this.headersText && this.headersText[i] ?
+ this.headersText[i] : this._getHeaderText(cell);
+ var liElm = Dom.createCheckItem(
+ 'col_'+i+'_'+tf.id, cellText, cellText);
+ Dom.addClass(liElm, this.listItemCssClass);
+ if(!this.tickToHide){
+ Dom.addClass(liElm, this.listSlcItemCssClass);
+ }
+ ul.appendChild(liElm);
+ if(!this.tickToHide){
+ liElm.check.checked = true;
+ }
+
+ Event.add(liElm.check, 'click', (evt)=> {
+ var elm = Event.target(evt);
+ var lbl = elm.parentNode;
+ this.checkItem(lbl);
+ });
+ }
+
+ //separator
+ var p = Dom.create('p', ['align','center']);
+ var btn;
+ //Close link
+ if(!this.btnCloseHtml){
+ btn = Dom.create('a', ['href','javascript:;']);
+ btn.className = this.btnCloseCssClass;
+ btn.innerHTML = this.btnCloseText;
+ Event.add(btn, 'click', (evt)=> { this.toggle(evt); });
+ p.appendChild(btn);
+ } else {
+ p.innerHTML = this.btnCloseHtml;
+ btn = p.firstChild;
+ Event.add(btn, 'click', (evt)=> { this.toggle(evt); });
+ }
+
+ container.appendChild(ul);
+ container.appendChild(p);
+
+ this.btnEl.parentNode.insertBefore(container, this.btnEl);
+ this.contEl = container;
+
+ if(this.atStart){
+ var a = this.atStart;
+ for(var k=0; k<a.length; k++){
+ var itm = Dom.id('col_'+a[k]+'_'+tf.id);
+ if(itm){
+ itm.click();
+ }
+ }
+ }
+ }
+
+ /**
+ * Hide or show specified columns
+ * @param {Numner} colIndex Column index
+ * @param {Boolean} hide hide column if true or show if false
+ */
+ setHidden(colIndex, hide){
+ var tf = this.tf;
+ var tbl = tf.tbl;
+
+ if(this.onBeforeColHidden && hide){
+ this.onBeforeColHidden.call(null, this, colIndex);
+ }
+ if(this.onBeforeColDisplayed && !hide){
+ this.onBeforeColDisplayed.call(null, this, colIndex);
+ }
+
+ this._hideCells(tbl, colIndex, hide);
+ if(this.headersTbl){
+ this._hideCells(this.headersTbl, colIndex, hide);
+ }
+
+ var hiddenCols = this.hiddenCols;
+ var itemIndex = hiddenCols.indexOf(colIndex);
+ if(hide){
+ if(itemIndex === -1){
+ this.hiddenCols.push(colIndex);
+ }
+ } else {
+ if(itemIndex !== -1){
+ this.hiddenCols.splice(itemIndex, 1);
+ }
+ }
+
+ var gridLayout;
+ var headTbl;
+ var gridColElms;
+ if(this.onAfterColHidden && hide){
+ //This event is fired just after a column is displayed for
+ //grid_layout support
+ //TODO: grid layout module should be responsible for those
+ //calculations
+ if(tf.gridLayout){
+ gridLayout = tf.feature('gridLayout');
+ headTbl = gridLayout.headTbl;
+ gridColElms = gridLayout.gridColElms;
+ var hiddenWidth = parseInt(
+ gridColElms[colIndex].style.width, 10);
+
+ var headTblW = parseInt(headTbl.style.width, 10);
+ headTbl.style.width = headTblW - hiddenWidth + 'px';
+ tbl.style.width = headTbl.style.width;
+ }
+ this.onAfterColHidden.call(null, this, colIndex);
+ }
+
+ if(this.onAfterColDisplayed && !hide){
+ //This event is fired just after a column is displayed for
+ //grid_layout support
+ //TODO: grid layout module should be responsible for those
+ //calculations
+ if(tf.gridLayout){
+ gridLayout = tf.feature('gridLayout');
+ headTbl = gridLayout.headTbl;
+ gridColElms = gridLayout.gridColElms;
+ var width = parseInt(gridColElms[colIndex].style.width, 10);
+ headTbl.style.width =
+ (parseInt(headTbl.style.width, 10) + width) + 'px';
+ tf.tbl.style.width = headTbl.style.width;
+ }
+ this.onAfterColDisplayed.call(null, this, colIndex);
+ }
+ }
+
+ /**
+ * Show specified column
+ * @param {Number} colIndex Column index
+ */
+ showCol(colIndex){
+ if(colIndex === undefined || !this.isColHidden(colIndex)){
+ return;
+ }
+ if(this.manager && this.contEl){
+ var itm = Dom.id('col_'+colIndex+'_'+this.tf.id);
+ if(itm){ itm.click(); }
+ } else {
+ this.setHidden(colIndex, false);
+ }
+ }
+
+ /**
+ * Hide specified column
+ * @param {Number} colIndex Column index
+ */
+ hideCol(colIndex){
+ if(colIndex === undefined || this.isColHidden(colIndex)){
+ return;
+ }
+ if(this.manager && this.contEl){
+ var itm = Dom.id('col_'+colIndex+'_'+this.tf.id);
+ if(itm){ itm.click(); }
+ } else {
+ this.setHidden(colIndex, true);
+ }
+ }
+
+ /**
+ * Determine if specified column is hidden
+ * @param {Number} colIndex Column index
+ */
+ isColHidden(colIndex){
+ if(this.hiddenCols.indexOf(colIndex) !== -1){
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Toggle visibility of specified column
+ * @param {Number} colIndex Column index
+ */
+ toggleCol(colIndex){
+ if(colIndex === undefined || this.isColHidden(colIndex)){
+ this.showCol(colIndex);
+ } else {
+ this.hideCol(colIndex);
+ }
+ }
+
+ /**
+ * Returns the indexes of the columns currently hidden
+ * @return {Array} column indexes
+ */
+ getHiddenCols(){
+ return this.hiddenCols;
+ }
+
+ /**
+ * Remove the columns manager
+ */
+ destroy(){
+ if(!this.btnEl && !this.contEl){
+ return;
+ }
+ if(Dom.id(this.contElTgtId)){
+ Dom.id(this.contElTgtId).innerHTML = '';
+ } else {
+ this.contEl.innerHTML = '';
+ this.contEl.parentNode.removeChild(this.contEl);
+ this.contEl = null;
+ }
+ this.btnEl.innerHTML = '';
+ this.btnEl.parentNode.removeChild(this.btnEl);
+ this.btnEl = null;
+ this.initialized = false;
+ }
+
+ _getHeaderText(cell){
+ if(!cell.hasChildNodes){
+ return '';
+ }
+
+ for(var i=0; i<cell.childNodes.length; i++){
+ var n = cell.childNodes[i];
+ if(n.nodeType === 3){
+ return n.nodeValue;
+ } else if(n.nodeType === 1){
+ if(n.id && n.id.indexOf('popUp') !== -1){
+ continue;
+ } else {
+ return Dom.getText(n);
+ }
+ }
+ continue;
+ }
+ return '';
+ }
+
+ _hideCells(tbl, colIndex, hide){
+ for(var i=0; i<tbl.rows.length; i++){
+ var row = tbl.rows[i];
+ var cell = row.cells[colIndex];
+ if(cell){
+ cell.style.display = hide ? 'none' : '';
+ }
+ }
+ }
+
+}
--- /dev/null
+import Dom from '../../dom';
+import Types from '../../types';
+import Event from '../../event';
+
+export default class FiltersVisibility{
+
+ /**
+ * Filters Row Visibility extension
+ * @param {Object} tf TableFilter instance
+ * @param {Object} f Config
+ */
+ constructor(tf, f){
+
+ this.initialized = false;
+ this.name = f.name;
+ this.desc = f.description || 'Filters row visibility manager';
+
+ // Path and image filenames
+ this.stylesheet = f.stylesheet || 'filtersVisibility.css';
+ this.icnExpand = f.expand_icon_name || 'icn_exp.png';
+ this.icnCollapse = f.collapse_icon_name || 'icn_clp.png';
+
+ //expand/collapse filters span element
+ this.contEl = null;
+ //expand/collapse filters btn element
+ this.btnEl = null;
+
+ this.icnExpandHtml = '<img src="'+ tf.themesPath + this.icnExpand +
+ '" alt="Expand filters" >';
+ this.icnCollapseHtml = '<img src="'+ tf.themesPath + this.icnCollapse +
+ '" alt="Collapse filters" >';
+ this.defaultText = 'Toggle filters';
+
+ //id of container element
+ this.targetId = f.target_id || null;
+ //enables/disables expand/collapse icon
+ this.enableIcon = f.enable_icon===false ? false : true;
+ this.btnText = f.btn_text || '';
+
+ //defines expand/collapse filters text
+ this.collapseBtnHtml = this.enableIcon ?
+ this.icnCollapseHtml + this.btnText :
+ this.btnText || this.defaultText;
+ this.expandBtnHtml = this.enableIcon ?
+ this.icnExpandHtml + this.btnText :
+ this.btnText || this.defaultText;
+
+ //defines expand/collapse filters button innerHtml
+ this.btnHtml = f.btn_html || null;
+ //defines css class for expand/collapse filters button
+ this.btnCssClass = f.btn_css_class || 'btnExpClpFlt';
+ //defines css class span containing expand/collapse filters
+ this.contCssClass = f.cont_css_class || 'expClpFlt';
+ this.filtersRowIndex = !Types.isUndef(f.filters_row_index) ?
+ f.filters_row_index : tf.getFiltersRowIndex();
+
+ this.visibleAtStart = !Types.isUndef(f.visible_at_start) ?
+ Boolean(f.visible_at_start) : true;
+
+ // Prefix
+ this.prfx = 'fltsVis_';
+
+ //callback before filters row is shown
+ this.onBeforeShow = Types.isFn(f.on_before_show) ?
+ f.on_before_show : null;
+ //callback after filters row is shown
+ this.onAfterShow = Types.isFn(f.on_after_show) ?
+ f.on_after_show : null;
+ //callback before filters row is hidden
+ this.onBeforeHide = Types.isFn(f.on_before_hide) ?
+ f.on_before_hide : null;
+ //callback after filters row is hidden
+ this.onAfterHide = Types.isFn(f.on_after_hide) ? f.on_after_hide : null;
+
+ //Loads extension stylesheet
+ tf.import(f.name+'Style', tf.stylePath + this.stylesheet, null, 'link');
+
+ this.tf = tf;
+ }
+
+ /**
+ * Initialise extension
+ */
+ init(){
+ if(this.initialized){
+ return;
+ }
+
+ this.buildUI();
+ this.initialized = true;
+ }
+
+ /**
+ * Build UI elements
+ */
+ buildUI(){
+ let tf = this.tf;
+ let span = Dom.create('span',['id', this.prfx+tf.id]);
+ span.className = this.contCssClass;
+
+ //Container element (rdiv or custom element)
+ if(!this.targetId){
+ tf.setToolbar();
+ }
+ let targetEl = !this.targetId ? tf.rDiv : Dom.id(this.targetId);
+
+ if(!this.targetId){
+ let firstChild = targetEl.firstChild;
+ firstChild.parentNode.insertBefore(span, firstChild);
+ } else {
+ targetEl.appendChild(span);
+ }
+
+ let btn;
+ if(!this.btnHtml){
+ btn = Dom.create('a', ['href', 'javascript:void(0);']);
+ btn.className = this.btnCssClass;
+ btn.title = this.btnText || this.defaultText;
+ btn.innerHTML = this.collapseBtnHtml;
+ span.appendChild(btn);
+ } else { //Custom html
+ span.innerHTML = this.btnHtml;
+ btn = span.firstChild;
+ }
+
+ Event.add(btn, 'click', ()=> this.toggle());
+
+ this.contEl = span;
+ this.btnEl = btn;
+
+ if(!this.visibleAtStart){
+ this.toggle();
+ }
+ }
+
+ /**
+ * Toggle filters visibility
+ */
+ toggle(){
+ let tf = this.tf;
+ let tbl = tf.gridLayout? tf.feature('gridLayout').headTbl : tf.tbl;
+ let fltRow = tbl.rows[this.filtersRowIndex];
+ let fltRowDisplay = fltRow.style.display;
+
+ if(this.onBeforeShow && fltRowDisplay !== ''){
+ this.onBeforeShow.call(this, this);
+ }
+ if(this.onBeforeHide && fltRowDisplay === ''){
+ this.onBeforeHide.call(null, this);
+ }
+
+ fltRow.style.display = fltRowDisplay==='' ? 'none' : '';
+ if(this.enableIcon && !this.btnHtml){
+ this.btnEl.innerHTML = fltRowDisplay === '' ?
+ this.expandBtnHtml : this.collapseBtnHtml;
+ }
+
+ if(this.onAfterShow && fltRowDisplay !== ''){
+ this.onAfterShow.call(null, this);
+ }
+ if(this.onAfterHide && fltRowDisplay === ''){
+ this.onAfterHide.call(null, this);
+ }
+ }
+
+ /**
+ * Destroy the UI
+ */
+ destroy(){
+ if(!this.btnEl && !this.contEl){
+ return;
+ }
+
+ this.btnEl.innerHTML = '';
+ this.btnEl.parentNode.removeChild(this.btnEl);
+ this.btnEl = null;
+
+ this.contEl.innerHTML = '';
+ this.contEl.parentNode.removeChild(this.contEl);
+ this.contEl = null;
+ this.initialized = false;
+ }
+
+}
--- /dev/null
+import Types from '../../types';
+import Dom from '../../dom';
+import Event from '../../event';
+import DateHelper from '../../date';
+import Helpers from '../../helpers';
+
+export default class AdapterSortableTable{
+
+ /**
+ * SortableTable Adapter module
+ * @param {Object} tf TableFilter instance
+ */
+ constructor(tf, opts){
+ this.initialized = false;
+ this.name = opts.name;
+ this.desc = opts.description || 'Sortable table';
+
+ //indicates if paging is enabled
+ this.isPaged = false;
+
+ //indicates if tables was sorted
+ this.sorted = false;
+
+ this.sortTypes = Types.isArray(opts.types) ? opts.types : [];
+ this.sortColAtStart = Types.isArray(opts.sort_col_at_start) ?
+ opts.sort_col_at_start : null;
+ this.asyncSort = Boolean(opts.async_sort);
+ this.triggerIds = Types.isArray(opts.trigger_ids) ?
+ opts.trigger_ids : [];
+
+ // edit .sort-arrow.descending / .sort-arrow.ascending in
+ // tablefilter.css to reflect any path change
+ this.imgPath = opts.images_path || tf.themesPath;
+ this.imgBlank = opts.image_blank || 'blank.png';
+ this.imgClassName = opts.image_class_name || 'sort-arrow';
+ this.imgAscClassName = opts.image_asc_class_name || 'ascending';
+ this.imgDescClassName = opts.image_desc_class_name ||'descending';
+ //cell attribute storing custom key
+ this.customKey = opts.custom_key || 'data-tf-sortKey';
+
+ /*** TF additional events ***/
+ //additional paging events for alternating background
+ // o.Evt._Paging.nextEvt = function(){
+ // if(o.sorted && o.alternateRows) o.Filter();
+ // }
+ // o.Evt._Paging.prevEvt = o.Evt._Paging.nextEvt;
+ // o.Evt._Paging.firstEvt = o.Evt._Paging.nextEvt;
+ // o.Evt._Paging.lastEvt = o.Evt._Paging.nextEvt;
+ // o.Evt._OnSlcPagesChangeEvt = o.Evt._Paging.nextEvt;
+
+ // callback invoked after sort is loaded and instanciated
+ this.onSortLoaded = Types.isFn(opts.on_sort_loaded) ?
+ opts.on_sort_loaded : null;
+ // callback invoked before table is sorted
+ this.onBeforeSort = Types.isFn(opts.on_before_sort) ?
+ opts.on_before_sort : null;
+ // callback invoked after table is sorted
+ this.onAfterSort = Types.isFn(opts.on_after_sort) ?
+ opts.on_after_sort : null;
+
+ this.tf = tf;
+ }
+
+ init(){
+ let tf = this.tf;
+ let adpt = this;
+
+ // SortableTable class sanity check (sortabletable.js)
+ if(Types.isUndef(SortableTable)){
+ throw new Error('SortableTable class not found.');
+ }
+
+ this.overrideSortableTable();
+ this.setSortTypes();
+
+ //Column sort at start
+ let sortColAtStart = adpt.sortColAtStart;
+ if(sortColAtStart){
+ this.stt.sort(sortColAtStart[0], sortColAtStart[1]);
+ }
+
+ if(this.onSortLoaded){
+ this.onSortLoaded.call(null, tf, this);
+ }
+
+ /*** SortableTable callbacks ***/
+ this.stt.onbeforesort = function(){
+ if(adpt.onBeforeSort){
+ adpt.onBeforeSort.call(null, tf, adpt.stt.sortColumn);
+ }
+
+ /*** sort behaviour for paging ***/
+ if(tf.paging){
+ adpt.isPaged = true;
+ tf.paging = false;
+ tf.feature('paging').destroy();
+ }
+ };
+
+ this.stt.onsort = function(){
+ adpt.sorted = true;
+
+ //rows alternating bg issue
+ // TODO: move into AlternateRows component
+ if(tf.alternateRows){
+ let rows = tf.tbl.rows, c = 0;
+
+ let setClass = function(row, i, removeOnly){
+ if(Types.isUndef(removeOnly)){
+ removeOnly = false;
+ }
+ let altRows = tf.feature('alternateRows'),
+ oddCls = altRows.oddCss,
+ evenCls = altRows.evenCss;
+ Dom.removeClass(row, oddCls);
+ Dom.removeClass(row, evenCls);
+
+ if(!removeOnly){
+ Dom.addClass(row, i % 2 ? oddCls : evenCls);
+ }
+ };
+
+ for (let i = tf.refRow; i < tf.nbRows; i++){
+ let isRowValid = rows[i].getAttribute('validRow');
+ if(tf.paging && rows[i].style.display === ''){
+ setClass(rows[i], c);
+ c++;
+ } else {
+ if((isRowValid==='true' || isRowValid===null) &&
+ rows[i].style.display === ''){
+ setClass(rows[i], c);
+ c++;
+ } else {
+ setClass(rows[i], c, true);
+ }
+ }
+ }
+ }
+ //sort behaviour for paging
+ if(adpt.isPaged){
+ let paginator = tf.feature('paging');
+ paginator.reset(false);
+ paginator.setPage(paginator.getPage());
+ adpt.isPaged = false;
+ }
+
+ if(adpt.onAfterSort){
+ adpt.onAfterSort.call(null, tf, adpt.stt.sortColumn);
+ }
+ };
+
+ this.initialized = true;
+ }
+
+ /**
+ * Sort specified column
+ * @param {Number} colIdx Column index
+ * @param {Boolean} desc Optional: descending manner
+ */
+ sortByColumnIndex(colIdx, desc){
+ this.stt.sort(colIdx, desc);
+ }
+
+ overrideSortableTable(){
+ let adpt = this,
+ tf = this.tf;
+
+ /**
+ * Overrides headerOnclick method in order to handle th event
+ * @param {Object} e [description]
+ */
+ SortableTable.prototype.headerOnclick = function(evt){
+ if(!adpt.initialized){
+ return;
+ }
+
+ // find Header element
+ let el = evt.target || evt.srcElement;
+
+ while(el.tagName !== 'TD' && el.tagName !== 'TH'){
+ el = el.parentNode;
+ }
+
+ this.sort(
+ SortableTable.msie ?
+ SortableTable.getCellIndex(el) : el.cellIndex
+ );
+ };
+
+ /**
+ * Overrides getCellIndex IE returns wrong cellIndex when columns are
+ * hidden
+ * @param {Object} oTd TD element
+ * @return {Number} Cell index
+ */
+ SortableTable.getCellIndex = function(oTd){
+ let cells = oTd.parentNode.cells,
+ l = cells.length, i;
+ for (i = 0; cells[i] != oTd && i < l; i++){}
+ return i;
+ };
+
+ /**
+ * Overrides initHeader in order to handle filters row position
+ * @param {Array} oSortTypes
+ */
+ SortableTable.prototype.initHeader = function(oSortTypes){
+ let stt = this;
+ if (!stt.tHead){
+ if(tf.gridLayout){
+ stt.tHead = tf.feature('gridLayout').headTbl.tHead;
+ } else {
+ return;
+ }
+ }
+
+ stt.headersRow = tf.headersRow;
+ let cells = stt.tHead.rows[stt.headersRow].cells;
+ stt.sortTypes = oSortTypes || [];
+ let l = cells.length;
+ let img, c;
+
+ for (let i = 0; i < l; i++) {
+ c = cells[i];
+ if (stt.sortTypes[i] !== null && stt.sortTypes[i] !== 'None'){
+ c.style.cursor = 'pointer';
+ img = Dom.create('img',
+ ['src', adpt.imgPath + adpt.imgBlank]);
+ c.appendChild(img);
+ if (stt.sortTypes[i] !== null){
+ c.setAttribute( '_sortType', stt.sortTypes[i]);
+ }
+ Event.add(c, 'click', stt._headerOnclick);
+ } else {
+ c.setAttribute('_sortType', oSortTypes[i]);
+ c._sortType = 'None';
+ }
+ }
+ stt.updateHeaderArrows();
+ };
+
+ /**
+ * Overrides updateHeaderArrows in order to handle arrows indicators
+ */
+ SortableTable.prototype.updateHeaderArrows = function(){
+ let stt = this;
+ let cells, l, img;
+
+ // external headers
+ if(adpt.asyncSort && adpt.triggerIds.length > 0){
+ let triggers = adpt.triggerIds;
+ cells = [];
+ l = triggers.length;
+ for(let j=0; j<triggers.length; j++){
+ cells.push(Dom.id(triggers[j]));
+ }
+ } else {
+ if(!this.tHead){
+ return;
+ }
+ cells = stt.tHead.rows[stt.headersRow].cells;
+ l = cells.length;
+ }
+ for(let i = 0; i < l; i++){
+ let cellAttr = cells[i].getAttribute('_sortType');
+ if(cellAttr !== null && cellAttr !== 'None'){
+ img = cells[i].lastChild || cells[i];
+ if(img.nodeName.toLowerCase() !== 'img'){
+ img = Dom.create('img',
+ ['src', adpt.imgPath + adpt.imgBlank]);
+ cells[i].appendChild(img);
+ }
+ if (i === stt.sortColumn){
+ img.className = adpt.imgClassName +' '+
+ (this.descending ?
+ adpt.imgDescClassName :
+ adpt.imgAscClassName);
+ } else{
+ img.className = adpt.imgClassName;
+ }
+ }
+ }
+ };
+
+ /**
+ * Overrides getRowValue for custom key value feature
+ * @param {Object} oRow Row element
+ * @param {String} sType
+ * @param {Number} nColumn
+ * @return {String}
+ */
+ SortableTable.prototype.getRowValue = function(oRow, sType, nColumn){
+ let stt = this;
+ // if we have defined a custom getRowValue use that
+ let sortTypeInfo = stt._sortTypeInfo[sType];
+ if (sortTypeInfo && sortTypeInfo.getRowValue){
+ return sortTypeInfo.getRowValue(oRow, nColumn);
+ }
+ let c = oRow.cells[nColumn];
+ let s = SortableTable.getInnerText(c);
+ return stt.getValueFromString(s, sType);
+ };
+
+ /**
+ * Overrides getInnerText in order to avoid Firefox unexpected sorting
+ * behaviour with untrimmed text elements
+ * @param {Object} oNode DOM element
+ * @return {String} DOM element inner text
+ */
+ SortableTable.getInnerText = function(oNode){
+ if(!oNode){
+ return;
+ }
+ if(oNode.getAttribute(adpt.customKey)){
+ return oNode.getAttribute(adpt.customKey);
+ } else {
+ return Dom.getText(oNode);
+ }
+ };
+ }
+
+ addSortType(){
+ var args = arguments;
+ SortableTable.prototype.addSortType(args[0], args[1], args[2], args[3]);
+ }
+
+ setSortTypes(){
+ let tf = this.tf,
+ sortTypes = this.sortTypes,
+ _sortTypes = [];
+
+ for(let i=0; i<tf.nbCells; i++){
+ let colType;
+
+ if(sortTypes[i]){
+ colType = sortTypes[i].toLowerCase();
+ if(colType === 'none'){
+ colType = 'None';
+ }
+ } else { // resolve column types
+ if(tf.hasColNbFormat && tf.colNbFormat[i] !== null){
+ colType = tf.colNbFormat[i].toLowerCase();
+ } else if(tf.hasColDateType && tf.colDateType[i] !== null){
+ colType = tf.colDateType[i].toLowerCase()+'date';
+ } else {
+ colType = 'String';
+ }
+ }
+ _sortTypes.push(colType);
+ }
+
+ //Public TF method to add sort type
+
+ //Custom sort types
+ this.addSortType('number', Number);
+ this.addSortType('caseinsensitivestring', SortableTable.toUpperCase);
+ this.addSortType('date', SortableTable.toDate);
+ this.addSortType('string');
+ this.addSortType('us', usNumberConverter);
+ this.addSortType('eu', euNumberConverter);
+ this.addSortType('dmydate', dmyDateConverter );
+ this.addSortType('ymddate', ymdDateConverter);
+ this.addSortType('mdydate', mdyDateConverter);
+ this.addSortType('ddmmmyyyydate', ddmmmyyyyDateConverter);
+ this.addSortType('ipaddress', ipAddress, sortIP);
+
+ this.stt = new SortableTable(tf.tbl, _sortTypes);
+
+ /*** external table headers adapter ***/
+ if(this.asyncSort && this.triggerIds.length > 0){
+ let triggers = this.triggerIds;
+ for(let j=0; j<triggers.length; j++){
+ if(triggers[j] === null){
+ continue;
+ }
+ let trigger = Dom.id(triggers[j]);
+ if(trigger){
+ trigger.style.cursor = 'pointer';
+
+ Event.add(trigger, 'click', (evt) => {
+ let elm = evt.target;
+ if(!this.tf.sort){
+ return;
+ }
+ this.stt.asyncSort(triggers.indexOf(elm.id));
+ });
+ trigger.setAttribute('_sortType', _sortTypes[j]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Destroy sort
+ */
+ destroy(){
+ let tf = this.tf;
+ this.sorted = false;
+ this.initialized = false;
+ this.stt.destroy();
+
+ let ids = tf.getFiltersId();
+ for (let idx = 0; idx < ids.length; idx++){
+ let header = tf.getHeaderElement(idx);
+ let img = Dom.tag(header, 'img');
+
+ if(img.length === 1){
+ header.removeChild(img[0]);
+ }
+ }
+ }
+
+}
+
+//Converters
+function usNumberConverter(s){
+ return Helpers.removeNbFormat(s, 'us');
+}
+function euNumberConverter(s){
+ return Helpers.removeNbFormat(s, 'eu');
+}
+function dateConverter(s, format){
+ return DateHelper.format(s, format);
+}
+function dmyDateConverter(s){
+ return dateConverter(s, 'DMY');
+}
+function mdyDateConverter(s){
+ return dateConverter(s, 'MDY');
+}
+function ymdDateConverter(s){
+ return dateConverter(s, 'YMD');
+}
+function ddmmmyyyyDateConverter(s){
+ return dateConverter(s, 'DDMMMYYYY');
+}
+
+function ipAddress(value){
+ let vals = value.split('.');
+ for (let x in vals) {
+ let val = vals[x];
+ while (3 > val.length){
+ val = '0'+val;
+ }
+ vals[x] = val;
+ }
+ return vals.join('.');
+}
+
+function sortIP(a,b){
+ let aa = ipAddress(a.value.toLowerCase());
+ let bb = ipAddress(b.value.toLowerCase());
+ if (aa==bb){
+ return 0;
+ } else if (aa<bb){
+ return -1;
+ } else {
+ return 1;
+ }
+}
--- /dev/null
+// import 'script!sortabletable';
+import AdapterSortableTable from './adapterSortabletable';
+
+if(!window.SortableTable){
+ require('script!sortabletable');
+}
+
+export default AdapterSortableTable;
--- /dev/null
+/**
+ * Misc helpers
+ */
+
+import Str from './string';
+
+export default {
+ removeNbFormat(data, format){
+ if(!data){
+ return;
+ }
+ if(!format){
+ format = 'us';
+ }
+ let n = data;
+ if(Str.lower(format) === 'us'){
+ n =+ n.replace(/[^\d\.-]/g,'');
+ } else {
+ n =+ n.replace(/[^\d\,-]/g,'').replace(',','.');
+ }
+ return n;
+ }
+};
--- /dev/null
+import {Feature} from './feature';
+import Dom from '../dom';
+
+export class AlternateRows extends Feature {
+
+ /**
+ * Alternating rows color
+ * @param {Object} tf TableFilter instance
+ */
+ constructor(tf) {
+ super(tf, 'alternateRows');
+
+ var config = this.config;
+ //defines css class for even rows
+ this.evenCss = config.even_row_css_class || 'even';
+ //defines css class for odd rows
+ this.oddCss = config.odd_row_css_class || 'odd';
+ }
+
+ /**
+ * Sets alternating rows color
+ */
+ init() {
+ if(this.initialized){
+ return;
+ }
+
+ var tf = this.tf;
+ var validRowsIndex = tf.validRowsIndex;
+ var noValidRowsIndex = validRowsIndex===null;
+ //1st index
+ var beginIndex = noValidRowsIndex ? tf.refRow : 0;
+ // nb indexes
+ var indexLen = noValidRowsIndex ?
+ tf.nbFilterableRows+beginIndex :
+ validRowsIndex.length;
+ var idx = 0;
+
+ //alternates bg color
+ for(var j=beginIndex; j<indexLen; j++){
+ var rowIdx = noValidRowsIndex ? j : validRowsIndex[j];
+ this.setRowBg(rowIdx, idx);
+ idx++;
+ }
+ this.initialized = true;
+ }
+
+ /**
+ * Sets row background color
+ * @param {Number} rowIdx Row index
+ * @param {Number} idx Valid rows collection index needed to calculate bg
+ * color
+ */
+ setRowBg(rowIdx, idx) {
+ if(!this.isEnabled() || isNaN(rowIdx)){
+ return;
+ }
+ var rows = this.tf.tbl.rows;
+ var i = isNaN(idx) ? rowIdx : idx;
+ this.removeRowBg(rowIdx);
+
+ Dom.addClass(
+ rows[rowIdx],
+ (i%2) ? this.evenCss : this.oddCss
+ );
+ }
+
+ /**
+ * Removes row background color
+ * @param {Number} idx Row index
+ */
+ removeRowBg(idx) {
+ if(isNaN(idx)){
+ return;
+ }
+ var rows = this.tf.tbl.rows;
+ Dom.removeClass(rows[idx], this.oddCss);
+ Dom.removeClass(rows[idx], this.evenCss);
+ }
+
+ /**
+ * Removes all alternating backgrounds
+ */
+ destroy() {
+ if(!this.initialized){
+ return;
+ }
+ for(var i=this.tf.refRow; i<this.tf.nbRows; i++){
+ this.removeRowBg(i);
+ }
+ this.disable();
+ this.initialized = false;
+ }
+
+}
--- /dev/null
+import Dom from '../dom';
+import Arr from '../array';
+import Str from '../string';
+import Sort from '../sort';
+import Event from '../event';
+
+export class CheckList{
+
+ /**
+ * Checklist UI component
+ * @param {Object} tf TableFilter instance
+ */
+ constructor(tf){
+ // Configuration object
+ var f = tf.config();
+
+ this.checkListDiv = []; //checklist container div
+ //defines css class for div containing checklist filter
+ this.checkListDivCssClass = f.div_checklist_css_class ||
+ 'div_checklist';
+ //defines css class for checklist filters
+ this.checkListCssClass = f.checklist_css_class || 'flt_checklist';
+ //defines css class for checklist item (li)
+ this.checkListItemCssClass = f.checklist_item_css_class ||
+ 'flt_checklist_item';
+ //defines css class for selected checklist item (li)
+ this.checkListSlcItemCssClass = f.checklist_selected_item_css_class ||
+ 'flt_checklist_slc_item';
+ //Load on demand text
+ this.activateCheckListTxt = f.activate_checklist_text ||
+ 'Click to load filter data';
+ //defines css class for checklist filters
+ this.checkListItemDisabledCssClass =
+ f.checklist_item_disabled_css_class ||
+ 'flt_checklist_item_disabled';
+ this.enableCheckListResetFilter =
+ f.enable_checklist_reset_filter===false ? false : true;
+ //checklist filter container div
+ this.prfxCheckListDiv = 'chkdiv_';
+
+ this.isCustom = null;
+ this.opts = null;
+ this.optsTxt = null;
+ this.excludedOpts = null;
+
+ this.tf = tf;
+ }
+
+ // TODO: move event here
+ onChange(evt){
+ let elm = evt.target;
+ this.tf.activeFilterId = elm.getAttribute('id');
+ this.tf.activeFlt = Dom.id(this.tf.activeFilterId);
+ this.tf.Evt.onSlcChange.call(this.tf, evt);
+ }
+
+ optionClick(evt){
+ this.setCheckListValues(evt.target);
+ this.onChange(evt);
+ }
+
+ /**
+ * Build checklist UI asynchronously
+ * @param {Number} colIndex Column index
+ * @param {Boolean} isExternal Render in external container
+ * @param {String} extFltId External container id
+ */
+ build(colIndex, isExternal, extFltId){
+ var tf = this.tf;
+ tf.EvtManager(
+ tf.Evt.name.checklist,
+ { slcIndex:colIndex, slcExternal:isExternal, slcId:extFltId }
+ );
+ }
+
+ /**
+ * Build checklist UI
+ * @param {Number} colIndex Column index
+ * @param {Boolean} isExternal Render in external container
+ * @param {String} extFltId External container id
+ */
+ _build(colIndex, isExternal=false, extFltId=null){
+ var tf = this.tf;
+ colIndex = parseInt(colIndex, 10);
+
+ this.opts = [];
+ this.optsTxt = [];
+
+ var divFltId = this.prfxCheckListDiv+colIndex+'_'+tf.id;
+ if((!Dom.id(divFltId) && !isExternal) ||
+ (!Dom.id(extFltId) && isExternal)){
+ return;
+ }
+
+ var flt = !isExternal ? this.checkListDiv[colIndex] : Dom.id(extFltId);
+ var ul = Dom.create(
+ 'ul', ['id', tf.fltIds[colIndex]], ['colIndex', colIndex]);
+ ul.className = this.checkListCssClass;
+ Event.add(ul, 'change', (evt) => { this.onChange(evt); });
+
+ var rows = tf.tbl.rows;
+ this.isCustom = tf.isCustomOptions(colIndex);
+
+ var activeFlt;
+ if(tf.linkedFilters && tf.activeFilterId){
+ activeFlt = tf.activeFilterId.split('_')[0];
+ activeFlt = activeFlt.split(tf.prfxFlt)[1];
+ }
+
+ var filteredDataCol = [];
+ if(tf.linkedFilters && tf.disableExcludedOptions){
+ this.excludedOpts = [];
+ }
+
+ for(var k=tf.refRow; k<tf.nbRows; k++){
+ // always visible rows don't need to appear on selects as always
+ // valid
+ if(tf.hasVisibleRows && tf.visibleRows.indexOf(k) !== -1){
+ continue;
+ }
+
+ var cells = rows[k].cells;
+ var ncells = cells.length;
+
+ // checks if row has exact cell #
+ if(ncells !== tf.nbCells || this.isCustom){
+ continue;
+ }
+
+ // this loop retrieves cell data
+ for(var j=0; j<ncells; j++){
+ // WTF: cyclomatic complexity hell :)
+ if((colIndex===j && (!tf.linkedFilters ||
+ (tf.linkedFilters && tf.disableExcludedOptions)))||
+ (colIndex===j && tf.linkedFilters &&
+ ((rows[k].style.display === '' && !tf.paging) ||
+ (tf.paging && ((!activeFlt || activeFlt===colIndex )||
+ (activeFlt!=colIndex &&
+ tf.validRowsIndex.indexOf(k) != -1)) )))){
+ var cell_data = tf.getCellData(cells[j]);
+ //Vary Peter's patch
+ var cell_string = Str.matchCase(cell_data, tf.matchCase);
+ // checks if celldata is already in array
+ if(!Arr.has(this.opts, cell_string, tf.matchCase)){
+ this.opts.push(cell_data);
+ }
+ var filteredCol = filteredDataCol[j];
+ if(tf.linkedFilters && tf.disableExcludedOptions){
+ if(!filteredCol){
+ filteredCol = tf.getFilteredDataCol(j);
+ }
+ if(!Arr.has(filteredCol, cell_string, tf.matchCase) &&
+ !Arr.has(this.excludedOpts,
+ cell_string, tf.matchCase) &&
+ !tf.isFirstLoad){
+ this.excludedOpts.push(cell_data);
+ }
+ }
+ }
+ }
+ }
+
+ //Retrieves custom values
+ if(this.isCustom){
+ var customValues = tf.getCustomOptions(colIndex);
+ this.opts = customValues[0];
+ this.optsTxt = customValues[1];
+ }
+
+ if(tf.sortSlc && !this.isCustom){
+ if (!tf.matchCase){
+ this.opts.sort(Sort.ignoreCase);
+ if(this.excludedOpts){
+ this.excludedOpts.sort(Sort.ignoreCase);
+ }
+ } else {
+ this.opts.sort();
+ if(this.excludedOpts){
+ this.excludedOpts.sort();
+ }
+ }
+ }
+ //asc sort
+ if(tf.sortNumAsc && tf.sortNumAsc.indexOf(colIndex) != -1){
+ try{
+ this.opts.sort(numSortAsc);
+ if(this.excludedOpts){
+ this.excludedOpts.sort(numSortAsc);
+ }
+ if(this.isCustom){
+ this.optsTxt.sort(numSortAsc);
+ }
+ } catch(e) {
+ this.opts.sort();
+ if(this.excludedOpts){
+ this.excludedOpts.sort();
+ }
+ if(this.isCustom){
+ this.optsTxt.sort();
+ }
+ }//in case there are alphanumeric values
+ }
+ //desc sort
+ if(tf.sortNumDesc && tf.sortNumDesc.indexOf(colIndex) != -1){
+ try{
+ this.opts.sort(numSortDesc);
+ if(this.excludedOpts){
+ this.excludedOpts.sort(numSortDesc);
+ }
+ if(this.isCustom){
+ this.optsTxt.sort(numSortDesc);
+ }
+ } catch(e) {
+ this.opts.sort();
+ if(this.excludedOpts){
+ this.excludedOpts.sort(); }
+ if(this.isCustom){
+ this.optsTxt.sort();
+ }
+ }//in case there are alphanumeric values
+ }
+
+ this.addChecks(colIndex, ul, tf.separator);
+
+ if(tf.loadFltOnDemand){
+ flt.innerHTML = '';
+ }
+ flt.appendChild(ul);
+ flt.setAttribute('filled', '1');
+ }
+
+ /**
+ * Add checklist options
+ * @param {Number} colIndex Column index
+ * @param {Object} ul Ul element
+ */
+ addChecks(colIndex, ul){
+ var tf = this.tf;
+ var chkCt = this.addTChecks(colIndex, ul);
+ var fltArr = []; //remember grid values
+ var store = tf.feature('store');
+ var tmpVal = store ?
+ store.getFilterValues(tf.fltsValuesCookie)[colIndex] : null;
+ if(tmpVal && Str.trim(tmpVal).length > 0){
+ if(tf.hasCustomSlcOptions &&
+ tf.customSlcOptions.cols.indexOf(colIndex) != -1){
+ fltArr.push(tmpVal);
+ } else {
+ fltArr = tmpVal.split(' '+tf.orOperator+' ');
+ }
+ }
+
+ for(var y=0; y<this.opts.length; y++){
+ var val = this.opts[y]; //item value
+ var lbl = this.isCustom ? this.optsTxt[y] : val; //item text
+ var li = Dom.createCheckItem(
+ tf.fltIds[colIndex]+'_'+(y+chkCt), val, lbl);
+ li.className = this.checkListItemCssClass;
+ if(tf.linkedFilters && tf.disableExcludedOptions &&
+ Arr.has(this.excludedOpts,
+ Str.matchCase(val, tf.matchCase), tf.matchCase)){
+ Dom.addClass(li, this.checkListItemDisabledCssClass);
+ li.check.disabled = true;
+ li.disabled = true;
+ } else {
+ Event.add(li.check, 'click',
+ (evt) => { this.optionClick(evt); });
+ }
+ ul.appendChild(li);
+
+ if(val===''){
+ //item is hidden
+ li.style.display = 'none';
+ }
+
+ /*** remember grid values ***/
+ if(tf.rememberGridValues){
+ if((tf.hasCustomSlcOptions &&
+ tf.customSlcOptions.cols.indexOf(colIndex) != -1 &&
+ fltArr.toString().indexOf(val) != -1) ||
+ Arr.has(fltArr,
+ Str.matchCase(val, tf.matchCase), tf.matchCase)){
+ li.check.checked = true;
+ this.setCheckListValues(li.check);
+ }
+ }
+ }
+ }
+
+ /**
+ * Add checklist header option
+ * @param {Number} colIndex Column index
+ * @param {Object} ul Ul element
+ */
+ addTChecks(colIndex, ul){
+ var tf = this.tf;
+ var chkCt = 1;
+ var li0 = Dom.createCheckItem(
+ tf.fltIds[colIndex]+'_0', '', tf.displayAllText);
+ li0.className = this.checkListItemCssClass;
+ ul.appendChild(li0);
+
+ Event.add(li0.check, 'click', (evt) => {
+ this.optionClick(evt);
+ });
+
+ if(!this.enableCheckListResetFilter){
+ li0.style.display = 'none';
+ }
+
+ if(tf.enableEmptyOption){
+ var li1 = Dom.createCheckItem(
+ tf.fltIds[colIndex]+'_1', tf.emOperator, tf.emptyText);
+ li1.className = this.checkListItemCssClass;
+ ul.appendChild(li1);
+ Event.add(li1.check, 'click', (evt) => {
+ this.optionClick(evt);
+ });
+ chkCt++;
+ }
+
+ if(tf.enableNonEmptyOption){
+ var li2 = Dom.createCheckItem(
+ tf.fltIds[colIndex]+'_2',
+ tf.nmOperator,
+ tf.nonEmptyText
+ );
+ li2.className = this.checkListItemCssClass;
+ ul.appendChild(li2);
+ Event.add(li2.check, 'click', (evt) => {
+ this.optionClick(evt);
+ });
+ chkCt++;
+ }
+ return chkCt;
+ }
+
+ /**
+ * Store checked options in DOM element attribute
+ * @param {Object} o checklist option DOM element
+ */
+ setCheckListValues(o){
+ if(!o){
+ return;
+ }
+ var tf = this.tf;
+ var chkValue = o.value; //checked item value
+ var chkIndex = parseInt(o.id.split('_')[2], 10);
+ var filterTag = 'ul', itemTag = 'li';
+ var n = o;
+
+ //ul tag search
+ while(Str.lower(n.nodeName)!==filterTag){
+ n = n.parentNode;
+ }
+
+ var li = n.childNodes[chkIndex];
+ var colIndex = n.getAttribute('colIndex');
+ var fltValue = n.getAttribute('value'); //filter value (ul tag)
+ var fltIndexes = n.getAttribute('indexes'); //selected items (ul tag)
+
+ if(o.checked){
+ //show all item
+ if(chkValue===''){
+ if((fltIndexes && fltIndexes!=='')){
+ //items indexes
+ var indSplit = fltIndexes.split(tf.separator);
+ //checked items loop
+ for(var u=0; u<indSplit.length; u++){
+ //checked item
+ var cChk = Dom.id(tf.fltIds[colIndex]+'_'+indSplit[u]);
+ if(cChk){
+ cChk.checked = false;
+ Dom.removeClass(
+ n.childNodes[indSplit[u]],
+ this.checkListSlcItemCssClass
+ );
+ }
+ }
+ }
+ n.setAttribute('value', '');
+ n.setAttribute('indexes', '');
+
+ } else {
+ fltValue = (fltValue) ? fltValue : '';
+ chkValue = Str.trim(
+ fltValue+' '+chkValue+' '+tf.orOperator);
+ chkIndex = fltIndexes + chkIndex + tf.separator;
+ n.setAttribute('value', chkValue );
+ n.setAttribute('indexes', chkIndex);
+ //1st option unchecked
+ if(Dom.id(tf.fltIds[colIndex]+'_0')){
+ Dom.id(tf.fltIds[colIndex]+'_0').checked = false;
+ }
+ }
+
+ if(Str.lower(li.nodeName) === itemTag){
+ Dom.removeClass(
+ n.childNodes[0], this.checkListSlcItemCssClass);
+ Dom.addClass(li, this.checkListSlcItemCssClass);
+ }
+ } else { //removes values and indexes
+ if(chkValue!==''){
+ var replaceValue = new RegExp(
+ Str.rgxEsc(chkValue+' '+tf.orOperator));
+ fltValue = fltValue.replace(replaceValue,'');
+ n.setAttribute('value', Str.trim(fltValue));
+
+ var replaceIndex = new RegExp(
+ Str.rgxEsc(chkIndex + tf.separator));
+ fltIndexes = fltIndexes.replace(replaceIndex, '');
+ n.setAttribute('indexes', fltIndexes);
+ }
+ if(Str.lower(li.nodeName)===itemTag){
+ Dom.removeClass(li, this.checkListSlcItemCssClass);
+ }
+ }
+ }
+}
--- /dev/null
+import {Feature} from './feature';
+import Dom from '../dom';
+import Event from '../event';
+
+export class ClearButton extends Feature{
+
+ /**
+ * Clear button component
+ * @param {Object} tf TableFilter instance
+ */
+ constructor(tf){
+ super(tf, 'btnReset');
+
+ // Configuration object
+ var f = this.config;
+
+ //id of container element
+ this.btnResetTgtId = f.btn_reset_target_id || null;
+ //reset button element
+ this.btnResetEl = null;
+ //defines reset text
+ this.btnResetText = f.btn_reset_text || 'Reset';
+ //defines reset button tooltip
+ this.btnResetTooltip = f.btn_reset_tooltip || 'Clear filters';
+ //defines reset button innerHtml
+ this.btnResetHtml = f.btn_reset_html ||
+ (!tf.enableIcons ? null :
+ '<input type="button" value="" class="'+tf.btnResetCssClass+'" ' +
+ 'title="'+this.btnResetTooltip+'" />');
+ //span containing reset button
+ this.prfxResetSpan = 'resetspan_';
+ }
+
+ onClick(){
+ if(!this.isEnabled()){
+ return;
+ }
+ this.tf.clearFilters();
+ }
+
+ /**
+ * Build DOM elements
+ */
+ init(){
+ var tf = this.tf;
+
+ if(this.initialized){
+ return;
+ }
+
+ var resetspan = Dom.create('span', ['id', this.prfxResetSpan+tf.id]);
+
+ // reset button is added to defined element
+ if(!this.btnResetTgtId){
+ tf.setToolbar();
+ }
+ var targetEl = !this.btnResetTgtId ?
+ tf.rDiv : Dom.id(this.btnResetTgtId);
+ targetEl.appendChild(resetspan);
+
+ if(!this.btnResetHtml){
+ var fltreset = Dom.create('a', ['href', 'javascript:void(0);']);
+ fltreset.className = tf.btnResetCssClass;
+ fltreset.appendChild(Dom.text(this.btnResetText));
+ resetspan.appendChild(fltreset);
+ Event.add(fltreset, 'click', ()=> { this.onClick(); });
+ } else {
+ resetspan.innerHTML = this.btnResetHtml;
+ var resetEl = resetspan.firstChild;
+ Event.add(resetEl, 'click', ()=> { this.onClick(); });
+ }
+ this.btnResetEl = resetspan.firstChild;
+
+ this.initialized = true;
+ }
+
+ /**
+ * Remove clear button UI
+ */
+ destroy(){
+ var tf = this.tf;
+
+ if(!this.initialized){
+ return;
+ }
+
+ var resetspan = Dom.id(this.prfxResetSpan+tf.id);
+ if(resetspan){
+ resetspan.parentNode.removeChild(resetspan);
+ }
+ this.btnResetEl = null;
+ this.disable();
+ this.initialized = false;
+ }
+}
--- /dev/null
+import Dom from '../dom';
+import Arr from '../array';
+import Str from '../string';
+import Sort from '../sort';
+
+export class Dropdown{
+
+ /**
+ * Dropdown UI component
+ * @param {Object} tf TableFilter instance
+ */
+ constructor(tf){
+ // Configuration object
+ var f = tf.config();
+
+ this.enableSlcResetFilter = f.enable_slc_reset_filter===false ?
+ false : true;
+ //defines empty option text
+ this.nonEmptyText = f.non_empty_text || '(Non empty)';
+ //sets select filling method: 'innerHTML' or 'createElement'
+ this.slcFillingMethod = f.slc_filling_method || 'createElement';
+ //IE only, tooltip text appearing on select before it is populated
+ this.activateSlcTooltip = f.activate_slc_tooltip ||
+ 'Click to activate';
+ //tooltip text appearing on multiple select
+ this.multipleSlcTooltip = f.multiple_slc_tooltip ||
+ 'Use Ctrl key for multiple selections';
+
+ this.isCustom = null;
+ this.opts = null;
+ this.optsTxt = null;
+ this.slcInnerHtml = null;
+
+ this.tf = tf;
+ }
+
+ /**
+ * Build drop-down filter UI asynchronously
+ * @param {Number} colIndex Column index
+ * @param {Boolean} isLinked Enable linked refresh behaviour
+ * @param {Boolean} isExternal Render in external container
+ * @param {String} extSlcId External container id
+ */
+ build(colIndex, isLinked, isExternal, extSlcId){
+ var tf = this.tf;
+ tf.EvtManager(
+ tf.Evt.name.dropdown,
+ {
+ slcIndex: colIndex,
+ slcRefreshed: isLinked,
+ slcExternal: isExternal,
+ slcId: extSlcId
+ }
+ );
+ }
+
+ /**
+ * Build drop-down filter UI
+ * @param {Number} colIndex Column index
+ * @param {Boolean} isLinked Enable linked refresh behaviour
+ * @param {Boolean} isExternal Render in external container
+ * @param {String} extSlcId External container id
+ */
+ _build(colIndex, isLinked=false, isExternal=false, extSlcId=null){
+ var tf = this.tf;
+ colIndex = parseInt(colIndex, 10);
+
+ this.opts = [];
+ this.optsTxt = [];
+ this.slcInnerHtml = '';
+
+ var slcId = tf.fltIds[colIndex];
+ if((!Dom.id(slcId) && !isExternal) ||
+ (!Dom.id(extSlcId) && isExternal)){
+ return;
+ }
+ var slc = !isExternal ? Dom.id(slcId) : Dom.id(extSlcId),
+ rows = tf.tbl.rows,
+ matchCase = tf.matchCase;
+
+ //custom select test
+ this.isCustom = tf.isCustomOptions(colIndex);
+
+ //custom selects text
+ var activeFlt;
+ if(isLinked && tf.activeFilterId){
+ activeFlt = tf.activeFilterId.split('_')[0];
+ activeFlt = activeFlt.split(tf.prfxFlt)[1];
+ }
+
+ /*** remember grid values ***/
+ var fltsValues = [], fltArr = [];
+ if(tf.rememberGridValues){
+ fltsValues =
+ tf.feature('store').getFilterValues(tf.fltsValuesCookie);
+ if(fltsValues && !Str.isEmpty(fltsValues.toString())){
+ if(this.isCustom){
+ fltArr.push(fltsValues[colIndex]);
+ } else {
+ fltArr = fltsValues[colIndex].split(' '+tf.orOperator+' ');
+ }
+ }
+ }
+
+ var excludedOpts = null,
+ filteredDataCol = null;
+ if(isLinked && tf.disableExcludedOptions){
+ excludedOpts = [];
+ filteredDataCol = [];
+ }
+
+ for(var k=tf.refRow; k<tf.nbRows; k++){
+ // always visible rows don't need to appear on selects as always
+ // valid
+ if(tf.hasVisibleRows && tf.visibleRows.indexOf(k) !== -1){
+ continue;
+ }
+
+ var cell = rows[k].cells,
+ nchilds = cell.length;
+
+ // checks if row has exact cell #
+ if(nchilds !== tf.nbCells || this.isCustom){
+ continue;
+ }
+
+ // this loop retrieves cell data
+ for(var j=0; j<nchilds; j++){
+ // WTF: cyclomatic complexity hell
+ if((colIndex===j &&
+ (!isLinked ||
+ (isLinked && tf.disableExcludedOptions))) ||
+ (colIndex==j && isLinked &&
+ ((rows[k].style.display === '' && !tf.paging) ||
+ (tf.paging && (!tf.validRowsIndex ||
+ (tf.validRowsIndex &&
+ tf.validRowsIndex.indexOf(k) != -1)) &&
+ ((activeFlt===undefined || activeFlt==colIndex) ||
+ (activeFlt!=colIndex &&
+ tf.validRowsIndex.indexOf(k) != -1 ))) ))){
+ var cell_data = tf.getCellData(cell[j]),
+ //Vary Peter's patch
+ cell_string = Str.matchCase(cell_data, matchCase);
+
+ // checks if celldata is already in array
+ if(!Arr.has(this.opts, cell_string, matchCase)){
+ this.opts.push(cell_data);
+ }
+
+ if(isLinked && tf.disableExcludedOptions){
+ var filteredCol = filteredDataCol[j];
+ if(!filteredCol){
+ filteredCol = tf.getFilteredDataCol(j);
+ }
+ if(!Arr.has(filteredCol, cell_string, matchCase) &&
+ !Arr.has(
+ excludedOpts, cell_string, matchCase) &&
+ !this.isFirstLoad){
+ excludedOpts.push(cell_data);
+ }
+ }
+ }//if colIndex==j
+ }//for j
+ }//for k
+
+ //Retrieves custom values
+ if(this.isCustom){
+ var customValues = tf.getCustomOptions(colIndex);
+ this.opts = customValues[0];
+ this.optsTxt = customValues[1];
+ }
+
+ if(tf.sortSlc && !this.isCustom){
+ if (!matchCase){
+ this.opts.sort(Sort.ignoreCase);
+ if(excludedOpts){
+ excludedOpts.sort(Sort.ignoreCase);
+ }
+ } else {
+ this.opts.sort();
+ if(excludedOpts){ excludedOpts.sort(); }
+ }
+ }
+
+ //asc sort
+ if(tf.sortNumAsc && tf.sortNumAsc.indexOf(colIndex) != -1){
+ try{
+ this.opts.sort( numSortAsc );
+ if(excludedOpts){
+ excludedOpts.sort(numSortAsc);
+ }
+ if(this.isCustom){
+ this.optsTxt.sort(numSortAsc);
+ }
+ } catch(e) {
+ this.opts.sort();
+ if(excludedOpts){
+ excludedOpts.sort();
+ }
+ if(this.isCustom){
+ this.optsTxt.sort();
+ }
+ }//in case there are alphanumeric values
+ }
+ //desc sort
+ if(tf.sortNumDesc && tf.sortNumDesc.indexOf(colIndex) != -1){
+ try{
+ this.opts.sort(numSortDesc);
+ if(excludedOpts){
+ excludedOpts.sort(numSortDesc);
+ }
+ if(this.isCustom){
+ this.optsTxt.sort(numSortDesc);
+ }
+ } catch(e) {
+ this.opts.sort();
+ if(excludedOpts){
+ excludedOpts.sort();
+ }
+ if(this.isCustom){
+ this.optsTxt.sort();
+ }
+ }//in case there are alphanumeric values
+ }
+
+ //populates drop-down
+ this.addOptions(
+ colIndex, slc, isLinked, excludedOpts, fltsValues, fltArr);
+ }
+
+ /**
+ * Add drop-down options
+ * @param {Number} colIndex Column index
+ * @param {Object} slc Select Dom element
+ * @param {Boolean} isLinked Enable linked refresh behaviour
+ * @param {Array} excludedOpts Array of excluded options
+ * @param {Array} fltsValues Collection of persisted filter values
+ * @param {Array} fltArr Collection of persisted filter values
+ */
+ addOptions(colIndex, slc, isLinked, excludedOpts, fltsValues, fltArr){
+ var tf = this.tf,
+ fillMethod = Str.lower(this.slcFillingMethod),
+ slcValue = slc.value;
+
+ slc.innerHTML = '';
+ slc = this.addFirstOption(slc);
+
+ for(var y=0; y<this.opts.length; y++){
+ if(this.opts[y]===''){
+ continue;
+ }
+ var val = this.opts[y]; //option value
+ var lbl = this.isCustom ? this.optsTxt[y] : val; //option text
+ var isDisabled = false;
+ if(isLinked && tf.disableExcludedOptions &&
+ Arr.has(
+ excludedOpts,
+ Str.matchCase(val, tf.matchCase),
+ tf.matchCase
+ )){
+ isDisabled = true;
+ }
+
+ if(fillMethod === 'innerhtml'){
+ var slcAttr = '';
+ if(tf.loadFltOnDemand && slcValue===this.opts[y]){
+ slcAttr = 'selected="selected"';
+ }
+ this.slcInnerHtml += '<option value="'+val+'" ' + slcAttr +
+ (isDisabled ? 'disabled="disabled"' : '')+ '>' +
+ lbl+'</option>';
+ } else {
+ var opt;
+ //fill select on demand
+ if(tf.loadFltOnDemand && slcValue===this.opts[y] &&
+ tf.getFilterType(colIndex) === tf.fltTypeSlc){
+ opt = Dom.createOpt(lbl, val, true);
+ } else {
+ if(tf.getFilterType(colIndex) !== tf.fltTypeMulti){
+ opt = Dom.createOpt(
+ lbl,
+ val,
+ (fltsValues[colIndex]!==' ' &&
+ val===fltsValues[colIndex]) ? true : false
+ );
+ } else {
+ opt = Dom.createOpt(
+ lbl,
+ val,
+ (Arr.has(fltArr,
+ Str.matchCase(this.opts[y], tf.matchCase),
+ tf.matchCase) ||
+ fltArr.toString().indexOf(val)!== -1) ?
+ true : false
+ );
+ }
+ }
+ if(isDisabled){
+ opt.disabled = true;
+ }
+ slc.appendChild(opt);
+ }
+ }// for y
+
+ if(fillMethod === 'innerhtml'){
+ slc.innerHTML += this.slcInnerHtml;
+ }
+ slc.setAttribute('filled', '1');
+ }
+
+ /**
+ * Add drop-down header option
+ * @param {Object} slc Select DOM element
+ */
+ addFirstOption(slc){
+ var tf = this.tf,
+ fillMethod = Str.lower(this.slcFillingMethod);
+
+ if(fillMethod === 'innerhtml'){
+ this.slcInnerHtml += '<option value="">'+ tf.displayAllText +
+ '</option>';
+ }
+ else {
+ var opt0 = Dom.createOpt(
+ (!this.enableSlcResetFilter ? '' : tf.displayAllText),'');
+ if(!this.enableSlcResetFilter){
+ opt0.style.display = 'none';
+ }
+ slc.appendChild(opt0);
+ if(tf.enableEmptyOption){
+ var opt1 = Dom.createOpt(tf.emptyText, tf.emOperator);
+ slc.appendChild(opt1);
+ }
+ if(tf.enableNonEmptyOption){
+ var opt2 = Dom.createOpt(tf.nonEmptyText, tf.nmOperator);
+ slc.appendChild(opt2);
+ }
+ }
+ return slc;
+ }
+
+}
--- /dev/null
+
+const NOTIMPLEMENTED = 'Not implemented.';
+
+export class Feature {
+ constructor(tf, feature) {
+ this.tf = tf;
+ this.feature = feature;
+ this.enabled = tf[feature];
+ this.config = tf.config();
+ this.initialized = false;
+ }
+
+ init() {
+ throw new Error(NOTIMPLEMENTED);
+ }
+
+ reset() {
+ this.enable();
+ this.init();
+ }
+
+ destroy() {
+ throw new Error(NOTIMPLEMENTED);
+ }
+
+ enable() {
+ this.enabled = true;
+ }
+
+ disable() {
+ this.enabled = false;
+ }
+
+ isEnabled() {
+ return this.enabled;
+ }
+}
--- /dev/null
+import {Feature} from './feature';
+import Dom from '../dom';
+import Types from '../types';
+import Event from '../event';
+
+export class GridLayout extends Feature{
+
+ /**
+ * Grid layout, table with fixed headers
+ * @param {Object} tf TableFilter instance
+ */
+ constructor(tf){
+ super(tf, 'gridLayout');
+
+ var f = this.config;
+
+ //defines grid width
+ this.gridWidth = f.grid_width || null;
+ //defines grid height
+ this.gridHeight = f.grid_height || null;
+ //defines css class for main container
+ this.gridMainContCssClass = f.grid_cont_css_class || 'grd_Cont';
+ //defines css class for div containing table
+ this.gridContCssClass = f.grid_tbl_cont_css_class || 'grd_tblCont';
+ //defines css class for div containing headers' table
+ this.gridHeadContCssClass = f.grid_tblHead_cont_css_class ||
+ 'grd_headTblCont';
+ //defines css class for div containing rows counter, paging etc.
+ this.gridInfDivCssClass = f.grid_inf_grid_css_class || 'grd_inf';
+ //defines which row contains column headers
+ this.gridHeadRowIndex = f.grid_headers_row_index || 0;
+ //array of headers row indexes to be placed in header table
+ this.gridHeadRows = f.grid_headers_rows || [0];
+ //generate filters in table headers
+ this.gridEnableFilters = f.grid_enable_default_filters!==undefined ?
+ f.grid_enable_default_filters : true;
+ //default col width
+ this.gridDefaultColWidth = f.grid_default_col_width || '100px';
+
+ this.gridColElms = [];
+
+ //div containing grid elements if grid_layout true
+ this.prfxMainTblCont = 'gridCont_';
+ //div containing table if grid_layout true
+ this.prfxTblCont = 'tblCont_';
+ //div containing headers table if grid_layout true
+ this.prfxHeadTblCont = 'tblHeadCont_';
+ //headers' table if grid_layout true
+ this.prfxHeadTbl = 'tblHead_';
+ //id of td containing the filter if grid_layout true
+ this.prfxGridFltTd = '_td_';
+ //id of th containing column header if grid_layout true
+ this.prfxGridTh = 'tblHeadTh_';
+
+ this.sourceTblHtml = tf.tbl.outerHTML;
+ }
+
+ /**
+ * Generates a grid with fixed headers
+ */
+ init(){
+ var tf = this.tf;
+ var f = this.config;
+ var tbl = tf.tbl;
+
+ if(this.initialized){
+ return;
+ }
+
+ tf.isExternalFlt = true;
+
+ // default width of 100px if column widths not set
+ if(!tf.hasColWidths){
+ tf.colWidths = [];
+ for(var k=0; k<tf.nbCells; k++){
+ var colW,
+ cell = tbl.rows[this.gridHeadRowIndex].cells[k];
+ if(cell.width !== ''){
+ colW = cell.width;
+ } else if(cell.style.width !== ''){
+ colW = parseInt(cell.style.width, 10);
+ } else {
+ colW = this.gridDefaultColWidth;
+ }
+ tf.colWidths[k] = colW;
+ }
+ tf.hasColWidths = true;
+ }
+ tf.setColWidths(this.gridHeadRowIndex);
+
+ var tblW;//initial table width
+ if(tbl.width !== ''){
+ tblW = tbl.width;
+ }
+ else if(tbl.style.width !== ''){
+ tblW = parseInt(tbl.style.width, 10);
+ } else {
+ tblW = tbl.clientWidth;
+ }
+
+ //Main container: it will contain all the elements
+ this.tblMainCont = Dom.create('div',
+ ['id', this.prfxMainTblCont + tf.id]);
+ this.tblMainCont.className = this.gridMainContCssClass;
+ if(this.gridWidth){
+ this.tblMainCont.style.width = this.gridWidth;
+ }
+ tbl.parentNode.insertBefore(this.tblMainCont, tbl);
+
+ //Table container: div wrapping content table
+ this.tblCont = Dom.create('div',['id', this.prfxTblCont + tf.id]);
+ this.tblCont.className = this.gridContCssClass;
+ if(this.gridWidth){
+ if(this.gridWidth.indexOf('%') != -1){
+ console.log(this.gridWidth);
+ this.tblCont.style.width = '100%';
+ } else {
+ this.tblCont.style.width = this.gridWidth;
+ }
+ }
+ if(this.gridHeight){
+ this.tblCont.style.height = this.gridHeight;
+ }
+ tbl.parentNode.insertBefore(this.tblCont, tbl);
+ var t = tbl.parentNode.removeChild(tbl);
+ this.tblCont.appendChild(t);
+
+ //In case table width is expressed in %
+ if(tbl.style.width === ''){
+ tbl.style.width = (tf._containsStr('%', tblW) ?
+ tbl.clientWidth : tblW) + 'px';
+ }
+
+ var d = this.tblCont.parentNode.removeChild(this.tblCont);
+ this.tblMainCont.appendChild(d);
+
+ //Headers table container: div wrapping headers table
+ this.headTblCont = Dom.create(
+ 'div',['id', this.prfxHeadTblCont + tf.id]);
+ this.headTblCont.className = this.gridHeadContCssClass;
+ if(this.gridWidth){
+ if(this.gridWidth.indexOf('%') != -1){
+ console.log(this.gridWidth);
+ this.headTblCont.style.width = '100%';
+ } else {
+ this.headTblCont.style.width = this.gridWidth;
+ }
+ }
+
+ //Headers table
+ this.headTbl = Dom.create('table', ['id', this.prfxHeadTbl + tf.id]);
+ var tH = Dom.create('tHead');
+
+ //1st row should be headers row, ids are added if not set
+ //Those ids are used by the sort feature
+ var hRow = tbl.rows[this.gridHeadRowIndex];
+ var sortTriggers = [];
+ for(var n=0; n<tf.nbCells; n++){
+ var c = hRow.cells[n];
+ var thId = c.getAttribute('id');
+ if(!thId || thId===''){
+ thId = this.prfxGridTh+n+'_'+tf.id;
+ c.setAttribute('id', thId);
+ }
+ sortTriggers.push(thId);
+ }
+
+ //Filters row is created
+ var filtersRow = Dom.create('tr');
+ if(this.gridEnableFilters && tf.fltGrid){
+ tf.externalFltTgtIds = [];
+ for(var j=0; j<tf.nbCells; j++){
+ var fltTdId = tf.prfxFlt+j+ this.prfxGridFltTd +tf.id;
+ var cl = Dom.create(tf.fltCellTag, ['id', fltTdId]);
+ filtersRow.appendChild(cl);
+ tf.externalFltTgtIds[j] = fltTdId;
+ }
+ }
+ //Headers row are moved from content table to headers table
+ for(var i=0; i<this.gridHeadRows.length; i++){
+ var headRow = tbl.rows[this.gridHeadRows[0]];
+ tH.appendChild(headRow);
+ }
+ this.headTbl.appendChild(tH);
+ if(tf.filtersRowIndex === 0){
+ tH.insertBefore(filtersRow,hRow);
+ } else {
+ tH.appendChild(filtersRow);
+ }
+
+ this.headTblCont.appendChild(this.headTbl);
+ this.tblCont.parentNode.insertBefore(this.headTblCont, this.tblCont);
+
+ //THead needs to be removed in content table for sort feature
+ var thead = Dom.tag(tbl, 'thead');
+ if(thead.length>0){
+ tbl.removeChild(thead[0]);
+ }
+
+ //Headers table style
+ this.headTbl.style.tableLayout = 'fixed';
+ tbl.style.tableLayout = 'fixed';
+ this.headTbl.cellPadding = tbl.cellPadding;
+ this.headTbl.cellSpacing = tbl.cellSpacing;
+ // this.headTbl.style.width = tbl.style.width;
+
+ //content table without headers needs col widths to be reset
+ tf.setColWidths(0, this.headTbl);
+
+ //Headers container width
+ // this.headTblCont.style.width = this.tblCont.clientWidth+'px';
+
+ tbl.style.width = '';
+ //
+ this.headTbl.style.width = tbl.clientWidth + 'px';
+ //
+
+ //scroll synchronisation
+ Event.add(this.tblCont, 'scroll', (evt)=> {
+ var elm = Event.target(evt);
+ var scrollLeft = elm.scrollLeft;
+ this.headTblCont.scrollLeft = scrollLeft;
+ //New pointerX calc taking into account scrollLeft
+ // if(!o.isPointerXOverwritten){
+ // try{
+ // o.Evt.pointerX = function(evt){
+ // var e = evt || global.event;
+ // var bdScrollLeft = tf_StandardBody().scrollLeft +
+ // scrollLeft;
+ // return (e.pageX + scrollLeft) ||
+ // (e.clientX + bdScrollLeft);
+ // };
+ // o.isPointerXOverwritten = true;
+ // } catch(err) {
+ // o.isPointerXOverwritten = false;
+ // }
+ // }
+ });
+
+ //Configure sort extension if any
+ var sort = (f.extensions || []).filter(function(itm){
+ return itm.name === 'sort';
+ });
+ if(sort.length === 1){
+ sort[0].async_sort = true;
+ sort[0].trigger_ids = sortTriggers;
+ }
+
+ //Cols generation for all browsers excepted IE<=7
+ this.tblHasColTag = Dom.tag(tbl, 'col').length > 0 ? true : false;
+
+ //Col elements are enough to keep column widths after sorting and
+ //filtering
+ var createColTags = function(){
+ for(var k=(tf.nbCells-1); k>=0; k--){
+ var col = Dom.create('col', ['id', tf.id+'_col_'+k]);
+ tbl.insertBefore(col, tbl.firstChild);
+ col.style.width = tf.colWidths[k];
+ this.gridColElms[k] = col;
+ }
+ this.tblHasColTag = true;
+ };
+
+ if(!this.tblHasColTag){
+ createColTags.call(this);
+ } else {
+ var cols = Dom.tag(tbl, 'col');
+ for(var ii=0; ii<tf.nbCells; ii++){
+ cols[ii].setAttribute('id', tf.id+'_col_'+ii);
+ cols[ii].style.width = tf.colWidths[ii];
+ this.gridColElms.push(cols[ii]);
+ }
+ }
+
+ var afterColResizedFn = Types.isFn(f.on_after_col_resized) ?
+ f.on_after_col_resized : null;
+ f.on_after_col_resized = function(o, colIndex){
+ if(!colIndex){
+ return;
+ }
+ var w = o.crWColsRow.cells[colIndex].style.width;
+ var col = o.gridColElms[colIndex];
+ col.style.width = w;
+
+ var thCW = o.crWColsRow.cells[colIndex].clientWidth;
+ var tdCW = o.crWRowDataTbl.cells[colIndex].clientWidth;
+
+ if(thCW != tdCW){
+ o.headTbl.style.width = tbl.clientWidth+'px';
+ }
+
+ if(afterColResizedFn){
+ afterColResizedFn.call(null,o,colIndex);
+ }
+ };
+
+ if(tf.popupFilters){
+ filtersRow.style.display = 'none';
+ }
+
+ if(tbl.clientWidth !== this.headTbl.clientWidth){
+ tbl.style.width = this.headTbl.clientWidth+'px';
+ }
+
+ this.initialized = true;
+ }
+
+ /**
+ * Removes the grid layout
+ */
+ destroy(){
+ var tf = this.tf;
+ var tbl = tf.tbl;
+
+ if(!this.initialized){
+ return;
+ }
+ var t = tbl.parentNode.removeChild(tbl);
+ this.tblMainCont.parentNode.insertBefore(t, this.tblMainCont);
+ this.tblMainCont.parentNode.removeChild(this.tblMainCont);
+
+ this.tblMainCont = null;
+ this.headTblCont = null;
+ this.headTbl = null;
+ this.tblCont = null;
+
+ tbl.outerHTML = this.sourceTblHtml;
+ //needed to keep reference of table element
+ this.tf.tbl = Dom.id(tf.id); // ???
+
+ this.initialized = false;
+ }
+}
--- /dev/null
+import {Feature} from './feature';
+import Dom from '../dom';
+import Event from '../event';
+
+
+const WIKI_URL = 'https://github.com/koalyptus/TableFilter/wiki/' +
+ '4.-Filter-operators';
+const WEBSITE_URL = 'http://koalyptus.github.io/TableFilter/';
+
+export class Help extends Feature{
+
+ /**
+ * Help UI component
+ * @param {Object} tf TableFilter instance
+ */
+ constructor(tf){
+ super(tf, 'help');
+
+ var f = this.config;
+
+ //id of custom container element for instructions
+ this.tgtId = f.help_instructions_target_id || null;
+ //id of custom container element for instructions
+ this.contTgtId = f.help_instructions_container_target_id ||
+ null;
+ //defines help text
+ this.instrText = f.help_instructions_text ?
+ f.help_instructions_text :
+ 'Use the filters above each column to filter and limit table ' +
+ 'data. Advanced searches can be performed by using the following ' +
+ 'operators: <br /><b><</b>, <b><=</b>, <b>></b>, ' +
+ '<b>>=</b>, <b>=</b>, <b>*</b>, <b>!</b>, <b>{</b>, <b>}</b>, ' +
+ '<b>||</b>,<b>&&</b>, <b>[empty]</b>, <b>[nonempty]</b>, ' +
+ '<b>rgx:</b><br/><a href="'+ WIKI_URL +'" target="_blank">' +
+ 'Learn more</a><hr/>';
+ //defines help innerHtml
+ this.instrHtml = f.help_instructions_html || null;
+ //defines reset button text
+ this.btnText = f.help_instructions_btn_text || '?';
+ //defines reset button innerHtml
+ this.btnHtml = f.help_instructions_btn_html || null;
+ //defines css class for help button
+ this.btnCssClass = f.help_instructions_btn_css_class || 'helpBtn';
+ //defines css class for help container
+ this.contCssClass = f.help_instructions_container_css_class ||
+ 'helpCont';
+ //help button element
+ this.btn = null;
+ //help content div
+ this.cont = null;
+ this.defaultHtml = '<div class="helpFooter"><h4>TableFilter ' +
+ 'v'+ tf.version +'</h4>' +
+ '<a href="'+ WEBSITE_URL +'" target="_blank">'+ WEBSITE_URL +'</a>'+
+ '<br/><span>©2015-'+ tf.year +' {AUTHOR}</span>' +
+ '<div align="center" style="margin-top:8px;">' +
+ '<a href="javascript:void(0);" class="close">Close</a></div></div>';
+
+ //id prefix for help elements
+ this.prfxHelpSpan = 'helpSpan_';
+ //id prefix for help elements
+ this.prfxHelpDiv = 'helpDiv_';
+ }
+
+ init(){
+ if(this.initialized){
+ return;
+ }
+
+ var tf = this.tf;
+
+ var helpspan = Dom.create('span', ['id', this.prfxHelpSpan+tf.id]);
+ var helpdiv = Dom.create('div', ['id', this.prfxHelpDiv+tf.id]);
+
+ //help button is added to defined element
+ if(!this.tgtId){
+ tf.setToolbar();
+ }
+ var targetEl = !this.tgtId ? tf.rDiv : Dom.id(this.tgtId);
+ targetEl.appendChild(helpspan);
+
+ var divContainer = !this.contTgtId ? helpspan : Dom.id(this.contTgtId);
+
+ if(!this.btnHtml){
+ divContainer.appendChild(helpdiv);
+ var helplink = Dom.create('a', ['href', 'javascript:void(0);']);
+ helplink.className = this.btnCssClass;
+ helplink.appendChild(Dom.text(this.btnText));
+ helpspan.appendChild(helplink);
+ Event.add(helplink, 'click', () => { this.toggle(); });
+ } else {
+ helpspan.innerHTML = this.btnHtml;
+ var helpEl = helpspan.firstChild;
+ Event.add(helpEl, 'click', () => { this.toggle(); });
+ divContainer.appendChild(helpdiv);
+ }
+
+ if(!this.instrHtml){
+ helpdiv.innerHTML = this.instrText;
+ helpdiv.className = this.contCssClass;
+ Event.add(helpdiv, 'dblclick', () => { this.toggle(); });
+ } else {
+ if(this.contTgtId){
+ divContainer.appendChild(helpdiv);
+ }
+ helpdiv.innerHTML = this.instrHtml;
+ if(!this.contTgtId){
+ helpdiv.className = this.contCssClass;
+ Event.add(helpdiv, 'dblclick', () => { this.toggle(); });
+ }
+ }
+ helpdiv.innerHTML += this.defaultHtml;
+ Event.add(helpdiv, 'click', () => { this.toggle(); });
+
+ this.cont = helpdiv;
+ this.btn = helpspan;
+ this.initialized = true;
+ }
+
+ /**
+ * Toggle help pop-up
+ */
+ toggle(){
+ // check only if explicitily set to false as in this case undefined
+ // signifies the help feature is enabled by default
+ if(this.enabled === false){
+ return;
+ }
+ var divDisplay = this.cont.style.display;
+ if(divDisplay === '' || divDisplay === 'none'){
+ this.cont.style.display = 'inline';
+ } else {
+ this.cont.style.display = 'none';
+ }
+ }
+
+ /**
+ * Remove help UI
+ */
+ destroy(){
+ if(!this.initialized){
+ return;
+ }
+ this.btn.parentNode.removeChild(this.btn);
+ this.btn = null;
+ if(!this.cont){
+ return;
+ }
+ this.cont.parentNode.removeChild(this.cont);
+ this.cont = null;
+
+ this.disable();
+ this.initialized = false;
+ }
+
+}
--- /dev/null
+import Dom from '../dom';
+import Str from '../string';
+
+export class HighlightKeyword{
+
+ /**
+ * HighlightKeyword, highlight matched keyword
+ * @param {Object} tf TableFilter instance
+ */
+ constructor(tf) {
+ var f = tf.config();
+ //defines css class for highlighting
+ this.highlightCssClass = f.highlight_css_class || 'keyword';
+ this.highlightedNodes = [];
+
+ this.tf = tf;
+ }
+
+ /**
+ * highlight occurences of searched term in passed node
+ * @param {Node} node
+ * @param {String} word Searched term
+ * @param {String} cssClass Css class name
+ */
+ highlight(node, word, cssClass){
+ // Iterate into this nodes childNodes
+ if(node.hasChildNodes){
+ var children = node.childNodes;
+ for(var i=0; i<children.length; i++){
+ this.highlight(children[i], word, cssClass);
+ }
+ }
+
+ if(node.nodeType === 3){
+ var tempNodeVal = Str.lower(node.nodeValue);
+ var tempWordVal = Str.lower(word);
+ if(tempNodeVal.indexOf(tempWordVal) != -1){
+ var pn = node.parentNode;
+ if(pn && pn.className != cssClass){
+ // word not highlighted yet
+ var nv = node.nodeValue,
+ ni = tempNodeVal.indexOf(tempWordVal),
+ // Create a load of replacement nodes
+ before = Dom.text(nv.substr(0, ni)),
+ docWordVal = nv.substr(ni,word.length),
+ after = Dom.text(nv.substr(ni+word.length)),
+ hiwordtext = Dom.text(docWordVal),
+ hiword = Dom.create('span');
+ hiword.className = cssClass;
+ hiword.appendChild(hiwordtext);
+ pn.insertBefore(before,node);
+ pn.insertBefore(hiword,node);
+ pn.insertBefore(after,node);
+ pn.removeChild(node);
+ this.highlightedNodes.push(hiword.firstChild);
+ }
+ }
+ }
+ }
+
+ /**
+ * Removes highlight to nodes matching passed string
+ * @param {String} word
+ * @param {String} cssClass Css class to remove
+ */
+ unhighlight(word, cssClass){
+ var arrRemove = [];
+ var highlightedNodes = this.highlightedNodes;
+ for(var i=0; i<highlightedNodes.length; i++){
+ var n = highlightedNodes[i];
+ if(!n){
+ continue;
+ }
+ var tempNodeVal = Str.lower(n.nodeValue),
+ tempWordVal = Str.lower(word);
+ if(tempNodeVal.indexOf(tempWordVal) !== -1){
+ var pn = n.parentNode;
+ if(pn && pn.className === cssClass){
+ var prevSib = pn.previousSibling,
+ nextSib = pn.nextSibling;
+ if(!prevSib || !nextSib){ continue; }
+ nextSib.nodeValue = prevSib.nodeValue + n.nodeValue +
+ nextSib.nodeValue;
+ prevSib.nodeValue = '';
+ n.nodeValue = '';
+ arrRemove.push(i);
+ }
+ }
+ }
+ for(var k=0; k<arrRemove.length; k++){
+ highlightedNodes.splice(arrRemove[k], 1);
+ }
+ }
+
+ /**
+ * Clear all occurrences of highlighted nodes
+ */
+ unhighlightAll(){
+ if(!this.tf.highlightKeywords || !this.tf.searchArgs){
+ return;
+ }
+ for(var y=0; y<this.tf.searchArgs.length; y++){
+ this.unhighlight(
+ this.tf.searchArgs[y], this.highlightCssClass);
+ }
+ this.highlightedNodes = [];
+ }
+}
\ No newline at end of file
--- /dev/null
+import {Feature} from './feature';
+import Dom from '../dom';
+import Types from '../types';
+
+var global = window;
+
+export class Loader extends Feature{
+
+ /**
+ * Loading message/spinner
+ * @param {Object} tf TableFilter instance
+ */
+ constructor(tf){
+ super(tf, 'loader');
+
+ // TableFilter configuration
+ var f = this.config;
+
+ //id of container element
+ this.loaderTgtId = f.loader_target_id || null;
+ //div containing loader
+ this.loaderDiv = null;
+ //defines loader text
+ this.loaderText = f.loader_text || 'Loading...';
+ //defines loader innerHtml
+ this.loaderHtml = f.loader_html || null;
+ //defines css class for loader div
+ this.loaderCssClass = f.loader_css_class || 'loader';
+ //delay for hiding loader
+ this.loaderCloseDelay = 200;
+ //callback function before loader is displayed
+ this.onShowLoader = Types.isFn(f.on_show_loader) ?
+ f.on_show_loader : null;
+ //callback function after loader is closed
+ this.onHideLoader = Types.isFn(f.on_hide_loader) ?
+ f.on_hide_loader : null;
+ //loader div
+ this.prfxLoader = 'load_';
+ }
+
+ init() {
+ if(this.initialized){
+ return;
+ }
+
+ var tf = this.tf;
+
+ var containerDiv = Dom.create('div', ['id', this.prfxLoader+tf.id]);
+ containerDiv.className = this.loaderCssClass;
+
+ var targetEl = !this.loaderTgtId ?
+ tf.tbl.parentNode : Dom.id(this.loaderTgtId);
+ if(!this.loaderTgtId){
+ targetEl.insertBefore(containerDiv, tf.tbl);
+ } else {
+ targetEl.appendChild(containerDiv);
+ }
+ this.loaderDiv = containerDiv;
+ if(!this.loaderHtml){
+ this.loaderDiv.appendChild(Dom.text(this.loaderText));
+ } else {
+ this.loaderDiv.innerHTML = this.loaderHtml;
+ }
+
+ this.show('none');
+ this.initialized = true;
+ }
+
+ show(p) {
+ if(!this.isEnabled() || this.loaderDiv.style.display === p){
+ return;
+ }
+
+ var displayLoader = () => {
+ if(!this.loaderDiv){
+ return;
+ }
+ if(this.onShowLoader && p !== 'none'){
+ this.onShowLoader.call(null, this);
+ }
+ this.loaderDiv.style.display = p;
+ if(this.onHideLoader && p === 'none'){
+ this.onHideLoader.call(null, this);
+ }
+ };
+
+ var t = p === 'none' ? this.loaderCloseDelay : 1;
+ global.setTimeout(displayLoader, t);
+ }
+
+ destroy(){
+ if(!this.initialized){
+ return;
+ }
+
+ this.loaderDiv.parentNode.removeChild(this.loaderDiv);
+ this.loaderDiv = null;
+
+ this.disable();
+ this.initialized = false;
+ }
+}
--- /dev/null
+import {Feature} from './feature';
+import Dom from '../dom';
+import Types from '../types';
+import Str from '../string';
+import Event from '../event';
+
+export class Paging extends Feature{
+
+ /**
+ * Pagination component
+ * @param {Object} tf TableFilter instance
+ */
+ constructor(tf){
+ super(tf, 'paging');
+
+ // Configuration object
+ var f = this.config;
+
+ //css class for paging buttons (previous,next,etc.)
+ this.btnPageCssClass = f.paging_btn_css_class || 'pgInp';
+ //stores paging select element
+ this.pagingSlc = null;
+ //results per page select element
+ this.resultsPerPageSlc = null;
+ //id of container element
+ this.pagingTgtId = f.paging_target_id || null;
+ //defines table paging length
+ this.pagingLength = !isNaN(f.paging_length) ? f.paging_length : 10;
+ //id of container element
+ this.resultsPerPageTgtId = f.results_per_page_target_id || null;
+ //css class for paging select element
+ this.pgSlcCssClass = f.paging_slc_css_class || 'pgSlc';
+ //css class for paging input element
+ this.pgInpCssClass = f.paging_inp_css_class || 'pgNbInp';
+ //stores results per page text and values
+ this.resultsPerPage = f.results_per_page || null;
+ //enables/disables results per page drop-down
+ this.hasResultsPerPage = Types.isArray(this.resultsPerPage);
+ //defines css class for results per page select
+ this.resultsSlcCssClass = f.results_slc_css_class || 'rspg';
+ //css class for label preceding results per page select
+ this.resultsSpanCssClass = f.results_span_css_class || 'rspgSpan';
+ //1st row index of current page
+ this.startPagingRow = 0;
+ //total nb of pages
+ this.nbPages = 0;
+ //current page nb
+ this.currentPageNb = 1;
+ //defines next page button text
+ this.btnNextPageText = f.btn_next_page_text || '>';
+ //defines previous page button text
+ this.btnPrevPageText = f.btn_prev_page_text || '<';
+ //defines last page button text
+ this.btnLastPageText = f.btn_last_page_text || '>|';
+ //defines first page button text
+ this.btnFirstPageText = f.btn_first_page_text || '|<';
+ //defines next page button html
+ this.btnNextPageHtml = f.btn_next_page_html ||
+ (!tf.enableIcons ? null :
+ '<input type="button" value="" class="'+this.btnPageCssClass +
+ ' nextPage" title="Next page" />');
+ //defines previous page button html
+ this.btnPrevPageHtml = f.btn_prev_page_html ||
+ (!tf.enableIcons ? null :
+ '<input type="button" value="" class="'+this.btnPageCssClass +
+ ' previousPage" title="Previous page" />');
+ //defines last page button html
+ this.btnFirstPageHtml = f.btn_first_page_html ||
+ (!tf.enableIcons ? null :
+ '<input type="button" value="" class="'+this.btnPageCssClass +
+ ' firstPage" title="First page" />');
+ //defines previous page button html
+ this.btnLastPageHtml = f.btn_last_page_html ||
+ (!tf.enableIcons ? null :
+ '<input type="button" value="" class="'+this.btnPageCssClass +
+ ' lastPage" title="Last page" />');
+ //defines text preceeding page selector drop-down
+ this.pageText = f.page_text || ' Page ';
+ //defines text after page selector drop-down
+ this.ofText = f.of_text || ' of ';
+ //css class for span containing tot nb of pages
+ this.nbPgSpanCssClass = f.nb_pages_css_class || 'nbpg';
+ //enables/disables paging buttons
+ this.hasPagingBtns = f.paging_btns===false ? false : true;
+ //defines previous page button html
+ this.pageSelectorType = f.page_selector_type || tf.fltTypeSlc;
+ //calls function before page is changed
+ this.onBeforeChangePage = Types.isFn(f.on_before_change_page) ?
+ f.on_before_change_page : null;
+ //calls function before page is changed
+ this.onAfterChangePage = Types.isFn(f.on_after_change_page) ?
+ f.on_after_change_page : null;
+
+ //pages select
+ this.prfxSlcPages = 'slcPages_';
+ //results per page select
+ this.prfxSlcResults = 'slcResults_';
+ //label preciding results per page select
+ this.prfxSlcResultsTxt = 'slcResultsTxt_';
+ //span containing next page button
+ this.prfxBtnNextSpan = 'btnNextSpan_';
+ //span containing previous page button
+ this.prfxBtnPrevSpan = 'btnPrevSpan_';
+ //span containing last page button
+ this.prfxBtnLastSpan = 'btnLastSpan_';
+ //span containing first page button
+ this.prfxBtnFirstSpan = 'btnFirstSpan_';
+ //next button
+ this.prfxBtnNext = 'btnNext_';
+ //previous button
+ this.prfxBtnPrev = 'btnPrev_';
+ //last button
+ this.prfxBtnLast = 'btnLast_';
+ //first button
+ this.prfxBtnFirst = 'btnFirst_';
+ //span for tot nb pages
+ this.prfxPgSpan = 'pgspan_';
+ //span preceding pages select (contains 'Page')
+ this.prfxPgBeforeSpan = 'pgbeforespan_';
+ //span following pages select (contains ' of ')
+ this.prfxPgAfterSpan = 'pgafterspan_';
+
+ var start_row = this.refRow;
+ var nrows = this.nbRows;
+ //calculates page nb
+ this.nbPages = Math.ceil((nrows-start_row)/this.pagingLength);
+
+ //Paging elements events
+ var o = this;
+ // Paging DOM events
+ this.evt = {
+ slcIndex(){
+ return (o.pageSelectorType===tf.fltTypeSlc) ?
+ o.pagingSlc.options.selectedIndex :
+ parseInt(o.pagingSlc.value, 10)-1;
+ },
+ nbOpts(){
+ return (o.pageSelectorType===tf.fltTypeSlc) ?
+ parseInt(o.pagingSlc.options.length, 10)-1 :
+ (o.nbPages-1);
+ },
+ next(){
+ var nextIndex = o.evt.slcIndex() < o.evt.nbOpts() ?
+ o.evt.slcIndex()+1 : 0;
+ o.changePage(nextIndex);
+ },
+ prev(){
+ var prevIndex = o.evt.slcIndex()>0 ?
+ o.evt.slcIndex()-1 : o.evt.nbOpts();
+ o.changePage(prevIndex);
+ },
+ last(){
+ o.changePage(o.evt.nbOpts());
+ },
+ first(){
+ o.changePage(0);
+ },
+ _detectKey(e){
+ var key = Event.keyCode(e);
+ if(key===13){
+ if(tf.sorted){
+ tf.filter();
+ o.changePage(o.evt.slcIndex());
+ } else{
+ o.changePage();
+ }
+ this.blur();
+ }
+ },
+ slcPagesChange: null,
+ nextEvt: null,
+ prevEvt: null,
+ lastEvt: null,
+ firstEvt: null
+ };
+ }
+
+ /**
+ * Initialize DOM elements
+ */
+ init(){
+ var slcPages;
+ var tf = this.tf;
+ var evt = this.evt;
+
+ if(this.initialized){
+ return;
+ }
+
+ // Check resultsPerPage is in expected format and initialise the
+ // results per page component
+ if(this.hasResultsPerPage){
+ if(this.resultsPerPage.length<2){
+ this.hasResultsPerPage = false;
+ } else {
+ this.pagingLength = this.resultsPerPage[1][0];
+ this.setResultsPerPage();
+ }
+ }
+
+ evt.slcPagesChange = (event) => {
+ var slc = event.target;
+ this.changePage(slc.selectedIndex);
+ };
+
+ // Paging drop-down list selector
+ if(this.pageSelectorType === tf.fltTypeSlc){
+ slcPages = Dom.create(
+ tf.fltTypeSlc, ['id', this.prfxSlcPages+tf.id]);
+ slcPages.className = this.pgSlcCssClass;
+ Event.add(slcPages, 'change', evt.slcPagesChange);
+ }
+
+ // Paging input selector
+ if(this.pageSelectorType === tf.fltTypeInp){
+ slcPages = Dom.create(
+ tf.fltTypeInp,
+ ['id', this.prfxSlcPages+tf.id],
+ ['value', this.currentPageNb]
+ );
+ slcPages.className = this.pgInpCssClass;
+ Event.add(slcPages, 'keypress', evt._detectKey);
+ }
+
+ // btns containers
+ var btnNextSpan = Dom.create(
+ 'span',['id', this.prfxBtnNextSpan+tf.id]);
+ var btnPrevSpan = Dom.create(
+ 'span',['id', this.prfxBtnPrevSpan+tf.id]);
+ var btnLastSpan = Dom.create(
+ 'span',['id', this.prfxBtnLastSpan+tf.id]);
+ var btnFirstSpan = Dom.create(
+ 'span',['id', this.prfxBtnFirstSpan+tf.id]);
+
+ if(this.hasPagingBtns){
+ // Next button
+ if(!this.btnNextPageHtml){
+ var btn_next = Dom.create(
+ tf.fltTypeInp,
+ ['id', this.prfxBtnNext+tf.id],
+ ['type', 'button'],
+ ['value', this.btnNextPageText],
+ ['title', 'Next']
+ );
+ btn_next.className = this.btnPageCssClass;
+ Event.add(btn_next, 'click', evt.next);
+ btnNextSpan.appendChild(btn_next);
+ } else {
+ btnNextSpan.innerHTML = this.btnNextPageHtml;
+ Event.add(btnNextSpan, 'click', evt.next);
+ }
+ // Previous button
+ if(!this.btnPrevPageHtml){
+ var btn_prev = Dom.create(
+ tf.fltTypeInp,
+ ['id', this.prfxBtnPrev+tf.id],
+ ['type', 'button'],
+ ['value', this.btnPrevPageText],
+ ['title', 'Previous']
+ );
+ btn_prev.className = this.btnPageCssClass;
+ Event.add(btn_prev, 'click', evt.prev);
+ btnPrevSpan.appendChild(btn_prev);
+ } else {
+ btnPrevSpan.innerHTML = this.btnPrevPageHtml;
+ Event.add(btnPrevSpan, 'click', evt.prev);
+ }
+ // Last button
+ if(!this.btnLastPageHtml){
+ var btn_last = Dom.create(
+ tf.fltTypeInp,
+ ['id', this.prfxBtnLast+tf.id],
+ ['type', 'button'],
+ ['value', this.btnLastPageText],
+ ['title', 'Last']
+ );
+ btn_last.className = this.btnPageCssClass;
+ Event.add(btn_last, 'click', evt.last);
+ btnLastSpan.appendChild(btn_last);
+ } else {
+ btnLastSpan.innerHTML = this.btnLastPageHtml;
+ Event.add(btnLastSpan, 'click', evt.last);
+ }
+ // First button
+ if(!this.btnFirstPageHtml){
+ var btn_first = Dom.create(
+ tf.fltTypeInp,
+ ['id', this.prfxBtnFirst+tf.id],
+ ['type', 'button'],
+ ['value', this.btnFirstPageText],
+ ['title', 'First']
+ );
+ btn_first.className = this.btnPageCssClass;
+ Event.add(btn_first, 'click', evt.first);
+ btnFirstSpan.appendChild(btn_first);
+ } else {
+ btnFirstSpan.innerHTML = this.btnFirstPageHtml;
+ Event.add(btnFirstSpan, 'click', evt.first);
+ }
+ }
+
+ // paging elements (buttons+drop-down list) are added to defined element
+ if(!this.pagingTgtId){
+ tf.setToolbar();
+ }
+ var targetEl = !this.pagingTgtId ? tf.mDiv : Dom.id(this.pagingTgtId);
+ targetEl.appendChild(btnFirstSpan);
+ targetEl.appendChild(btnPrevSpan);
+
+ var pgBeforeSpan = Dom.create(
+ 'span',['id', this.prfxPgBeforeSpan+tf.id] );
+ pgBeforeSpan.appendChild( Dom.text(this.pageText) );
+ pgBeforeSpan.className = this.nbPgSpanCssClass;
+ targetEl.appendChild(pgBeforeSpan);
+ targetEl.appendChild(slcPages);
+ var pgAfterSpan = Dom.create(
+ 'span',['id', this.prfxPgAfterSpan+tf.id]);
+ pgAfterSpan.appendChild( Dom.text(this.ofText) );
+ pgAfterSpan.className = this.nbPgSpanCssClass;
+ targetEl.appendChild(pgAfterSpan);
+ var pgspan = Dom.create( 'span',['id', this.prfxPgSpan+tf.id] );
+ pgspan.className = this.nbPgSpanCssClass;
+ pgspan.appendChild( Dom.text(' '+this.nbPages+' ') );
+ targetEl.appendChild(pgspan);
+ targetEl.appendChild(btnNextSpan);
+ targetEl.appendChild(btnLastSpan);
+ this.pagingSlc = Dom.id(this.prfxSlcPages+tf.id);
+
+ if(!tf.rememberGridValues){
+ this.setPagingInfo();
+ }
+ if(!tf.fltGrid){
+ tf.validateAllRows();
+ this.setPagingInfo(tf.validRowsIndex);
+ }
+
+ this.initialized = true;
+ }
+
+ /**
+ * Reset paging when filters are already instantiated
+ * @param {Boolean} filterTable Execute filtering once paging instanciated
+ */
+ reset(filterTable=false){
+ var tf = this.tf;
+ if(!tf.hasGrid() || this.isEnabled()){
+ return;
+ }
+ this.enable();
+ this.init();
+ tf.resetValues();
+ if(filterTable){
+ tf.filter();
+ }
+ }
+
+ /**
+ * Calculate number of pages based on valid rows
+ * Refresh paging select according to number of pages
+ * @param {Array} validRows Collection of valid rows
+ */
+ setPagingInfo(validRows=[]){
+ var tf = this.tf;
+ var rows = tf.tbl.rows;
+ var mdiv = !this.pagingTgtId ? tf.mDiv : Dom.id(this.pagingTgtId);
+ var pgspan = Dom.id(this.prfxPgSpan+tf.id);
+
+ //store valid rows indexes
+ tf.validRowsIndex = validRows;
+
+ if(validRows.length === 0){
+ //counts rows to be grouped
+ for(var j=tf.refRow; j<tf.nbRows; j++){
+ var row = rows[j];
+ if(!row){
+ continue;
+ }
+
+ var isRowValid = row.getAttribute('validRow');
+ if(Types.isNull(isRowValid) || Boolean(isRowValid==='true')){
+ tf.validRowsIndex.push(j);
+ }
+ }
+ }
+
+ //calculate nb of pages
+ this.nbPages = Math.ceil(tf.validRowsIndex.length/this.pagingLength);
+ //refresh page nb span
+ pgspan.innerHTML = this.nbPages;
+ //select clearing shortcut
+ if(this.pageSelectorType === tf.fltTypeSlc){
+ this.pagingSlc.innerHTML = '';
+ }
+
+ if(this.nbPages>0){
+ mdiv.style.visibility = 'visible';
+ if(this.pageSelectorType === tf.fltTypeSlc){
+ for(var z=0; z<this.nbPages; z++){
+ var opt = Dom.createOpt(z+1, z*this.pagingLength, false);
+ this.pagingSlc.options[z] = opt;
+ }
+ } else{
+ //input type
+ this.pagingSlc.value = this.currentPageNb;
+ }
+
+ } else {
+ /*** if no results paging select and buttons are hidden ***/
+ mdiv.style.visibility = 'hidden';
+ }
+ this.groupByPage(tf.validRowsIndex);
+ }
+
+ /**
+ * Group table rows by page and display valid rows
+ * @param {Array} validRows Collection of valid rows
+ */
+ groupByPage(validRows){
+ var tf = this.tf;
+ var alternateRows = tf.feature('alternateRows');
+ var rows = tf.tbl.rows;
+ var endPagingRow = parseInt(this.startPagingRow, 10) +
+ parseInt(this.pagingLength, 10);
+
+ //store valid rows indexes
+ if(validRows){
+ tf.validRowsIndex = validRows;
+ }
+
+ //this loop shows valid rows of current page
+ for(var h=0, len=tf.validRowsIndex.length; h<len; h++){
+ var validRowIdx = tf.validRowsIndex[h];
+ var r = rows[validRowIdx];
+ var isRowValid = r.getAttribute('validRow');
+
+ if(h>=this.startPagingRow && h<endPagingRow){
+ if(Types.isNull(isRowValid) || Boolean(isRowValid==='true')){
+ r.style.display = '';
+ }
+ if(tf.alternateRows && alternateRows){
+ alternateRows.setRowBg(validRowIdx, h);
+ }
+ } else {
+ r.style.display = 'none';
+ if(tf.alternateRows && alternateRows){
+ alternateRows.removeRowBg(validRowIdx);
+ }
+ }
+ }
+
+ tf.nbVisibleRows = tf.validRowsIndex.length;
+ //re-applies filter behaviours after filtering process
+ tf.applyProps();
+ }
+
+ /**
+ * Return the current page number
+ * @return {Number} Page number
+ */
+ getPage(){
+ return this.currentPageNb;
+ }
+
+ /**
+ * Show page based on passed param value (string or number):
+ * @param {String} or {Number} cmd possible string values: 'next',
+ * 'previous', 'last', 'first' or page number as per param
+ */
+ setPage(cmd){
+ var tf = this.tf;
+ if(!tf.hasGrid() || !this.isEnabled()){
+ return;
+ }
+ var btnEvt = this.evt,
+ cmdtype = typeof cmd;
+ if(cmdtype==='string'){
+ switch(Str.lower(cmd)){
+ case 'next':
+ btnEvt.next();
+ break;
+ case 'previous':
+ btnEvt.prev();
+ break;
+ case 'last':
+ btnEvt.last();
+ break;
+ case 'first':
+ btnEvt.first();
+ break;
+ default:
+ btnEvt.next();
+ break;
+ }
+ }
+ else if(cmdtype==='number'){
+ this.changePage(cmd-1);
+ }
+ }
+
+ /**
+ * Generates UI elements for the number of results per page drop-down
+ */
+ setResultsPerPage(){
+ var tf = this.tf;
+ var evt = this.evt;
+
+ if(!tf.hasGrid() && !tf.isFirstLoad){
+ return;
+ }
+ if(this.resultsPerPageSlc || !this.resultsPerPage){
+ return;
+ }
+
+ evt.slcResultsChange = (ev) => {
+ this.changeResultsPerPage();
+ ev.target.blur();
+ };
+
+ var slcR = Dom.create(
+ tf.fltTypeSlc, ['id', this.prfxSlcResults+tf.id]);
+ slcR.className = this.resultsSlcCssClass;
+ var slcRText = this.resultsPerPage[0],
+ slcROpts = this.resultsPerPage[1];
+ var slcRSpan = Dom.create(
+ 'span',['id', this.prfxSlcResultsTxt+tf.id]);
+ slcRSpan.className = this.resultsSpanCssClass;
+
+ // results per page select is added to external element
+ if(!this.resultsPerPageTgtId){
+ tf.setToolbar();
+ }
+ var targetEl = !this.resultsPerPageTgtId ?
+ tf.rDiv : Dom.id(this.resultsPerPageTgtId);
+ slcRSpan.appendChild(Dom.text(slcRText));
+
+ var help = tf.feature('help');
+ if(help && help.btn){
+ help.btn.parentNode.insertBefore(slcRSpan, help.btn);
+ help.btn.parentNode.insertBefore(slcR, help.btn);
+ } else {
+ targetEl.appendChild(slcRSpan);
+ targetEl.appendChild(slcR);
+ }
+
+ for(var r=0; r<slcROpts.length; r++){
+ var currOpt = new Option(slcROpts[r], slcROpts[r], false, false);
+ slcR.options[r] = currOpt;
+ }
+ Event.add(slcR, 'change', evt.slcResultsChange);
+ this.resultsPerPageSlc = slcR;
+ }
+
+ /**
+ * Remove number of results per page UI elements
+ */
+ removeResultsPerPage(){
+ var tf = this.tf;
+ if(!tf.hasGrid() || !this.resultsPerPageSlc || !this.resultsPerPage){
+ return;
+ }
+ var slcR = this.resultsPerPageSlc,
+ slcRSpan = Dom.id(this.prfxSlcResultsTxt+tf.id);
+ if(slcR){
+ slcR.parentNode.removeChild(slcR);
+ }
+ if(slcRSpan){
+ slcRSpan.parentNode.removeChild(slcRSpan);
+ }
+ this.resultsPerPageSlc = null;
+ }
+
+ /**
+ * Change the page asynchronously according to passed index
+ * @param {Number} index Index of the page (0-n)
+ */
+ changePage(index){
+ var tf = this.tf;
+ var evt = tf.Evt;
+ tf.EvtManager(evt.name.changepage, { pgIndex:index });
+ }
+
+ /**
+ * Change rows asynchronously according to page results
+ */
+ changeResultsPerPage(){
+ var tf = this.tf;
+ var evt = tf.Evt;
+ tf.EvtManager(evt.name.changeresultsperpage);
+ }
+
+ /**
+ * Re-set asynchronously page nb at page re-load
+ */
+ resetPage(){
+ var tf = this.tf;
+ var evt = tf.Evt;
+ tf.EvtManager(evt.name.resetpage);
+ }
+
+ /**
+ * Re-set asynchronously page length at page re-load
+ */
+ resetPageLength(){
+ var tf = this.tf;
+ var evt = tf.Evt;
+ tf.EvtManager(evt.name.resetpagelength);
+ }
+
+ /**
+ * Change the page according to passed index
+ * @param {Number} index Index of the page (0-n)
+ */
+ _changePage(index){
+ var tf = this.tf;
+
+ if(!this.isEnabled()){
+ return;
+ }
+ if(index === null){
+ index = this.pageSelectorType===tf.fltTypeSlc ?
+ this.pagingSlc.options.selectedIndex : (this.pagingSlc.value-1);
+ }
+ if( index>=0 && index<=(this.nbPages-1) ){
+ if(this.onBeforeChangePage){
+ this.onBeforeChangePage.call(null, this, index);
+ }
+ this.currentPageNb = parseInt(index, 10)+1;
+ if(this.pageSelectorType===tf.fltTypeSlc){
+ this.pagingSlc.options[index].selected = true;
+ } else {
+ this.pagingSlc.value = this.currentPageNb;
+ }
+
+ if(tf.rememberPageNb){
+ tf.feature('store').savePageNb(tf.pgNbCookie);
+ }
+ this.startPagingRow = (this.pageSelectorType===tf.fltTypeSlc) ?
+ this.pagingSlc.value : (index*this.pagingLength);
+
+ this.groupByPage();
+
+ if(this.onAfterChangePage){
+ this.onAfterChangePage.call(null, this, index);
+ }
+ }
+ }
+
+ /**
+ * Change rows according to page results drop-down
+ * TODO: accept a parameter setting the results per page length
+ */
+ _changeResultsPerPage(){
+ var tf = this.tf;
+
+ if(!this.isEnabled()){
+ return;
+ }
+ var slcR = this.resultsPerPageSlc;
+ var slcPagesSelIndex = (this.pageSelectorType===tf.fltTypeSlc) ?
+ this.pagingSlc.selectedIndex :
+ parseInt(this.pagingSlc.value-1, 10);
+ this.pagingLength = parseInt(slcR.options[slcR.selectedIndex].value,10);
+ this.startPagingRow = this.pagingLength*slcPagesSelIndex;
+
+ if(!isNaN(this.pagingLength)){
+ if(this.startPagingRow >= tf.nbFilterableRows){
+ this.startPagingRow = (tf.nbFilterableRows-this.pagingLength);
+ }
+ this.setPagingInfo();
+
+ if(this.pageSelectorType===tf.fltTypeSlc){
+ var slcIndex =
+ (this.pagingSlc.options.length-1<=slcPagesSelIndex ) ?
+ (this.pagingSlc.options.length-1) : slcPagesSelIndex;
+ this.pagingSlc.options[slcIndex].selected = true;
+ }
+ if(tf.rememberPageLen){
+ tf.feature('store').savePageLength(tf.pgLenCookie);
+ }
+ }
+ }
+
+ /**
+ * Re-set page nb at page re-load
+ */
+ _resetPage(name){
+ var tf = this.tf;
+ var pgnb = tf.feature('store').getPageNb(name);
+ if(pgnb!==''){
+ this.changePage((pgnb-1));
+ }
+ }
+
+ /**
+ * Re-set page length value at page re-load
+ */
+ _resetPageLength(name){
+ var tf = this.tf;
+ if(!this.isEnabled()){
+ return;
+ }
+ var pglenIndex = tf.feature('store').getPageLength(name);
+
+ if(pglenIndex!==''){
+ this.resultsPerPageSlc.options[pglenIndex].selected = true;
+ this.changeResultsPerPage();
+ }
+ }
+
+ /**
+ * Remove paging feature
+ */
+ destroy(){
+ var tf = this.tf;
+
+ if(!this.initialized){
+ return;
+ }
+ // btns containers
+ var btnNextSpan = Dom.id(this.prfxBtnNextSpan+tf.id);
+ var btnPrevSpan = Dom.id(this.prfxBtnPrevSpan+tf.id);
+ var btnLastSpan = Dom.id(this.prfxBtnLastSpan+tf.id);
+ var btnFirstSpan = Dom.id(this.prfxBtnFirstSpan+tf.id);
+ //span containing 'Page' text
+ var pgBeforeSpan = Dom.id(this.prfxPgBeforeSpan+tf.id);
+ //span containing 'of' text
+ var pgAfterSpan = Dom.id(this.prfxPgAfterSpan+tf.id);
+ //span containing nb of pages
+ var pgspan = Dom.id(this.prfxPgSpan+tf.id);
+
+ var evt = this.evt;
+
+ if(this.pagingSlc){
+ if(this.pageSelectorType === tf.fltTypeSlc){
+ Event.remove(this.pagingSlc, 'change', evt.slcPagesChange);
+ }
+ else if(this.pageSelectorType === tf.fltTypeInp){
+ Event.remove(this.pagingSlc, 'keypress', evt._detectKey);
+ }
+ this.pagingSlc.parentNode.removeChild(this.pagingSlc);
+ }
+
+ if(btnNextSpan){
+ Event.remove(btnNextSpan, 'click', evt.next);
+ btnNextSpan.parentNode.removeChild(btnNextSpan);
+ }
+
+ if(btnPrevSpan){
+ Event.remove(btnPrevSpan, 'click', evt.prev);
+ btnPrevSpan.parentNode.removeChild(btnPrevSpan);
+ }
+
+ if(btnLastSpan){
+ Event.remove(btnLastSpan, 'click', evt.last);
+ btnLastSpan.parentNode.removeChild(btnLastSpan);
+ }
+
+ if(btnFirstSpan){
+ Event.remove(btnFirstSpan, 'click', evt.first);
+ btnFirstSpan.parentNode.removeChild(btnFirstSpan);
+ }
+
+ if(pgBeforeSpan){
+ pgBeforeSpan.parentNode.removeChild(pgBeforeSpan);
+ }
+
+ if(pgAfterSpan){
+ pgAfterSpan.parentNode.removeChild(pgAfterSpan);
+ }
+
+ if(pgspan){
+ pgspan.parentNode.removeChild(pgspan);
+ }
+
+ if(this.hasResultsPerPage){
+ this.removeResultsPerPage();
+ }
+
+ this.pagingSlc = null;
+ this.nbPages = 0;
+ this.disable();
+ this.initialized = false;
+ }
+}
--- /dev/null
+import {Feature} from './feature';
+import Types from '../types';
+import Dom from '../dom';
+import Event from '../event';
+
+export class PopupFilter extends Feature{
+
+ /**
+ * Pop-up filter component
+ * @param {Object} tf TableFilter instance
+ */
+ constructor(tf){
+ super(tf, 'popupFilters');
+
+ // Configuration object
+ var f = this.config;
+
+ // Enable external filters behaviour
+ tf.isExternalFlt = true;
+ tf.externalFltTgtIds = [];
+
+ //filter icon path
+ this.popUpImgFlt = f.popup_filters_image ||
+ tf.themesPath+'icn_filter.gif';
+ //active filter icon path
+ this.popUpImgFltActive = f.popup_filters_image_active ||
+ tf.themesPath+'icn_filterActive.gif';
+ this.popUpImgFltHtml = f.popup_filters_image_html ||
+ '<img src="'+ this.popUpImgFlt +'" alt="Column filter" />';
+ //defines css class for popup div containing filter
+ this.popUpDivCssClass = f.popup_div_css_class || 'popUpFilter';
+ //callback function before popup filtes is opened
+ this.onBeforePopUpOpen = Types.isFn(f.on_before_popup_filter_open) ?
+ f.on_before_popup_filter_open : null;
+ //callback function after popup filtes is opened
+ this.onAfterPopUpOpen = Types.isFn(f.on_after_popup_filter_open) ?
+ f.on_after_popup_filter_open : null;
+ //callback function before popup filtes is closed
+ this.onBeforePopUpClose =
+ Types.isFn(f.on_before_popup_filter_close) ?
+ f.on_before_popup_filter_close : null;
+ //callback function after popup filtes is closed
+ this.onAfterPopUpClose = Types.isFn(f.on_after_popup_filter_close) ?
+ f.on_after_popup_filter_close : null;
+
+ //stores filters spans
+ this.popUpFltSpans = [];
+ //stores filters icons
+ this.popUpFltImgs = [];
+ //stores filters containers
+ this.popUpFltElms = this.popUpFltElmCache || [];
+ this.popUpFltAdjustToContainer = true;
+
+ //id prefix for pop-up filter span
+ this.prfxPopUpSpan = 'popUpSpan_';
+ //id prefix for pop-up div containing filter
+ this.prfxPopUpDiv = 'popUpDiv_';
+ }
+
+ onClick(e){
+ var evt = e || global.event,
+ elm = evt.target.parentNode,
+ colIndex = parseInt(elm.getAttribute('ci'), 10);
+
+ this.closeAll(colIndex);
+ this.toggle(colIndex);
+
+ if(this.popUpFltAdjustToContainer){
+ var popUpDiv = this.popUpFltElms[colIndex],
+ header = this.tf.getHeaderElement(colIndex),
+ headerWidth = header.clientWidth * 0.95;
+ popUpDiv.style.width = parseInt(headerWidth, 10) + 'px';
+ }
+ Event.cancel(evt);
+ Event.stop(evt);
+ }
+
+ /**
+ * Initialize DOM elements
+ */
+ init(){
+ if(this.initialized){
+ return;
+ }
+
+ var tf = this.tf;
+ for(var i=0; i<tf.nbCells; i++){
+ if(tf.getFilterType(i) === tf.fltTypeNone){
+ continue;
+ }
+ var popUpSpan = Dom.create(
+ 'span',
+ ['id', this.prfxPopUpSpan+tf.id+'_'+i],
+ ['ci', i]
+ );
+ popUpSpan.innerHTML = this.popUpImgFltHtml;
+ var header = tf.getHeaderElement(i);
+ header.appendChild(popUpSpan);
+ Event.add(popUpSpan, 'click', (evt) => { this.onClick(evt); });
+ this.popUpFltSpans[i] = popUpSpan;
+ this.popUpFltImgs[i] = popUpSpan.firstChild;
+ }
+
+ this.initialized = true;
+ }
+
+ /**
+ * Reset previously destroyed feature
+ */
+ reset(){
+ this.enable();
+ this.init();
+ this.buildAll();
+ }
+
+ /**
+ * Build all pop-up filters elements
+ */
+ buildAll(){
+ for(var i=0; i<this.popUpFltElmCache.length; i++){
+ this.build(i, this.popUpFltElmCache[i]);
+ }
+ }
+
+ /**
+ * Build a specified pop-up filter elements
+ * @param {Number} colIndex Column index
+ * @param {Object} div Optional container DOM element
+ */
+ build(colIndex, div){
+ var tf = this.tf;
+ var popUpDiv = !div ?
+ Dom.create('div', ['id', this.prfxPopUpDiv+tf.id+'_'+colIndex]) :
+ div;
+ popUpDiv.className = this.popUpDivCssClass;
+ tf.externalFltTgtIds.push(popUpDiv.id);
+ var header = tf.getHeaderElement(colIndex);
+ header.insertBefore(popUpDiv, header.firstChild);
+ Event.add(popUpDiv, 'click', (evt) => { Event.stop(evt); });
+ this.popUpFltElms[colIndex] = popUpDiv;
+ }
+
+ /**
+ * Toogle visibility of specified filter
+ * @param {Number} colIndex Column index
+ */
+ toggle(colIndex){
+ var tf = this.tf,
+ popUpFltElm = this.popUpFltElms[colIndex];
+
+ if(popUpFltElm.style.display === 'none' ||
+ popUpFltElm.style.display === ''){
+ if(this.onBeforePopUpOpen){
+ this.onBeforePopUpOpen.call(
+ null, this, this.popUpFltElms[colIndex], colIndex);
+ }
+ popUpFltElm.style.display = 'block';
+ if(tf.getFilterType(colIndex) === tf.fltTypeInp){
+ var flt = tf.getFilterElement(colIndex);
+ if(flt){
+ flt.focus();
+ }
+ }
+ if(this.onAfterPopUpOpen){
+ this.onAfterPopUpOpen.call(
+ null, this, this.popUpFltElms[colIndex], colIndex);
+ }
+ } else {
+ if(this.onBeforePopUpClose){
+ this.onBeforePopUpClose.call(
+ null, this, this.popUpFltElms[colIndex], colIndex);
+ }
+ popUpFltElm.style.display = 'none';
+ if(this.onAfterPopUpClose){
+ this.onAfterPopUpClose.call(
+ null, this, this.popUpFltElms[colIndex], colIndex);
+ }
+ }
+ }
+
+ /**
+ * Close all filters excepted for the specified one if any
+ * @param {Number} exceptIdx Column index of the filter to not close
+ */
+ closeAll(exceptIdx){
+ for(var i=0; i<this.popUpFltElms.length; i++){
+ if(i === exceptIdx){
+ continue;
+ }
+ var popUpFltElm = this.popUpFltElms[i];
+ if(popUpFltElm){
+ popUpFltElm.style.display = 'none';
+ }
+ }
+ }
+
+ /**
+ * Build all the icons representing the pop-up filters
+ */
+ buildIcons(){
+ for(var i=0; i<this.popUpFltImgs.length; i++){
+ this.buildIcon(i, false);
+ }
+ }
+
+ /**
+ * Build specified icon
+ * @param {Number} colIndex Column index
+ * @param {Boolean} active Apply active state
+ */
+ buildIcon(colIndex, active){
+ if(this.popUpFltImgs[colIndex]){
+ this.popUpFltImgs[colIndex].src = active ?
+ this.popUpImgFltActive : this.popUpImgFlt;
+ }
+ }
+
+ /**
+ * Remove pop-up filters
+ */
+ destroy(){
+ if(!this.initialized){
+ return;
+ }
+
+ this.popUpFltElmCache = [];
+ for(var i=0; i<this.popUpFltElms.length; i++){
+ var popUpFltElm = this.popUpFltElms[i],
+ popUpFltSpan = this.popUpFltSpans[i],
+ popUpFltImg = this.popUpFltImgs[i];
+ if(popUpFltElm){
+ popUpFltElm.parentNode.removeChild(popUpFltElm);
+ this.popUpFltElmCache[i] = popUpFltElm;
+ }
+ popUpFltElm = null;
+ if(popUpFltSpan){
+ popUpFltSpan.parentNode.removeChild(popUpFltSpan);
+ }
+ popUpFltSpan = null;
+ if(popUpFltImg){
+ popUpFltImg.parentNode.removeChild(popUpFltImg);
+ }
+ popUpFltImg = null;
+ }
+ this.popUpFltElms = [];
+ this.popUpFltSpans = [];
+ this.popUpFltImgs = [];
+
+ this.disable();
+ this.initialized = false;
+ }
+
+}
--- /dev/null
+import {Feature} from './feature';
+import Dom from '../dom';
+import Types from '../types';
+
+export class RowsCounter extends Feature{
+
+ /**
+ * Rows counter
+ * @param {Object} tf TableFilter instance
+ */
+ constructor(tf){
+ super(tf, 'rowsCounter');
+
+ // TableFilter configuration
+ var f = this.config;
+
+ //id of custom container element
+ this.rowsCounterTgtId = f.rows_counter_target_id || null;
+ //element containing tot nb rows
+ this.rowsCounterDiv = null;
+ //element containing tot nb rows label
+ this.rowsCounterSpan = null;
+ //defines rows counter text
+ this.rowsCounterText = f.rows_counter_text || 'Rows: ';
+ this.fromToTextSeparator = f.from_to_text_separator || '-';
+ this.overText = f.over_text || ' / ';
+ //defines css class rows counter
+ this.totRowsCssClass = f.tot_rows_css_class || 'tot';
+ //rows counter div
+ this.prfxCounter = 'counter_';
+ //nb displayed rows label
+ this.prfxTotRows = 'totrows_span_';
+ //label preceding nb rows label
+ this.prfxTotRowsTxt = 'totRowsTextSpan_';
+ //callback raised before counter is refreshed
+ this.onBeforeRefreshCounter = Types.isFn(f.on_before_refresh_counter) ?
+ f.on_before_refresh_counter : null;
+ //callback raised after counter is refreshed
+ this.onAfterRefreshCounter = Types.isFn(f.on_after_refresh_counter) ?
+ f.on_after_refresh_counter : null;
+ }
+
+ init(){
+ if(this.initialized){
+ return;
+ }
+
+ var tf = this.tf;
+
+ //rows counter container
+ var countDiv = Dom.create('div', ['id', this.prfxCounter+tf.id]);
+ countDiv.className = this.totRowsCssClass;
+ //rows counter label
+ var countSpan = Dom.create('span', ['id', this.prfxTotRows+tf.id]);
+ var countText = Dom.create('span', ['id', this.prfxTotRowsTxt+tf.id]);
+ countText.appendChild(Dom.text(this.rowsCounterText));
+
+ // counter is added to defined element
+ if(!this.rowsCounterTgtId){
+ tf.setToolbar();
+ }
+ var targetEl = !this.rowsCounterTgtId ?
+ tf.lDiv : Dom.id( this.rowsCounterTgtId );
+
+ //default container: 'lDiv'
+ if(!this.rowsCounterTgtId){
+ countDiv.appendChild(countText);
+ countDiv.appendChild(countSpan);
+ targetEl.appendChild(countDiv);
+ }
+ else{
+ //custom container, no need to append statusDiv
+ targetEl.appendChild(countText);
+ targetEl.appendChild(countSpan);
+ }
+ this.rowsCounterDiv = countDiv;
+ this.rowsCounterSpan = countSpan;
+
+ this.initialized = true;
+ this.refresh();
+ }
+
+ refresh(p){
+ if(!this.rowsCounterSpan){
+ return;
+ }
+
+ var tf = this.tf;
+
+ if(this.onBeforeRefreshCounter){
+ this.onBeforeRefreshCounter.call(null, tf, this.rowsCounterSpan);
+ }
+
+ var totTxt;
+ if(!tf.paging){
+ if(p && p !== ''){
+ totTxt = p;
+ } else{
+ totTxt = tf.nbFilterableRows - tf.nbHiddenRows;
+ }
+ } else {
+ var paging = tf.feature('paging');
+ if(paging){
+ //paging start row
+ var paging_start_row = parseInt(paging.startPagingRow, 10) +
+ ((tf.nbVisibleRows>0) ? 1 : 0);
+ var paging_end_row = (paging_start_row+paging.pagingLength)-1 <=
+ tf.nbVisibleRows ?
+ paging_start_row+paging.pagingLength-1 :
+ tf.nbVisibleRows;
+ totTxt = paging_start_row + this.fromToTextSeparator +
+ paging_end_row + this.overText + tf.nbVisibleRows;
+ }
+ }
+
+ this.rowsCounterSpan.innerHTML = totTxt;
+ if(this.onAfterRefreshCounter){
+ this.onAfterRefreshCounter.call(
+ null, tf, this.rowsCounterSpan, totTxt);
+ }
+ }
+
+ destroy(){
+ if(!this.initialized){
+ return;
+ }
+
+ if(!this.rowsCounterTgtId && this.rowsCounterDiv){
+ this.rowsCounterDiv.parentNode.removeChild(this.rowsCounterDiv);
+ } else {
+ Dom.id(this.rowsCounterTgtId).innerHTML = '';
+ }
+ this.rowsCounterSpan = null;
+ this.rowsCounterDiv = null;
+
+ this.disable();
+ this.initialized = false;
+ }
+}
--- /dev/null
+import {Feature} from './feature';
+import Dom from '../dom';
+import Types from '../types';
+
+var global = window;
+
+export class StatusBar extends Feature{
+
+ /**
+ * Status bar UI component
+ * @param {Object} tf TableFilter instance
+ */
+ constructor(tf){
+ super(tf, 'statusBar');
+
+ // Configuration object
+ var f = this.config;
+
+ //id of custom container element
+ this.statusBarTgtId = f.status_bar_target_id || null;
+ //element containing status bar label
+ this.statusBarDiv = null;
+ //status bar
+ this.statusBarSpan = null;
+ //status bar label
+ this.statusBarSpanText = null;
+ //defines status bar text
+ this.statusBarText = f.status_bar_text || '';
+ //defines css class status bar
+ this.statusBarCssClass = f.status_bar_css_class || 'status';
+ //delay for status bar clearing
+ this.statusBarCloseDelay = 250;
+
+ //calls function before message is displayed
+ this.onBeforeShowMsg = Types.isFn(f.on_before_show_msg) ?
+ f.on_before_show_msg : null;
+ //calls function after message is displayed
+ this.onAfterShowMsg = Types.isFn(f.on_after_show_msg) ?
+ f.on_after_show_msg : null;
+
+ // status bar div
+ this.prfxStatus = 'status_';
+ // status bar label
+ this.prfxStatusSpan = 'statusSpan_';
+ // text preceding status bar label
+ this.prfxStatusTxt = 'statusText_';
+ }
+
+ init(){
+ if(this.initialized){
+ return;
+ }
+
+ var tf = this.tf;
+
+ //status bar container
+ var statusDiv = Dom.create('div', ['id', this.prfxStatus+tf.id]);
+ statusDiv.className = this.statusBarCssClass;
+
+ //status bar label
+ var statusSpan = Dom.create('span', ['id', this.prfxStatusSpan+tf.id]);
+ //preceding text
+ var statusSpanText = Dom.create('span',
+ ['id', this.prfxStatusTxt+tf.id]);
+ statusSpanText.appendChild(Dom.text(this.statusBarText));
+
+ // target element container
+ if(!this.statusBarTgtId){
+ tf.setToolbar();
+ }
+ var targetEl = (!this.statusBarTgtId) ?
+ tf.lDiv : Dom.id(this.statusBarTgtId);
+
+ //default container: 'lDiv'
+ if(!this.statusBarTgtId){
+ statusDiv.appendChild(statusSpanText);
+ statusDiv.appendChild(statusSpan);
+ targetEl.appendChild(statusDiv);
+ } else {
+ // custom container, no need to append statusDiv
+ targetEl.appendChild(statusSpanText);
+ targetEl.appendChild(statusSpan);
+ }
+
+ this.statusBarDiv = statusDiv;
+ this.statusBarSpan = statusSpan;
+ this.statusBarSpanText = statusSpanText;
+
+ this.initialized = true;
+ }
+
+ message(t=''){
+ if(!this.isEnabled()){
+ return;
+ }
+
+ if(this.onBeforeShowMsg){
+ this.onBeforeShowMsg.call(null, this.tf, t);
+ }
+
+ var d = t==='' ? this.statusBarCloseDelay : 1;
+ global.setTimeout(() => {
+ this.statusBarSpan.innerHTML = t;
+ if(this.onAfterShowMsg){
+ this.onAfterShowMsg.call(null, this.tf, t);
+ }
+ }, d);
+ }
+
+ destroy(){
+ if(!this.initialized){
+ return;
+ }
+
+ this.statusBarDiv.innerHTML = '';
+ this.statusBarDiv.parentNode.removeChild(this.statusBarDiv);
+ this.statusBarSpan = null;
+ this.statusBarSpanText = null;
+ this.statusBarDiv = null;
+
+ this.disable();
+ this.initialized = false;
+ }
+
+}
--- /dev/null
+import Cookie from '../cookie';
+
+export class Store{
+
+ /**
+ * Store, persistence manager
+ * @param {Object} tf TableFilter instance
+ *
+ * TODO: use localStorage and fallback to cookie persistence
+ */
+ constructor(tf) {
+ var f = tf.config();
+
+ this.duration = !isNaN(f.set_cookie_duration) ?
+ parseInt(f.set_cookie_duration, 10) : 100000;
+
+ this.tf = tf;
+ }
+
+ /**
+ * Store filters' values in cookie
+ * @param {String} cookie name
+ */
+ saveFilterValues(name){
+ var tf = this.tf;
+ var fltValues = [];
+ //store filters' values
+ for(var i=0; i<tf.fltIds.length; i++){
+ var value = tf.getFilterValue(i);
+ if (value === ''){
+ value = ' ';
+ }
+ fltValues.push(value);
+ }
+ //adds array size
+ fltValues.push(tf.fltIds.length);
+
+ //writes cookie
+ Cookie.write(
+ name,
+ fltValues.join(tf.separator),
+ this.duration
+ );
+ }
+
+ /**
+ * Retrieve filters' values from cookie
+ * @param {String} cookie name
+ * @return {Array}
+ */
+ getFilterValues(name){
+ var flts = Cookie.read(name);
+ var rgx = new RegExp(this.tf.separator, 'g');
+ // filters' values array
+ return flts.split(rgx);
+ }
+
+ /**
+ * Store page number in cookie
+ * @param {String} cookie name
+ */
+ savePageNb(name){
+ Cookie.write(
+ name,
+ this.tf.feature('paging').currentPageNb,
+ this.duration
+ );
+ }
+
+ /**
+ * Retrieve page number from cookie
+ * @param {String} cookie name
+ * @return {String}
+ */
+ getPageNb(name){
+ return Cookie.read(name);
+ }
+
+ /**
+ * Store page length in cookie
+ * @param {String} cookie name
+ */
+ savePageLength(name){
+ Cookie.write(
+ name,
+ this.tf.feature('paging').resultsPerPageSlc.selectedIndex,
+ this.duration
+ );
+ }
+
+ /**
+ * Retrieve page length from cookie
+ * @param {String} cookie name
+ * @return {String}
+ */
+ getPageLength(name){
+ return Cookie.read(name);
+ }
+
+}
--- /dev/null
+import Str from './string';
+
+export default {
+ ignoreCase(a, b){
+ let x = Str.lower(a);
+ let y = Str.lower(b);
+ return ((x < y) ? -1 : ((x > y) ? 1 : 0));
+ }
+};
--- /dev/null
+/**
+ * String utilities
+ */
+
+export default {
+
+ lower(text){
+ return text.toLowerCase();
+ },
+
+ upper(text){
+ return text.toUpperCase();
+ },
+
+ trim(text){
+ if (text.trim){
+ return text.trim();
+ }
+ return text.replace(/^\s*|\s*$/g, '');
+ },
+
+ isEmpty(text){
+ return this.trim(text) === '';
+ },
+
+ rgxEsc(text){
+ let chars = /[-\/\\^$*+?.()|[\]{}]/g;
+ let escMatch = '\\$&';
+ return String(text).replace(chars, escMatch);
+ },
+
+ matchCase(text, mc){
+ if(!mc){
+ return this.lower(text);
+ }
+ return text;
+ }
+
+};
--- /dev/null
+import Event from './event';
+import Dom from './dom';
+import Str from './string';
+import Cookie from './cookie';
+import Types from './types';
+import Arr from './array';
+import DateHelper from './date';
+import Helpers from './helpers';
+
+// Features
+import {Store} from './modules/store';
+import {GridLayout} from './modules/gridLayout';
+import {Loader} from './modules/loader';
+import {HighlightKeyword} from './modules/highlightKeywords';
+import {PopupFilter} from './modules/popupFilter';
+import {Dropdown} from './modules/dropdown';
+import {CheckList} from './modules/checkList';
+import {RowsCounter} from './modules/rowsCounter';
+import {StatusBar} from './modules/statusBar';
+import {Paging} from './modules/paging';
+import {ClearButton} from './modules/clearButton';
+import {Help} from './modules/help';
+import {AlternateRows} from './modules/alternateRows';
+
+var global = window,
+ isValidDate = DateHelper.isValid,
+ formatDate = DateHelper.format,
+ doc = global.document;
+
+export class TableFilter{
+
+ /**
+ * TableFilter object constructor
+ * requires `table` or `id` arguments, `row` and `configuration` optional
+ * @param {DOMElement} table Table DOM element
+ * @param {String} id Table id
+ * @param {Number} row index indicating the 1st row
+ * @param {Object} configuration object
+ */
+ constructor(...args) {
+ if(args.length === 0){ return; }
+
+ this.id = null;
+ this.version = '{VERSION}';
+ this.year = new Date().getFullYear();
+ this.tbl = null;
+ this.startRow = null;
+ this.refRow = null;
+ this.headersRow = null;
+ this.cfg = {};
+ this.nbFilterableRows = null;
+ this.nbRows = null;
+ this.nbCells = null;
+ this._hasGrid = false;
+
+ // TODO: use for-of with babel plug-in
+ args.forEach((arg)=> {
+ let argtype = typeof arg;
+ if(argtype === 'object' && arg && arg.nodeName === 'TABLE'){
+ this.tbl = arg;
+ this.id = arg.id || `tf_${new Date().getTime()}_`;
+ } else if(argtype === 'string'){
+ this.id = arg;
+ this.tbl = Dom.id(arg);
+ } else if(argtype === 'number'){
+ this.startRow = arg;
+ } else if(argtype === 'object'){
+ this.cfg = arg;
+ }
+ });
+
+ if(!this.tbl || this.tbl.nodeName != 'TABLE' || this.getRowsNb() === 0){
+ throw new Error(
+ 'Could not instantiate TableFilter: HTML table not found.');
+ }
+
+ // configuration object
+ let f = this.cfg;
+
+ //Start row et cols nb
+ this.refRow = this.startRow === null ? 2 : (this.startRow+1);
+ try{ this.nbCells = this.getCellsNb(this.refRow); }
+ catch(e){ this.nbCells = this.getCellsNb(0); }
+
+ //default script base path
+ this.basePath = f.base_path || 'tablefilter/';
+
+ /*** filter types ***/
+ this.fltTypeInp = 'input';
+ this.fltTypeSlc = 'select';
+ this.fltTypeMulti = 'multiple';
+ this.fltTypeCheckList = 'checklist';
+ this.fltTypeNone = 'none';
+
+ /*** filters' grid properties ***/
+
+ //enables/disables filter grid
+ this.fltGrid = f.grid === false ? false : true;
+
+ //enables/disables grid layout (fixed headers)
+ this.gridLayout = Boolean(f.grid_layout);
+
+ this.filtersRowIndex = isNaN(f.filters_row_index) ?
+ 0 : f.filters_row_index;
+ this.headersRow = isNaN(f.headers_row_index) ?
+ (this.filtersRowIndex === 0 ? 1 : 0) : f.headers_row_index;
+
+ if(this.gridLayout){
+ if(this.headersRow > 1){
+ this.filtersRowIndex = this.headersRow+1;
+ } else {
+ this.filtersRowIndex = 1;
+ this.headersRow = 0;
+ }
+ }
+
+ //defines tag of the cells containing filters (td/th)
+ this.fltCellTag = f.filters_cell_tag!=='th' ||
+ f.filters_cell_tag!=='td' ? 'td' : f.filters_cell_tag;
+
+ //stores filters ids
+ this.fltIds = [];
+ //stores filters DOM elements
+ this.fltElms = [];
+ //stores filters values
+ this.searchArgs = null;
+ //stores valid rows indexes (rows visible upon filtering)
+ this.validRowsIndex = null;
+ //stores filters row element
+ this.fltGridEl = null;
+ //is first load boolean
+ this.isFirstLoad = true;
+ //container div for paging elements, reset btn etc.
+ this.infDiv = null;
+ //div for rows counter
+ this.lDiv = null;
+ //div for reset button and results per page select
+ this.rDiv = null;
+ //div for paging elements
+ this.mDiv = null;
+
+ //defines css class for div containing paging elements, rows counter etc
+ this.infDivCssClass = f.inf_div_css_class || 'inf';
+ //defines css class for left div
+ this.lDivCssClass = f.left_div_css_class || 'ldiv';
+ //defines css class for right div
+ this.rDivCssClass = f.right_div_css_class || 'rdiv';
+ //defines css class for mid div
+ this.mDivCssClass = f.middle_div_css_class || 'mdiv';
+ //table container div css class
+ this.contDivCssClass = f.content_div_css_class || 'cont';
+
+ /*** filters' grid appearance ***/
+ //stylesheet file
+ this.stylePath = f.style_path || this.basePath + 'style/';
+ this.stylesheet = f.stylesheet || this.stylePath+'tablefilter.css';
+ this.stylesheetId = this.id + '_style';
+ //defines css class for filters row
+ this.fltsRowCssClass = f.flts_row_css_class || 'fltrow';
+ //enables/disables icons (paging, reset button)
+ this.enableIcons = f.enable_icons===false ? false : true;
+ //enables/disbles rows alternating bg colors
+ this.alternateRows = Boolean(f.alternate_rows);
+ //defines widths of columns
+ this.hasColWidths = Types.isArray(f.col_widths);
+ this.colWidths = this.hasColWidths ? f.col_widths : null;
+ //defines css class for filters
+ this.fltCssClass = f.flt_css_class || 'flt';
+ //defines css class for multiple selects filters
+ this.fltMultiCssClass = f.flt_multi_css_class || 'flt_multi';
+ //defines css class for filters
+ this.fltSmallCssClass = f.flt_small_css_class || 'flt_s';
+ //defines css class for single-filter
+ this.singleFltCssClass = f.single_flt_css_class || 'single_flt';
+
+ /*** filters' grid behaviours ***/
+ //enables/disables enter key
+ this.enterKey = f.enter_key===false ? false : true;
+ //calls function before filtering starts
+ this.onBeforeFilter = Types.isFn(f.on_before_filter) ?
+ f.on_before_filter : null;
+ //calls function after filtering
+ this.onAfterFilter = Types.isFn(f.on_after_filter) ?
+ f.on_after_filter : null;
+ //enables/disables case sensitivity
+ this.caseSensitive = Boolean(f.case_sensitive);
+ //has exact match per column
+ this.hasExactMatchByCol = Types.isArray(f.columns_exact_match);
+ this.exactMatchByCol = this.hasExactMatchByCol ?
+ f.columns_exact_match : [];
+ //enables/disbles exact match for search
+ this.exactMatch = Boolean(f.exact_match);
+ //refreshes drop-down lists upon validation
+ this.linkedFilters = Boolean(f.linked_filters);
+ //wheter excluded options are disabled
+ this.disableExcludedOptions = Boolean(f.disable_excluded_options);
+ //stores active filter element
+ this.activeFlt = null;
+ //id of active filter
+ this.activeFilterId = null;
+ //enables always visible rows
+ this.hasVisibleRows = Boolean(f.rows_always_visible);
+ //array containing always visible rows
+ this.visibleRows = this.hasVisibleRows ? f.rows_always_visible : [];
+ //enables/disables external filters generation
+ this.isExternalFlt = Boolean(f.external_flt_grid);
+ //array containing ids of external elements containing filters
+ this.externalFltTgtIds = f.external_flt_grid_ids || null;
+ //stores filters elements if isExternalFlt is true
+ this.externalFltEls = [];
+ //delays any filtering process if loader true
+ this.execDelay = !isNaN(f.exec_delay) ? parseInt(f.exec_delay,10) : 100;
+ //calls function when filters grid loaded
+ this.onFiltersLoaded = Types.isFn(f.on_filters_loaded) ?
+ f.on_filters_loaded : null;
+ //enables/disables single filter search
+ this.singleSearchFlt = Boolean(f.single_filter);
+ //calls function after row is validated
+ this.onRowValidated = Types.isFn(f.on_row_validated) ?
+ f.on_row_validated : null;
+ //array defining columns for customCellData event
+ this.customCellDataCols = f.custom_cell_data_cols ?
+ f.custom_cell_data_cols : [];
+ //calls custom function for retrieving cell data
+ this.customCellData = Types.isFn(f.custom_cell_data) ?
+ f.custom_cell_data : null;
+ //input watermark text array
+ this.watermark = f.watermark || '';
+ this.isWatermarkArray = Types.isArray(this.watermark);
+ //id of toolbar container element
+ this.toolBarTgtId = f.toolbar_target_id || null;
+ //enables/disables help div
+ this.help = Types.isUndef(f.help_instructions) ?
+ undefined : Boolean(f.help_instructions);
+ //popup filters
+ this.popupFilters = Boolean(f.popup_filters);
+ //active columns color
+ this.markActiveColumns = Boolean(f.mark_active_columns);
+ //defines css class for active column header
+ this.activeColumnsCssClass = f.active_columns_css_class ||
+ 'activeHeader';
+ //calls function before active column header is marked
+ this.onBeforeActiveColumn = Types.isFn(f.on_before_active_column) ?
+ f.on_before_active_column : null;
+ //calls function after active column header is marked
+ this.onAfterActiveColumn = Types.isFn(f.on_after_active_column) ?
+ f.on_after_active_column : null;
+
+ /*** select filter's customisation and behaviours ***/
+ //defines 1st option text
+ this.displayAllText = f.display_all_text || 'Clear';
+ //enables/disables empty option in combo-box filters
+ this.enableEmptyOption = Boolean(f.enable_empty_option);
+ //defines empty option text
+ this.emptyText = f.empty_text || '(Empty)';
+ //enables/disables non empty option in combo-box filters
+ this.enableNonEmptyOption = Boolean(f.enable_non_empty_option);
+ //defines empty option text
+ this.nonEmptyText = f.non_empty_text || '(Non empty)';
+ //enables/disables onChange event on combo-box
+ this.onSlcChange = f.on_change===false ? false : true;
+ //enables/disables select options sorting
+ this.sortSlc = f.sort_select===false ? false : true;
+ //enables/disables ascending numeric options sorting
+ this.isSortNumAsc = Boolean(f.sort_num_asc);
+ this.sortNumAsc = this.isSortNumAsc ? f.sort_num_asc : null;
+ //enables/disables descending numeric options sorting
+ this.isSortNumDesc = Boolean(f.sort_num_desc);
+ this.sortNumDesc = this.isSortNumDesc ? f.sort_num_desc : null;
+ //Select filters are populated on demand
+ this.loadFltOnDemand = Boolean(f.load_filters_on_demand);
+ this.hasCustomOptions = Types.isObj(f.custom_options);
+ this.customOptions = f.custom_options;
+
+ /*** Filter operators ***/
+ this.rgxOperator = f.regexp_operator || 'rgx:';
+ this.emOperator = f.empty_operator || '[empty]';
+ this.nmOperator = f.nonempty_operator || '[nonempty]';
+ this.orOperator = f.or_operator || '||';
+ this.anOperator = f.and_operator || '&&';
+ this.grOperator = f.greater_operator || '>';
+ this.lwOperator = f.lower_operator || '<';
+ this.leOperator = f.lower_equal_operator || '<=';
+ this.geOperator = f.greater_equal_operator || '>=';
+ this.dfOperator = f.different_operator || '!';
+ this.lkOperator = f.like_operator || '*';
+ this.eqOperator = f.equal_operator || '=';
+ this.stOperator = f.start_with_operator || '{';
+ this.enOperator = f.end_with_operator || '}';
+ this.curExp = f.cur_exp || '^[¥£€$]';
+ this.separator = f.separator || ',';
+
+ /*** rows counter ***/
+ //show/hides rows counter
+ this.rowsCounter = Boolean(f.rows_counter);
+
+ /*** status bar ***/
+ //show/hides status bar
+ this.statusBar = Boolean(f.status_bar);
+
+ /*** loader ***/
+ //enables/disables loader/spinner indicator
+ this.loader = Boolean(f.loader);
+
+ /*** validation - reset buttons/links ***/
+ //show/hides filter's validation button
+ this.displayBtn = Boolean(f.btn);
+ //defines validation button text
+ this.btnText = f.btn_text || (!this.enableIcons ? 'Go' : '');
+ //defines css class for validation button
+ this.btnCssClass = f.btn_css_class ||
+ (!this.enableIcons ? 'btnflt' : 'btnflt_icon');
+ //show/hides reset link
+ this.btnReset = Boolean(f.btn_reset);
+ //defines css class for reset button
+ this.btnResetCssClass = f.btn_reset_css_class || 'reset';
+ //callback function before filters are cleared
+ this.onBeforeReset = Types.isFn(f.on_before_reset) ?
+ f.on_before_reset : null;
+ //callback function after filters are cleared
+ this.onAfterReset = Types.isFn(f.on_after_reset) ?
+ f.on_after_reset : null;
+
+ /*** paging ***/
+ //enables/disables table paging
+ this.paging = Boolean(f.paging);
+ this.nbVisibleRows = 0; //nb visible rows
+ this.nbHiddenRows = 0; //nb hidden rows
+
+ /*** autofilter on typing ***/
+ //enables/disables auto filtering, table is filtered when user stops
+ //typing
+ this.autoFilter = Boolean(f.auto_filter);
+ //onkeyup delay timer (msecs)
+ this.autoFilterDelay = !isNaN(f.auto_filter_delay) ?
+ f.auto_filter_delay : 900;
+ //typing indicator
+ this.isUserTyping = null;
+ this.autoFilterTimer = null;
+
+ /*** keyword highlighting ***/
+ //enables/disables keyword highlighting
+ this.highlightKeywords = Boolean(f.highlight_keywords);
+
+ /*** data types ***/
+ //defines default date type (european DMY)
+ this.defaultDateType = f.default_date_type || 'DMY';
+ //defines default thousands separator
+ //US = ',' EU = '.'
+ this.thousandsSeparator = f.thousands_separator || ',';
+ //defines default decimal separator
+ //US & javascript = '.' EU = ','
+ this.decimalSeparator = f.decimal_separator || '.';
+ //enables number format per column
+ this.hasColNbFormat = Types.isArray(f.col_number_format);
+ //array containing columns nb formats
+ this.colNbFormat = this.hasColNbFormat ? f.col_number_format : null;
+ //enables date type per column
+ this.hasColDateType = Types.isArray(f.col_date_type);
+ //array containing columns date type
+ this.colDateType = this.hasColDateType ? f.col_date_type : null;
+
+ /*** status messages ***/
+ //filtering
+ this.msgFilter = f.msg_filter || 'Filtering data...';
+ //populating drop-downs
+ this.msgPopulate = f.msg_populate || 'Populating filter...';
+ //populating drop-downs
+ this.msgPopulateCheckList = f.msg_populate_checklist ||
+ 'Populating list...';
+ //changing paging page
+ this.msgChangePage = f.msg_change_page || 'Collecting paging data...';
+ //clearing filters
+ this.msgClear = f.msg_clear || 'Clearing filters...';
+ //changing nb results/page
+ this.msgChangeResults = f.msg_change_results ||
+ 'Changing results per page...';
+ //re-setting grid values
+ this.msgResetValues = f.msg_reset_grid_values ||
+ 'Re-setting filters values...';
+ //re-setting page
+ this.msgResetPage = f.msg_reset_page || 'Re-setting page...';
+ //re-setting page length
+ this.msgResetPageLength = f.msg_reset_page_length ||
+ 'Re-setting page length...';
+ //table sorting
+ this.msgSort = f.msg_sort || 'Sorting data...';
+ //extensions loading
+ this.msgLoadExtensions = f.msg_load_extensions ||
+ 'Loading extensions...';
+ //themes loading
+ this.msgLoadThemes = f.msg_load_themes || 'Loading theme(s)...';
+
+ /*** ids prefixes ***/
+ //css class name added to table
+ this.prfxTf = 'TF';
+ //filters (inputs - selects)
+ this.prfxFlt = 'flt';
+ //validation button
+ this.prfxValButton = 'btn';
+ //container div for paging elements, rows counter etc.
+ this.prfxInfDiv = 'inf_';
+ //left div
+ this.prfxLDiv = 'ldiv_';
+ //right div
+ this.prfxRDiv = 'rdiv_';
+ //middle div
+ this.prfxMDiv = 'mdiv_';
+ //filter values cookie
+ this.prfxCookieFltsValues = 'tf_flts_';
+ //page nb cookie
+ this.prfxCookiePageNb = 'tf_pgnb_';
+ //page length cookie
+ this.prfxCookiePageLen = 'tf_pglen_';
+
+ /*** cookies ***/
+ this.hasStoredValues = false;
+ //remembers filters values on page load
+ this.rememberGridValues = Boolean(f.remember_grid_values);
+ //cookie storing filter values
+ this.fltsValuesCookie = this.prfxCookieFltsValues + this.id;
+ //remembers page nb on page load
+ this.rememberPageNb = this.paging && f.remember_page_number;
+ //cookie storing page nb
+ this.pgNbCookie = this.prfxCookiePageNb + this.id;
+ //remembers page length on page load
+ this.rememberPageLen = this.paging && f.remember_page_length;
+ //cookie storing page length
+ this.pgLenCookie = this.prfxCookiePageLen + this.id;
+
+ /*** extensions ***/
+ //imports external script
+ this.extensions = f.extensions;
+ this.hasExtensions = Types.isArray(this.extensions);
+
+ /*** themes ***/
+ this.enableDefaultTheme = Boolean(f.enable_default_theme);
+ //imports themes
+ this.hasThemes = (this.enableDefaultTheme || Types.isArray(f.themes));
+ this.themes = f.themes || [];
+ //themes path
+ this.themesPath = f.themes_path || this.stylePath + 'themes/';
+
+ // Features registry
+ this.Mod = {};
+
+ // Extensions registry
+ this.ExtRegistry = {};
+
+ /*** TF events ***/
+ this.Evt = {
+ name: {
+ filter: 'Filter',
+ dropdown: 'DropDown',
+ checklist: 'CheckList',
+ changepage: 'ChangePage',
+ clear: 'Clear',
+ changeresultsperpage: 'ChangeResults',
+ resetvalues: 'ResetValues',
+ resetpage: 'ResetPage',
+ resetpagelength: 'ResetPageLength',
+ loadextensions: 'LoadExtensions',
+ loadthemes: 'LoadThemes'
+ },
+
+ // Detect <enter> key
+ detectKey(e) {
+ if(!this.enterKey){ return; }
+ let _ev = e || global.event;
+ if(_ev){
+ let key = Event.keyCode(_ev);
+ if(key===13){
+ this.filter();
+ Event.cancel(_ev);
+ Event.stop(_ev);
+ } else {
+ this.isUserTyping = true;
+ global.clearInterval(this.autoFilterTimer);
+ this.autoFilterTimer = null;
+ }
+ }
+ },
+ // if auto-filter on, detect user is typing and filter columns
+ onKeyUp(e) {
+ if(!this.autoFilter){
+ return;
+ }
+ let _ev = e || global.event;
+ let key = Event.keyCode(_ev);
+ this.isUserTyping = false;
+
+ function filter() {
+ /*jshint validthis:true */
+ global.clearInterval(this.autoFilterTimer);
+ this.autoFilterTimer = null;
+ if(!this.isUserTyping){
+ this.filter();
+ this.isUserTyping = null;
+ }
+ }
+
+ if(key!==13 && key!==9 && key!==27 && key!==38 && key!==40) {
+ if(this.autoFilterTimer === null){
+ this.autoFilterTimer = global.setInterval(
+ filter.bind(this), this.autoFilterDelay);
+ }
+ } else {
+ global.clearInterval(this.autoFilterTimer);
+ this.autoFilterTimer = null;
+ }
+ },
+ // if auto-filter on, detect user is typing
+ onKeyDown() {
+ if(!this.autoFilter) { return; }
+ this.isUserTyping = true;
+ },
+ // if auto-filter on, clear interval on filter blur
+ onInpBlur() {
+ if(this.autoFilter){
+ this.isUserTyping = false;
+ global.clearInterval(this.autoFilterTimer);
+ }
+ // TODO: hack to prevent ezEditTable enter key event hijaking.
+ // Needs to be fixed in the vendor's library
+ if(this.hasExtension('advancedGrid')){
+ var advGrid = this.extension('advancedGrid');
+ var ezEditTable = advGrid._ezEditTable;
+ if(advGrid.cfg.editable){
+ ezEditTable.Editable.Set();
+ }
+ if(advGrid.cfg.selection){
+ ezEditTable.Selection.Set();
+ }
+ }
+ },
+ // set focused text-box filter as active
+ onInpFocus(e) {
+ let _ev = e || global.event;
+ let elm = Event.target(_ev);
+ this.activeFilterId = elm.getAttribute('id');
+ this.activeFlt = Dom.id(this.activeFilterId);
+ if(this.popupFilters){
+ Event.cancel(_ev);
+ Event.stop(_ev);
+ }
+ // TODO: hack to prevent ezEditTable enter key event hijaking.
+ // Needs to be fixed in the vendor's library
+ if(this.hasExtension('advancedGrid')){
+ var advGrid = this.extension('advancedGrid');
+ var ezEditTable = advGrid._ezEditTable;
+ if(advGrid.cfg.editable){
+ ezEditTable.Editable.Remove();
+ }
+ if(advGrid.cfg.selection){
+ ezEditTable.Selection.Remove();
+ }
+ }
+ },
+ // set focused drop-down filter as active
+ onSlcFocus(e) {
+ let _ev = e || global.event;
+ let elm = Event.target(_ev);
+ this.activeFilterId = elm.getAttribute('id');
+ this.activeFlt = Dom.id(this.activeFilterId);
+ // select is populated when element has focus
+ if(this.loadFltOnDemand && elm.getAttribute('filled') === '0'){
+ let ct = elm.getAttribute('ct');
+ this.Mod.dropdown._build(ct);
+ }
+ if(this.popupFilters){
+ Event.cancel(_ev);
+ Event.stop(_ev);
+ }
+ },
+ // filter columns on drop-down filter change
+ onSlcChange(e) {
+ if(!this.activeFlt){ return; }
+ let _ev = e || global.event;
+ if(this.popupFilters){ Event.stop(_ev); }
+ if(this.onSlcChange){ this.filter(); }
+ },
+ // fill checklist filter on click if required
+ onCheckListClick(e) {
+ let _ev = e || global.event;
+ let elm = Event.target(_ev);
+ if(this.loadFltOnDemand && elm.getAttribute('filled') === '0'){
+ let ct = elm.getAttribute('ct');
+ this.Mod.checkList._build(ct);
+ this.Mod.checkList.checkListDiv[ct].onclick = null;
+ this.Mod.checkList.checkListDiv[ct].title = '';
+ }
+ },
+ // filter when validation button clicked
+ onBtnClick() {
+ this.filter();
+ }
+ };
+ }
+
+ /**
+ * Initialise filtering grid bar behaviours and layout
+ *
+ * TODO: decompose in smaller methods
+ */
+ init(){
+ if(this._hasGrid){
+ return;
+ }
+ if(!this.tbl){
+ this.tbl = Dom.id(this.id);
+ }
+ if(this.gridLayout){
+ this.refRow = this.startRow===null ? 0 : this.startRow;
+ }
+ if(this.popupFilters &&
+ ((this.filtersRowIndex === 0 && this.headersRow === 1) ||
+ this.gridLayout)){
+ this.headersRow = 0;
+ }
+
+ let Mod = this.Mod;
+ let n = this.singleSearchFlt ? 1 : this.nbCells,
+ inpclass;
+
+ //loads stylesheet if not imported
+ this.import(this.stylesheetId, this.stylesheet, null, 'link');
+
+ //loads theme
+ if(this.hasThemes){ this._loadThemes(); }
+
+ if(this.rememberGridValues || this.rememberPageNb ||
+ this.rememberPageLen){
+ Mod.store = new Store(this);
+ }
+
+ if(this.gridLayout){
+ Mod.gridLayout = new GridLayout(this);
+ Mod.gridLayout.init();
+ }
+
+ if(this.loader){
+ if(!Mod.loader){
+ Mod.loader = new Loader(this);
+ Mod.loader.init();
+ }
+ }
+
+ if(this.highlightKeywords){
+ Mod.highlightKeyword = new HighlightKeyword(this);
+ }
+
+ if(this.popupFilters){
+ if(!Mod.popupFilter){
+ Mod.popupFilter = new PopupFilter(this);
+ }
+ Mod.popupFilter.init();
+ }
+
+ //filters grid is not generated
+ if(!this.fltGrid){
+ this.refRow = this.refRow-1;
+ if(this.gridLayout){
+ this.refRow = 0;
+ }
+ this.nbFilterableRows = this.getRowsNb();
+ this.nbVisibleRows = this.nbFilterableRows;
+ this.nbRows = this.nbFilterableRows + this.refRow;
+ } else {
+ if(this.isFirstLoad){
+ let fltrow;
+ if(!this.gridLayout){
+ let thead = Dom.tag(this.tbl, 'thead');
+ if(thead.length > 0){
+ fltrow = thead[0].insertRow(this.filtersRowIndex);
+ } else {
+ fltrow = this.tbl.insertRow(this.filtersRowIndex);
+ }
+
+ if(this.headersRow > 1 &&
+ this.filtersRowIndex <= this.headersRow &&
+ !this.popupFilters){
+ this.headersRow++;
+ }
+ if(this.popupFilters){
+ this.headersRow++;
+ }
+
+ fltrow.className = this.fltsRowCssClass;
+
+ if(this.isExternalFlt || this.popupFilters){
+ fltrow.style.display = 'none';
+ }
+ }
+
+ this.nbFilterableRows = this.getRowsNb();
+ this.nbVisibleRows = this.nbFilterableRows;
+ this.nbRows = this.tbl.rows.length;
+
+ for(let i=0; i<n; i++){// this loop adds filters
+
+ if(this.popupFilters){
+ Mod.popupFilter.build(i);
+ }
+
+ let fltcell = Dom.create(this.fltCellTag),
+ col = this.getFilterType(i),
+ externalFltTgtId =
+ this.isExternalFlt && this.externalFltTgtIds ?
+ this.externalFltTgtIds[i] : null;
+
+ if(this.singleSearchFlt){
+ fltcell.colSpan = this.nbCells;
+ }
+ if(!this.gridLayout){
+ fltrow.appendChild(fltcell);
+ }
+ inpclass = (i==n-1 && this.displayBtn) ?
+ this.fltSmallCssClass : this.fltCssClass;
+
+ //only 1 input for single search
+ if(this.singleSearchFlt){
+ col = this.fltTypeInp;
+ inpclass = this.singleFltCssClass;
+ }
+
+ //drop-down filters
+ if(col===this.fltTypeSlc || col===this.fltTypeMulti){
+ if(!Mod.dropdown){
+ Mod.dropdown = new Dropdown(this);
+ }
+ let dropdown = Mod.dropdown;
+
+ let slc = Dom.create(this.fltTypeSlc,
+ ['id', this.prfxFlt+i+'_'+this.id],
+ ['ct', i], ['filled', '0']
+ );
+
+ if(col===this.fltTypeMulti){
+ slc.multiple = this.fltTypeMulti;
+ slc.title = dropdown.multipleSlcTooltip;
+ }
+ slc.className = Str.lower(col)===this.fltTypeSlc ?
+ inpclass : this.fltMultiCssClass;// for ie<=6
+
+ //filter is appended in desired external element
+ if(externalFltTgtId){
+ Dom.id(externalFltTgtId).appendChild(slc);
+ this.externalFltEls.push(slc);
+ } else {
+ fltcell.appendChild(slc);
+ }
+
+ this.fltIds.push(this.prfxFlt+i+'_'+this.id);
+
+ if(!this.loadFltOnDemand){
+ dropdown._build(i);
+ }
+
+ Event.add(slc, 'keypress',
+ this.Evt.detectKey.bind(this));
+ Event.add(slc, 'change',
+ this.Evt.onSlcChange.bind(this));
+ Event.add(slc, 'focus', this.Evt.onSlcFocus.bind(this));
+
+ //1st option is created here since dropdown.build isn't
+ //invoked
+ if(this.loadFltOnDemand){
+ let opt0 = Dom.createOpt(this.displayAllText, '');
+ slc.appendChild(opt0);
+ }
+ }
+ // checklist
+ else if(col===this.fltTypeCheckList){
+ let checkList;
+ Mod.checkList = new CheckList(this);
+ checkList = Mod.checkList;
+
+ let divCont = Dom.create('div',
+ ['id', checkList.prfxCheckListDiv+i+'_'+this.id],
+ ['ct', i], ['filled', '0']);
+ divCont.className = checkList.checkListDivCssClass;
+
+ //filter is appended in desired element
+ if(externalFltTgtId){
+ Dom.id(externalFltTgtId).appendChild(divCont);
+ this.externalFltEls.push(divCont);
+ } else {
+ fltcell.appendChild(divCont);
+ }
+
+ checkList.checkListDiv[i] = divCont;
+ this.fltIds.push(this.prfxFlt+i+'_'+this.id);
+ if(!this.loadFltOnDemand){
+ checkList._build(i);
+ }
+
+ if(this.loadFltOnDemand){
+ Event.add(divCont, 'click',
+ this.Evt.onCheckListClick.bind(this));
+ divCont.appendChild(
+ Dom.text(checkList.activateCheckListTxt));
+ }
+ }
+
+ else{
+ //show/hide input
+ let inptype = col===this.fltTypeInp ? 'text' : 'hidden';
+ let inp = Dom.create(this.fltTypeInp,
+ ['id',this.prfxFlt+i+'_'+this.id],
+ ['type',inptype], ['ct',i]);
+ if(inptype!=='hidden' && this.watermark){
+ inp.setAttribute(
+ 'placeholder',
+ this.isWatermarkArray ?
+ (this.watermark[i] || '') : this.watermark
+ );
+ }
+ inp.className = inpclass;
+ Event.add(inp, 'focus', this.Evt.onInpFocus.bind(this));
+
+ //filter is appended in desired element
+ if(externalFltTgtId){
+ Dom.id(externalFltTgtId).appendChild(inp);
+ this.externalFltEls.push(inp);
+ } else {
+ fltcell.appendChild(inp);
+ }
+
+ this.fltIds.push(this.prfxFlt+i+'_'+this.id);
+
+ Event.add(inp, 'keypress',
+ this.Evt.detectKey.bind(this));
+ Event.add(inp, 'keydown',
+ this.Evt.onKeyDown.bind(this));
+ Event.add(inp, 'keyup', this.Evt.onKeyUp.bind(this));
+ Event.add(inp, 'blur', this.Evt.onInpBlur.bind(this));
+
+ if(this.rememberGridValues){
+ let flts_values = this.Mod.store.getFilterValues(
+ this.fltsValuesCookie);
+ if(flts_values[i]!=' '){
+ this.setFilterValue(i, flts_values[i], false);
+ }
+ }
+ }
+ // this adds submit button
+ if(i==n-1 && this.displayBtn){
+ let btn = Dom.create(this.fltTypeInp,
+ ['id',this.prfxValButton+i+'_'+this.id],
+ ['type','button'], ['value',this.btnText]);
+ btn.className = this.btnCssClass;
+
+ //filter is appended in desired element
+ if(externalFltTgtId){
+ Dom.id(externalFltTgtId).appendChild(btn);
+ } else{
+ fltcell.appendChild(btn);
+ }
+
+ Event.add(btn, 'click', this.Evt.onBtnClick.bind(this));
+ }//if
+
+ }// for i
+
+ } else {
+ this._resetGrid();
+ }//if isFirstLoad
+
+ }//if this.fltGrid
+
+ /* Filter behaviours */
+ if(this.hasVisibleRows){
+ this.enforceVisibility();
+ }
+ if(this.rowsCounter){
+ Mod.rowsCounter = new RowsCounter(this);
+ Mod.rowsCounter.init();
+ }
+ if(this.statusBar){
+ Mod.statusBar = new StatusBar(this);
+ Mod.statusBar.init();
+ }
+ if(this.paging || Mod.paging){
+ if(!Mod.paging){
+ Mod.paging = new Paging(this);
+ Mod.paging.init();
+ }
+ Mod.paging.reset();
+ }
+ if(this.btnReset){
+ Mod.clearButton = new ClearButton(this);
+ Mod.clearButton.init();
+ }
+ if(this.help){
+ if(!Mod.help){
+ Mod.help = new Help(this);
+ }
+ Mod.help.init();
+ }
+ if(this.hasColWidths && !this.gridLayout){
+ this.setColWidths();
+ }
+ if(this.alternateRows){
+ Mod.alternateRows = new AlternateRows(this);
+ Mod.alternateRows.init();
+ }
+
+ this.isFirstLoad = false;
+ this._hasGrid = true;
+
+ if(this.rememberGridValues || this.rememberPageLen ||
+ this.rememberPageNb){
+ this.resetValues();
+ }
+
+ //TF css class is added to table
+ if(!this.gridLayout){
+ Dom.addClass(this.tbl, this.prfxTf);
+ }
+
+ if(this.loader){
+ Mod.loader.show('none');
+ }
+
+ /* Loads extensions */
+ if(this.hasExtensions){
+ this.initExtensions();
+ }
+
+ if(this.onFiltersLoaded){
+ this.onFiltersLoaded.call(null, this);
+ }
+ }
+
+ /**
+ * Manages state messages
+ * @param {String} evt Event name
+ * @param {Object} cfg Config object
+ */
+ EvtManager(evt,
+ cfg={ slcIndex: null, slcExternal: false, slcId: null, pgIndex: null }){
+ let slcIndex = cfg.slcIndex;
+ let slcExternal = cfg.slcExternal;
+ let slcId = cfg.slcId;
+ let pgIndex = cfg.pgIndex;
+ let cpt = this.Mod;
+
+ function efx(){
+ /*jshint validthis:true */
+ let ev = this.Evt.name;
+
+ switch(evt){
+ case ev.filter:
+ this._filter();
+ break;
+ case ev.dropdown:
+ if(this.linkedFilters){
+ cpt.dropdown._build(slcIndex, true);
+ } else {
+ cpt.dropdown._build(
+ slcIndex, false, slcExternal, slcId);
+ }
+ break;
+ case ev.checklist:
+ cpt.checkList._build(slcIndex, slcExternal, slcId);
+ break;
+ case ev.changepage:
+ cpt.paging._changePage(pgIndex);
+ break;
+ case ev.clear:
+ this._clearFilters();
+ this._filter();
+ break;
+ case ev.changeresultsperpage:
+ cpt.paging._changeResultsPerPage();
+ break;
+ case ev.resetvalues:
+ this._resetValues();
+ this._filter();
+ break;
+ case ev.resetpage:
+ cpt.paging._resetPage(this.pgNbCookie);
+ break;
+ case ev.resetpagelength:
+ cpt.paging._resetPageLength(this.pgLenCookie);
+ break;
+ case ev.loadextensions:
+ this._loadExtensions();
+ break;
+ case ev.loadthemes:
+ this._loadThemes();
+ break;
+ }
+ if(this.statusBar){
+ cpt.statusBar.message('');
+ }
+ if(this.loader){
+ cpt.loader.show('none');
+ }
+ }
+
+ if(!this.loader && !this.statusBar && !this.linkedFilters) {
+ efx.call(this);
+ } else {
+ if(this.loader){
+ cpt.loader.show('');
+ }
+ if(this.statusBar){
+ cpt.statusBar.message(this['msg'+evt]);
+ }
+ global.setTimeout(efx.bind(this), this.execDelay);
+ }
+ }
+
+ /**
+ * Return a feature instance for a given name
+ * @param {String} name Name of the feature
+ * @return {Object}
+ */
+ feature(name){
+ return this.Mod[name];
+ }
+
+ /**
+ * Initialise all the extensions defined in the configuration object
+ */
+ initExtensions(){
+ let exts = this.extensions;
+
+ for(let i=0, len=exts.length; i<len; i++){
+ let ext = exts[i];
+ if(!this.ExtRegistry[ext.name]){
+ this.loadExtension(ext);
+ }
+ }
+ }
+
+ /**
+ * Load an extension module
+ * @param {Object} ext Extension config object
+ */
+ loadExtension(ext){
+ if(!ext || !ext.name){
+ return;
+ }
+
+ let name = ext.name;
+ let path = ext.path;
+ let modulePath;
+
+ if(name && path){
+ modulePath = ext.path + name;
+ } else {
+ name = name.replace('.js', '');
+ modulePath = 'extensions/{}/{}'.replace(/{}/g, name);
+ }
+
+ // Trick to set config's publicPath dynamically for Webpack...
+ __webpack_public_path__ = this.basePath;
+
+ require(['./' + modulePath], (mod)=> {
+ let inst = new mod(this, ext);
+ inst.init();
+ this.ExtRegistry[name] = inst;
+ });
+ }
+
+ /**
+ * Get an extension instance
+ * @param {String} name Name of the extension
+ * @return {Object} Extension instance
+ */
+ extension(name){
+ return this.ExtRegistry[name];
+ }
+
+ /**
+ * Check passed extension name exists
+ * @param {String} name Name of the extension
+ * @return {Boolean}
+ */
+ hasExtension(name){
+ return !Types.isEmpty(this.ExtRegistry[name]);
+ }
+
+ /**
+ * Destroy all the extensions defined in the configuration object
+ */
+ destroyExtensions(){
+ let exts = this.extensions;
+
+ for(let i=0, len=exts.length; i<len; i++){
+ let ext = exts[i];
+ let extInstance = this.ExtRegistry[ext.name];
+ if(extInstance){
+ extInstance.destroy();
+ this.ExtRegistry[ext.name] = null;
+ }
+ }
+ }
+
+ loadThemes(){
+ this.EvtManager(this.Evt.name.loadthemes);
+ }
+
+ /**
+ * Load themes defined in the configuration object
+ */
+ _loadThemes(){
+ let themes = this.themes;
+ //Default theme config
+ if(this.enableDefaultTheme){
+ let defaultTheme = { name: 'default' };
+ this.themes.push(defaultTheme);
+ }
+ if(Types.isArray(themes)){
+ for(let i=0, len=themes.length; i<len; i++){
+ let theme = themes[i];
+ let name = theme.name;
+ let path = theme.path;
+ let styleId = this.prfxTf + name;
+ if(name && !path){
+ path = this.themesPath + name + '/' + name + '.css';
+ }
+ else if(!name && theme.path){
+ name = 'theme{0}'.replace('{0}', i);
+ }
+
+ if(!this.isImported(path, 'link')){
+ this.import(styleId, path, null, 'link');
+ }
+ }
+ }
+
+ //Some elements need to be overriden for default theme
+ //Reset button
+ this.btnResetText = null;
+ this.btnResetHtml = '<input type="button" value="" class="' +
+ this.btnResetCssClass+'" title="Clear filters" />';
+
+ //Paging buttons
+ this.btnPrevPageHtml = '<input type="button" value="" class="' +
+ this.btnPageCssClass+' previousPage" title="Previous page" />';
+ this.btnNextPageHtml = '<input type="button" value="" class="' +
+ this.btnPageCssClass+' nextPage" title="Next page" />';
+ this.btnFirstPageHtml = '<input type="button" value="" class="' +
+ this.btnPageCssClass+' firstPage" title="First page" />';
+ this.btnLastPageHtml = '<input type="button" value="" class="' +
+ this.btnPageCssClass+' lastPage" title="Last page" />';
+
+ //Loader
+ this.loader = true;
+ this.loaderHtml = '<div class="defaultLoader"></div>';
+ this.loaderText = null;
+ }
+
+ /**
+ * Return stylesheet DOM element for a given theme name
+ * @return {DOMElement} stylesheet element
+ */
+ getStylesheet(name='default'){
+ return Dom.id(this.prfxTf + name);
+ }
+
+ /**
+ * Destroy filter grid
+ */
+ destroy(){
+ if(!this._hasGrid){
+ return;
+ }
+ let rows = this.tbl.rows,
+ Mod = this.Mod;
+
+ this._clearFilters();
+
+ if(this.isExternalFlt && !this.popupFilters){
+ this.removeExternalFlts();
+ }
+ if(this.infDiv){
+ this.removeToolbar();
+ }
+ if(this.highlightKeywords){
+ Mod.highlightKeyword.unhighlightAll();
+ }
+ if(this.markActiveColumns){
+ this.clearActiveColumns();
+ }
+ if(this.hasExtensions){
+ this.destroyExtensions();
+ }
+
+ for(let j=this.refRow; j<this.nbRows; j++){
+ // validate row
+ this.validateRow(j, true);
+
+ //removes alternating colors
+ if(this.alternateRows){
+ Mod.alternateRows.removeRowBg(j);
+ }
+
+ }//for j
+
+ if(this.fltGrid && !this.gridLayout){
+ this.fltGridEl = rows[this.filtersRowIndex];
+ this.tbl.deleteRow(this.filtersRowIndex);
+ }
+
+ // Destroy modules
+ Object.keys(Mod).forEach(function(key) {
+ var feature = Mod[key];
+ if(feature && Types.isFn(feature.destroy)){
+ feature.destroy();
+ }
+ });
+
+ Dom.removeClass(this.tbl, this.prfxTf);
+ this.nbHiddenRows = 0;
+ this.validRowsIndex = null;
+ this.activeFlt = null;
+ this._hasGrid = false;
+ this.tbl = null;
+ }
+
+ /**
+ * Generate container element for paging, reset button, rows counter etc.
+ */
+ setToolbar(){
+ if(this.infDiv){
+ return;
+ }
+
+ /*** container div ***/
+ let infdiv = Dom.create('div', ['id', this.prfxInfDiv+this.id]);
+ infdiv.className = this.infDivCssClass;
+
+ //custom container
+ if(this.toolBarTgtId){
+ Dom.id(this.toolBarTgtId).appendChild(infdiv);
+ }
+ //grid-layout
+ else if(this.gridLayout){
+ let gridLayout = this.Mod.gridLayout;
+ gridLayout.tblMainCont.appendChild(infdiv);
+ infdiv.className = gridLayout.gridInfDivCssClass;
+ }
+ //default location: just above the table
+ else{
+ var cont = Dom.create('caption');
+ cont.appendChild(infdiv);
+ this.tbl.insertBefore(cont, this.tbl.firstChild);
+ }
+ this.infDiv = Dom.id(this.prfxInfDiv+this.id);
+
+ /*** left div containing rows # displayer ***/
+ let ldiv = Dom.create('div', ['id', this.prfxLDiv+this.id]);
+ ldiv.className = this.lDivCssClass;
+ infdiv.appendChild(ldiv);
+ this.lDiv = Dom.id(this.prfxLDiv+this.id);
+
+ /*** right div containing reset button
+ + nb results per page select ***/
+ let rdiv = Dom.create('div', ['id', this.prfxRDiv+this.id]);
+ rdiv.className = this.rDivCssClass;
+ infdiv.appendChild(rdiv);
+ this.rDiv = Dom.id(this.prfxRDiv+this.id);
+
+ /*** mid div containing paging elements ***/
+ let mdiv = Dom.create('div', ['id', this.prfxMDiv+this.id]);
+ mdiv.className = this.mDivCssClass;
+ infdiv.appendChild(mdiv);
+ this.mDiv = Dom.id(this.prfxMDiv+this.id);
+
+ // Enable help instructions by default if topbar is generated and not
+ // explicitely set to false
+ if(Types.isUndef(this.help)){
+ if(!this.Mod.help){
+ this.Mod.help = new Help(this);
+ }
+ this.Mod.help.init();
+ this.help = true;
+ }
+ }
+
+ /**
+ * Remove toolbar container element
+ */
+ removeToolbar(){
+ if(!this.infDiv){
+ return;
+ }
+ this.infDiv.parentNode.removeChild(this.infDiv);
+ this.infDiv = null;
+
+ let tbl = this.tbl;
+ let captions = Dom.tag(tbl, 'caption');
+ if(captions.length > 0){
+ [].forEach.call(captions, function(elm) {
+ tbl.removeChild(elm);
+ });
+ }
+ }
+
+ /**
+ * Remove all the external column filters
+ */
+ removeExternalFlts(){
+ if(!this.isExternalFlt || !this.externalFltTgtIds){
+ return;
+ }
+ let ids = this.externalFltTgtIds,
+ len = ids.length;
+ for(let ct=0; ct<len; ct++){
+ let externalFltTgtId = ids[ct],
+ externalFlt = Dom.id(externalFltTgtId);
+ if(externalFlt){
+ externalFlt.innerHTML = '';
+ }
+ }
+ }
+
+ /**
+ * Check if given column implements a filter with custom options
+ * @param {Number} colIndex Column's index
+ * @return {Boolean}
+ */
+ isCustomOptions(colIndex) {
+ return this.hasCustomOptions &&
+ this.customOptions.cols.indexOf(colIndex) != -1;
+ }
+
+ /**
+ * Returns an array [[value0, value1 ...],[text0, text1 ...]] with the
+ * custom options values and texts
+ * @param {Number} colIndex Column's index
+ * @return {Array}
+ */
+ getCustomOptions(colIndex){
+ if(Types.isEmpty(colIndex) || !this.isCustomOptions(colIndex)){
+ return;
+ }
+
+ let customOptions = this.customOptions;
+ let cols = customOptions.cols;
+ let optTxt = [], optArray = [];
+ let index = cols.indexOf(colIndex);
+ let slcValues = customOptions.values[index];
+ let slcTexts = customOptions.texts[index];
+ let slcSort = customOptions.sorts[index];
+
+ for(let r=0, len=slcValues.length; r<len; r++){
+ optArray.push(slcValues[r]);
+ if(slcTexts[r]){
+ optTxt.push(slcTexts[r]);
+ } else {
+ optTxt.push(slcValues[r]);
+ }
+ }
+ if(slcSort){
+ optArray.sort();
+ optTxt.sort();
+ }
+ return [optArray, optTxt];
+ }
+
+ resetValues(){
+ this.EvtManager(this.Evt.name.resetvalues);
+ }
+
+ /**
+ * Reset persisted filter values
+ */
+ _resetValues(){
+ //only loadFltOnDemand
+ if(this.rememberGridValues && this.loadFltOnDemand){
+ this._resetGridValues(this.fltsValuesCookie);
+ }
+ if(this.rememberPageLen && this.Mod.paging){
+ this.Mod.paging.resetPageLength(this.pgLenCookie);
+ }
+ if(this.rememberPageNb && this.Mod.paging){
+ this.Mod.paging.resetPage(this.pgNbCookie);
+ }
+ }
+
+ /**
+ * Reset persisted filter values when load filters on demand feature is
+ * enabled
+ * @param {String} name cookie name storing filter values
+ */
+ _resetGridValues(name){
+ if(!this.loadFltOnDemand){
+ return;
+ }
+ let fltsValues = this.Mod.store.getFilterValues(name),
+ slcFltsIndex = this.getFiltersByType(this.fltTypeSlc, true),
+ multiFltsIndex = this.getFiltersByType(this.fltTypeMulti, true);
+
+ //if the number of columns is the same as before page reload
+ if(Number(fltsValues[(fltsValues.length-1)]) === this.fltIds.length){
+ for(let i=0; i<(fltsValues.length - 1); i++){
+ if(fltsValues[i]===' '){
+ continue;
+ }
+ let s, opt;
+ let fltType = this.getFilterType(i);
+ // if loadFltOnDemand, drop-down needs to contain stored
+ // value(s) for filtering
+ if(fltType===this.fltTypeSlc || fltType===this.fltTypeMulti){
+ let slc = Dom.id( this.fltIds[i] );
+ slc.options[0].selected = false;
+
+ //selects
+ if(slcFltsIndex.indexOf(i) != -1){
+ opt = Dom.createOpt(fltsValues[i],fltsValues[i],true);
+ slc.appendChild(opt);
+ this.hasStoredValues = true;
+ }
+ //multiple select
+ if(multiFltsIndex.indexOf(i) != -1){
+ s = fltsValues[i].split(' '+this.orOperator+' ');
+ for(let j=0, len=s.length; j<len; j++){
+ if(s[j]===''){
+ continue;
+ }
+ opt = Dom.createOpt(s[j],s[j],true);
+ slc.appendChild(opt);
+ this.hasStoredValues = true;
+ }
+ }// if multiFltsIndex
+ }
+ else if(fltType===this.fltTypeCheckList){
+ let checkList = this.Mod.checkList;
+ let divChk = checkList.checkListDiv[i];
+ divChk.title = divChk.innerHTML;
+ divChk.innerHTML = '';
+
+ let ul = Dom.create(
+ 'ul',['id',this.fltIds[i]],['colIndex',i]);
+ ul.className = checkList.checkListCssClass;
+
+ let li0 = Dom.createCheckItem(
+ this.fltIds[i]+'_0', '', this.displayAllText);
+ li0.className = checkList.checkListItemCssClass;
+ ul.appendChild(li0);
+
+ divChk.appendChild(ul);
+
+ s = fltsValues[i].split(' '+this.orOperator+' ');
+ for(let j=0, len=s.length; j<len; j++){
+ if(s[j]===''){
+ continue;
+ }
+ let li = Dom.createCheckItem(
+ this.fltIds[i]+'_'+(j+1), s[j], s[j]);
+ li.className = checkList.checkListItemCssClass;
+ ul.appendChild(li);
+ li.check.checked = true;
+ checkList.setCheckListValues(li.check);
+ this.hasStoredValues = true;
+ }
+ }
+ }//end for
+
+ if(!this.hasStoredValues && this.paging){
+ this.Mod.paging.setPagingInfo();
+ }
+ }//end if
+ }
+
+ filter(){
+ this.EvtManager(this.Evt.name.filter);
+ }
+
+ /**
+ * Filter the table by retrieving the data from each cell in every single
+ * row and comparing it to the search term for current column. A row is
+ * hidden when all the search terms are not found in inspected row.
+ *
+ * TODO: Reduce complexity of this massive method
+ */
+ _filter(){
+ if(!this.fltGrid || (!this._hasGrid && !this.isFirstLoad)){
+ return;
+ }
+ //invoke onbefore callback
+ if(this.onBeforeFilter){
+ this.onBeforeFilter.call(null, this);
+ }
+
+ let row = this.tbl.rows,
+ Mod = this.Mod,
+ hiddenrows = 0;
+
+ this.validRowsIndex = [];
+
+ // removes keyword highlighting
+ if(this.highlightKeywords){
+ Mod.highlightKeyword.unhighlightAll();
+ }
+ //removes popup filters active icons
+ if(this.popupFilters){
+ Mod.popupFilter.buildIcons();
+ }
+ //removes active column header class
+ if(this.markActiveColumns){
+ this.clearActiveColumns();
+ }
+ // search args re-init
+ this.searchArgs = this.getFiltersValue();
+
+ var num_cell_data, nbFormat;
+ var re_le = new RegExp(this.leOperator),
+ re_ge = new RegExp(this.geOperator),
+ re_l = new RegExp(this.lwOperator),
+ re_g = new RegExp(this.grOperator),
+ re_d = new RegExp(this.dfOperator),
+ re_lk = new RegExp(Str.rgxEsc(this.lkOperator)),
+ re_eq = new RegExp(this.eqOperator),
+ re_st = new RegExp(this.stOperator),
+ re_en = new RegExp(this.enOperator),
+ // re_an = new RegExp(this.anOperator),
+ // re_cr = new RegExp(this.curExp),
+ re_em = this.emOperator,
+ re_nm = this.nmOperator,
+ re_re = new RegExp(Str.rgxEsc(this.rgxOperator));
+
+ //keyword highlighting
+ function highlight(str, ok, cell){
+ /*jshint validthis:true */
+ if(this.highlightKeywords && ok){
+ str = str.replace(re_lk, '');
+ str = str.replace(re_eq, '');
+ str = str.replace(re_st, '');
+ str = str.replace(re_en, '');
+ let w = str;
+ if(re_le.test(str) || re_ge.test(str) || re_l.test(str) ||
+ re_g.test(str) || re_d.test(str)){
+ w = Dom.getText(cell);
+ }
+ if(w !== ''){
+ Mod.highlightKeyword.highlight(
+ cell, w, Mod.highlightKeyword.highlightCssClass);
+ }
+ }
+ }
+
+ //looks for search argument in current row
+ function hasArg(sA, cell_data, j){
+ /*jshint validthis:true */
+ let occurence,
+ removeNbFormat = Helpers.removeNbFormat;
+ //Search arg operator tests
+ let hasLO = re_l.test(sA),
+ hasLE = re_le.test(sA),
+ hasGR = re_g.test(sA),
+ hasGE = re_ge.test(sA),
+ hasDF = re_d.test(sA),
+ hasEQ = re_eq.test(sA),
+ hasLK = re_lk.test(sA),
+ // hasAN = re_an.test(sA),
+ hasST = re_st.test(sA),
+ hasEN = re_en.test(sA),
+ hasEM = (re_em === sA),
+ hasNM = (re_nm === sA),
+ hasRE = re_re.test(sA);
+
+ //Search arg dates tests
+ let isLDate = hasLO && isValidDate(sA.replace(re_l,''), dtType);
+ let isLEDate = hasLE && isValidDate(sA.replace(re_le,''), dtType);
+ let isGDate = hasGR && isValidDate(sA.replace(re_g,''), dtType);
+ let isGEDate = hasGE && isValidDate(sA.replace(re_ge,''), dtType);
+ let isDFDate = hasDF && isValidDate(sA.replace(re_d,''), dtType);
+ let isEQDate = hasEQ && isValidDate(sA.replace(re_eq,''), dtType);
+
+ let dte1, dte2;
+ //dates
+ if(isValidDate(cell_data,dtType)){
+ dte1 = formatDate(cell_data,dtType);
+ // lower date
+ if(isLDate){
+ dte2 = formatDate(sA.replace(re_l,''), dtType);
+ occurence = dte1 < dte2;
+ }
+ // lower equal date
+ else if(isLEDate){
+ dte2 = formatDate(sA.replace(re_le,''), dtType);
+ occurence = dte1 <= dte2;
+ }
+ // greater equal date
+ else if(isGEDate){
+ dte2 = formatDate(sA.replace(re_ge,''), dtType);
+ occurence = dte1 >= dte2;
+ }
+ // greater date
+ else if(isGDate){
+ dte2 = formatDate(sA.replace(re_g,''), dtType);
+ occurence = dte1 > dte2;
+ }
+ // different date
+ else if(isDFDate){
+ dte2 = formatDate(sA.replace(re_d,''), dtType);
+ occurence = dte1.toString() != dte2.toString();
+ }
+ // equal date
+ else if(isEQDate){
+ dte2 = formatDate(sA.replace(re_eq,''), dtType);
+ occurence = dte1.toString() == dte2.toString();
+ }
+ // searched keyword with * operator doesn't have to be a date
+ else if(re_lk.test(sA)){// like date
+ occurence = this._containsStr(
+ sA.replace(re_lk,''), cell_data, false);
+ }
+ else if(isValidDate(sA,dtType)){
+ dte2 = formatDate(sA,dtType);
+ occurence = dte1.toString() == dte2.toString();
+ }
+ //empty
+ else if(hasEM){
+ occurence = Str.isEmpty(cell_data);
+ }
+ //non-empty
+ else if(hasNM){
+ occurence = !Str.isEmpty(cell_data);
+ }
+ }
+
+ else{
+ //first numbers need to be formated
+ if(this.hasColNbFormat && this.colNbFormat[j]){
+ num_cell_data = removeNbFormat(
+ cell_data, this.colNbFormat[j]);
+ nbFormat = this.colNbFormat[j];
+ } else {
+ if(this.thousandsSeparator === ',' &&
+ this.decimalSeparator === '.'){
+ num_cell_data = removeNbFormat(cell_data, 'us');
+ nbFormat = 'us';
+ } else {
+ num_cell_data = removeNbFormat(cell_data, 'eu');
+ nbFormat = 'eu';
+ }
+ }
+
+ // first checks if there is any operator (<,>,<=,>=,!,*,=,{,},
+ // rgx:)
+ // lower equal
+ if(hasLE){
+ occurence = num_cell_data <= removeNbFormat(
+ sA.replace(re_le, ''), nbFormat);
+ }
+ //greater equal
+ else if(hasGE){
+ occurence = num_cell_data >= removeNbFormat(
+ sA.replace(re_ge, ''), nbFormat);
+ }
+ //lower
+ else if(hasLO){
+ occurence = num_cell_data < removeNbFormat(
+ sA.replace(re_l, ''), nbFormat);
+ }
+ //greater
+ else if(hasGR){
+ occurence = num_cell_data > removeNbFormat(
+ sA.replace(re_g, ''), nbFormat);
+ }
+ //different
+ else if(hasDF){
+ occurence = this._containsStr(
+ sA.replace(re_d, ''), cell_data) ? false : true;
+ }
+ //like
+ else if(hasLK){
+ occurence = this._containsStr(
+ sA.replace(re_lk, ''), cell_data, false);
+ }
+ //equal
+ else if(hasEQ){
+ occurence = this._containsStr(
+ sA.replace(re_eq, ''), cell_data, true);
+ }
+ //starts with
+ else if(hasST){
+ occurence = cell_data.indexOf(sA.replace(re_st, ''))===0 ?
+ true : false;
+ }
+ //ends with
+ else if(hasEN){
+ let searchArg = sA.replace(re_en, '');
+ occurence =
+ cell_data.lastIndexOf(searchArg,cell_data.length-1) ===
+ (cell_data.length-1)-(searchArg.length-1) &&
+ cell_data.lastIndexOf(
+ searchArg, cell_data.length-1) > -1 ? true : false;
+ }
+ //empty
+ else if(hasEM){
+ occurence = Str.isEmpty(cell_data);
+ }
+ //non-empty
+ else if(hasNM){
+ occurence = !Str.isEmpty(cell_data);
+ }
+ //regexp
+ else if(hasRE){
+ //in case regexp fires an exception
+ try{
+ //operator is removed
+ let srchArg = sA.replace(re_re,'');
+ let rgx = new RegExp(srchArg);
+ occurence = rgx.test(cell_data);
+ } catch(e) { occurence = false; }
+ } else {
+ occurence = this._containsStr(sA, cell_data,
+ this.isExactMatch(j));
+ }
+
+ }//else
+ return occurence;
+ }//fn
+
+ for(let k=this.refRow; k<this.nbRows; k++){
+ /*** if table already filtered some rows are not visible ***/
+ if(row[k].style.display === 'none'){
+ row[k].style.display = '';
+ }
+
+ let cell = row[k].cells,
+ nchilds = cell.length;
+
+ // checks if row has exact cell #
+ if(nchilds !== this.nbCells){
+ continue;
+ }
+
+ let occurence = [],
+ isRowValid = true,
+ //only for single filter search
+ singleFltRowValid = false;
+
+ // this loop retrieves cell data
+ for(let j=0; j<nchilds; j++){
+ //searched keyword
+ let sA = this.searchArgs[this.singleSearchFlt ? 0 : j];
+ var dtType = this.hasColDateType ?
+ this.colDateType[j] : this.defaultDateType;
+ if(sA === ''){
+ continue;
+ }
+
+ let cell_data = Str.matchCase(this.getCellData(cell[j]),
+ this.caseSensitive);
+
+ //multiple search parameter operator ||
+ let sAOrSplit = sA.split(this.orOperator),
+ //multiple search || parameter boolean
+ hasMultiOrSA = (sAOrSplit.length>1) ? true : false,
+ //multiple search parameter operator &&
+ sAAndSplit = sA.split(this.anOperator),
+ //multiple search && parameter boolean
+ hasMultiAndSA = sAAndSplit.length>1 ? true : false;
+
+ //multiple sarch parameters
+ if(hasMultiOrSA || hasMultiAndSA){
+ let cS,
+ occur = false,
+ s = hasMultiOrSA ? sAOrSplit : sAAndSplit;
+ for(let w=0, len=s.length; w<len; w++){
+ cS = Str.trim(s[w]);
+ occur = hasArg.call(this, cS, cell_data, j);
+ highlight.call(this, cS, occur, cell[j]);
+ if(hasMultiOrSA && occur){
+ break;
+ }
+ if(hasMultiAndSA && !occur){
+ break;
+ }
+ }
+ occurence[j] = occur;
+ }
+ //single search parameter
+ else {
+ occurence[j] =
+ hasArg.call(this, Str.trim(sA), cell_data, j);
+ highlight.call(this, sA, occurence[j], cell[j]);
+ }//else single param
+
+ if(!occurence[j]){
+ isRowValid = false;
+ }
+ if(this.singleSearchFlt && occurence[j]){
+ singleFltRowValid = true;
+ }
+ if(this.popupFilters){
+ Mod.popupFilter.buildIcon(j, true);
+ }
+ if(this.markActiveColumns){
+ if(k === this.refRow){
+ if(this.onBeforeActiveColumn){
+ this.onBeforeActiveColumn.call(null, this, j);
+ }
+ Dom.addClass(
+ this.getHeaderElement(j),
+ this.activeColumnsCssClass);
+ if(this.onAfterActiveColumn){
+ this.onAfterActiveColumn.call(null, this, j);
+ }
+ }
+ }
+ }//for j
+
+ if(this.singleSearchFlt && singleFltRowValid){
+ isRowValid = true;
+ }
+
+ if(!isRowValid){
+ this.validateRow(k, false);
+ if(Mod.alternateRows){
+ Mod.alternateRows.removeRowBg(k);
+ }
+ // always visible rows need to be counted as valid
+ if(this.hasVisibleRows && this.visibleRows.indexOf(k) !== -1){
+ this.validRowsIndex.push(k);
+ } else {
+ hiddenrows++;
+ }
+ } else {
+ this.validateRow(k, true);
+ this.validRowsIndex.push(k);
+ if(this.alternateRows){
+ Mod.alternateRows.setRowBg(k, this.validRowsIndex.length);
+ }
+ if(this.onRowValidated){
+ this.onRowValidated.call(null, this, k);
+ }
+ }
+ }// for k
+
+ this.nbVisibleRows = this.validRowsIndex.length;
+ this.nbHiddenRows = hiddenrows;
+
+ if(this.rememberGridValues){
+ Mod.store.saveFilterValues(this.fltsValuesCookie);
+ }
+ //applies filter props after filtering process
+ if(!this.paging){
+ this.applyProps();
+ } else {
+ // Shouldn't need to care of that here...
+ // TODO: provide a method in paging module
+ Mod.paging.startPagingRow = 0;
+ Mod.paging.currentPageNb = 1;
+ //
+ Mod.paging.setPagingInfo(this.validRowsIndex);
+ }
+ //invokes onafter callback
+ if(this.onAfterFilter){
+ this.onAfterFilter.call(null,this);
+ }
+ }
+
+ /**
+ * Re-apply the features/behaviour concerned by filtering/paging operation
+ *
+ * NOTE: this will disappear whenever custom events in place
+ */
+ applyProps(){
+ let Mod = this.Mod;
+
+ //shows rows always visible
+ if(this.hasVisibleRows){
+ this.enforceVisibility();
+ }
+ //columns operations
+ if(this.hasExtension('colOps')){
+ this.extension('colOps').calc();
+ }
+
+ //re-populates drop-down filters
+ if(this.linkedFilters){
+ this.linkFilters();
+ }
+
+ if(this.rowsCounter){
+ Mod.rowsCounter.refresh(this.nbVisibleRows);
+ }
+
+ if(this.popupFilters){
+ Mod.popupFilter.closeAll();
+ }
+ }
+
+ /**
+ * Return the data of a specified colum
+ * @param {Number} colIndex Column index
+ * @param {Boolean} includeHeaders Optional: include headers row
+ * @param {Boolean} num Optional: return unformatted number
+ * @param {Array} exclude Optional: list of row indexes to be excluded
+ * @return {Array} Flat list of data for a column
+ */
+ getColValues(colIndex, includeHeaders=false, num=false, exclude=[]){
+ if(!this.fltGrid){
+ return;
+ }
+ let row = this.tbl.rows,
+ colValues = [];
+
+ if(includeHeaders){
+ colValues.push(this.getHeadersText()[colIndex]);
+ }
+
+ for(let i=this.refRow; i<this.nbRows; i++){
+ let isExludedRow = false;
+ // checks if current row index appears in exclude array
+ if(exclude.length > 0){
+ isExludedRow = exclude.indexOf(i) != -1;
+ }
+ let cell = row[i].cells,
+ nchilds = cell.length;
+
+ // checks if row has exact cell # and is not excluded
+ if(nchilds === this.nbCells && !isExludedRow){
+ // this loop retrieves cell data
+ for(let j=0; j<nchilds; j++){
+ if(j != colIndex || row[i].style.display !== ''){
+ continue;
+ }
+ let cell_data = this.getCellData(cell[j]),
+ nbFormat = this.colNbFormat ?
+ this.colNbFormat[colIndex] : null,
+ data = num ?
+ Helpers.removeNbFormat(cell_data, nbFormat) :
+ cell_data;
+ colValues.push(data);
+ }
+ }
+ }
+ return colValues;
+ }
+
+ /**
+ * Return the filter's value of a specified column
+ * @param {Number} index Column index
+ * @return {String} Filter value
+ */
+ getFilterValue(index){
+ if(!this.fltGrid){
+ return;
+ }
+ let fltValue,
+ flt = this.getFilterElement(index);
+ if(!flt){
+ return '';
+ }
+
+ let fltColType = this.getFilterType(index);
+ if(fltColType !== this.fltTypeMulti &&
+ fltColType !== this.fltTypeCheckList){
+ fltValue = flt.value;
+ }
+ //mutiple select
+ else if(fltColType === this.fltTypeMulti){
+ fltValue = '';
+ for(let j=0, len=flt.options.length; j<len; j++){
+ if(flt.options[j].selected){
+ fltValue = fltValue.concat(
+ flt.options[j].value+' ' +
+ this.orOperator + ' '
+ );
+ }
+ }
+ //removes last operator ||
+ fltValue = fltValue.substr(0, fltValue.length-4);
+ }
+ //checklist
+ else if(fltColType === this.fltTypeCheckList){
+ if(flt.getAttribute('value') !== null){
+ fltValue = flt.getAttribute('value');
+ //removes last operator ||
+ fltValue = fltValue.substr(0, fltValue.length-3);
+ } else{
+ fltValue = '';
+ }
+ }
+ return fltValue;
+ }
+
+ /**
+ * Return the filters' values
+ * @return {Array} List of filters' values
+ */
+ getFiltersValue(){
+ if(!this.fltGrid){
+ return;
+ }
+ let searchArgs = [];
+ for(let i=0, len=this.fltIds.length; i<len; i++){
+ searchArgs.push(
+ Str.trim(
+ Str.matchCase(this.getFilterValue(i), this.caseSensitive))
+ );
+ }
+ return searchArgs;
+ }
+
+ /**
+ * Return the ID of the filter of a specified column
+ * @param {Number} index Column's index
+ * @return {String} ID of the filter element
+ */
+ getFilterId(index){
+ if(!this.fltGrid){
+ return;
+ }
+ return this.fltIds[index];
+ }
+
+ /**
+ * Return the list of ids of filters matching a specified type.
+ * Note: hidden filters are also returned
+ *
+ * @param {String} type Filter type string ('input', 'select', 'multiple',
+ * 'checklist')
+ * @param {Boolean} bool If true returns columns indexes instead of IDs
+ * @return {[type]} List of element IDs or column indexes
+ */
+ getFiltersByType(type, bool){
+ if(!this.fltGrid){
+ return;
+ }
+ let arr = [];
+ for(let i=0, len=this.fltIds.length; i<len; i++){
+ let fltType = this.getFilterType(i);
+ if(fltType === Str.lower(type)){
+ let a = bool ? i : this.fltIds[i];
+ arr.push(a);
+ }
+ }
+ return arr;
+ }
+
+ /**
+ * Return the filter's DOM element for a given column
+ * @param {Number} index Column's index
+ * @return {DOMElement}
+ */
+ getFilterElement(index){
+ let fltId = this.fltIds[index];
+ return Dom.id(fltId);
+ }
+
+ /**
+ * Return the number of cells for a given row index
+ * @param {Number} rowIndex Index of the row
+ * @return {Number} Number of cells
+ */
+ getCellsNb(rowIndex=0){
+ let tr = this.tbl.rows[rowIndex];
+ return tr.cells.length;
+ }
+
+ /**
+ * Return the number of filterable rows starting from reference row if
+ * defined
+ * @param {Boolean} includeHeaders Include the headers row
+ * @return {Number} Number of filterable rows
+ */
+ getRowsNb(includeHeaders){
+ let s = Types.isUndef(this.refRow) ? 0 : this.refRow,
+ ntrs = this.tbl.rows.length;
+ if(includeHeaders){ s = 0; }
+ return parseInt(ntrs-s, 10);
+ }
+
+ /**
+ * Return the data of a given cell
+ * @param {DOMElement} cell Cell's DOM object
+ * @return {String}
+ */
+ getCellData(cell){
+ var idx = cell.cellIndex;
+ //Check for customCellData callback
+ if(this.customCellData && this.customCellDataCols.indexOf(idx) != -1){
+ return this.customCellData.call(null, this, cell, idx);
+ } else {
+ return Dom.getText(cell);
+ }
+ }
+
+ /**
+ * Return the table data with following format:
+ * [
+ * [rowIndex, [value0, value1...]],
+ * [rowIndex, [value0, value1...]]
+ * ]
+ * @param {Boolean} includeHeaders Optional: include headers row
+ * @return {Array}
+ *
+ * TODO: provide an API returning data in JSON format
+ */
+ getTableData(includeHeaders=false){
+ let rows = this.tbl.rows;
+ let tblData = [];
+ if(includeHeaders){
+ tblData.push([this.getHeadersRowIndex(), this.getHeadersText()]);
+ }
+ for(let k=this.refRow; k<this.nbRows; k++){
+ let rowData = [k, []];
+ let cells = rows[k].cells;
+ for(let j=0, len=cells.length; j<len; j++){
+ let cellData = this.getCellData(cells[j]);
+ rowData[1].push(cellData);
+ }
+ tblData.push(rowData);
+ }
+ return tblData;
+ }
+
+ /**
+ * Return the filtered data with following format:
+ * [
+ * [rowIndex, [value0, value1...]],
+ * [rowIndex, [value0, value1...]]
+ * ]
+ * @param {Boolean} includeHeaders Optional: include headers row
+ * @return {Array}
+ *
+ * TODO: provide an API returning data in JSON format
+ */
+ getFilteredData(includeHeaders=false){
+ if(!this.validRowsIndex){
+ return [];
+ }
+ let rows = this.tbl.rows,
+ filteredData = [];
+ if(includeHeaders){
+ filteredData.push([this.getHeadersRowIndex(),
+ this.getHeadersText()]);
+ }
+
+ let validRows = this.getValidRows(true);
+ for(let i=0; i<validRows.length; i++){
+ let rData = [this.validRowsIndex[i], []],
+ cells = rows[this.validRowsIndex[i]].cells;
+ for(let k=0; k<cells.length; k++){
+ let cellData = this.getCellData(cells[k]);
+ rData[1].push(cellData);
+ }
+ filteredData.push(rData);
+ }
+ return filteredData;
+ }
+
+ /**
+ * Return the filtered data for a given column index
+ * @param {Number} colIndex Colmun's index
+ * @param {Boolean} includeHeaders Optional: include headers row
+ * @return {Array} Flat list of values ['val0','val1','val2'...]
+ *
+ * TODO: provide an API returning data in JSON format
+ */
+ getFilteredDataCol(colIndex, includeHeaders=false){
+ if(Types.isUndef(colIndex)){
+ return [];
+ }
+ let data = this.getFilteredData(),
+ colData = [];
+ if(includeHeaders){
+ colData.push(this.getHeadersText()[colIndex]);
+ }
+ for(let i=0, len=data.length; i<len; i++){
+ let r = data[i],
+ //cols values of current row
+ d = r[1],
+ //data of searched column
+ c = d[colIndex];
+ colData.push(c);
+ }
+ return colData;
+ }
+
+ /**
+ * Get the display value of a row
+ * @param {RowElement} DOM element of the row
+ * @return {String} Usually 'none' or ''
+ */
+ getRowDisplay(row){
+ if(!Types.isObj(row)){
+ return null;
+ }
+ return row.style.display;
+ }
+
+ /**
+ * Validate/invalidate row by setting the 'validRow' attribute on the row
+ * @param {Number} rowIndex Index of the row
+ * @param {Boolean} isValid
+ */
+ validateRow(rowIndex, isValid){
+ let row = this.tbl.rows[rowIndex];
+ if(!row || typeof isValid !== 'boolean'){
+ return;
+ }
+
+ // always visible rows are valid
+ if(this.hasVisibleRows && this.visibleRows.indexOf(rowIndex) !== -1){
+ isValid = true;
+ }
+
+ let displayFlag = isValid ? '' : 'none',
+ validFlag = isValid ? 'true' : 'false';
+ row.style.display = displayFlag;
+
+ if(this.paging){
+ row.setAttribute('validRow', validFlag);
+ }
+ }
+
+ /**
+ * Validate all filterable rows
+ */
+ validateAllRows(){
+ if(!this._hasGrid){
+ return;
+ }
+ this.validRowsIndex = [];
+ for(let k=this.refRow; k<this.nbFilterableRows; k++){
+ this.validateRow(k, true);
+ this.validRowsIndex.push(k);
+ }
+ }
+
+ /**
+ * Set search value to a given filter
+ * @param {Number} index Column's index
+ * @param {String} searcharg Search term
+ */
+ setFilterValue(index, searcharg=''){
+ if((!this.fltGrid && !this.isFirstLoad) ||
+ !this.getFilterElement(index)){
+ return;
+ }
+ let slc = this.getFilterElement(index),
+ fltColType = this.getFilterType(index);
+
+ if(fltColType !== this.fltTypeMulti &&
+ fltColType != this.fltTypeCheckList){
+ slc.value = searcharg;
+ }
+ //multiple selects
+ else if(fltColType === this.fltTypeMulti){
+ let s = searcharg.split(' '+this.orOperator+' ');
+ // let ct = 0; //keywords counter
+ for(let j=0, len=slc.options.length; j<len; j++){
+ let option = slc.options[j];
+ if(s==='' || s[0]===''){
+ option.selected = false;
+ }
+ if(option.value===''){
+ option.selected = false;
+ }
+ if(option.value!=='' &&
+ Arr.has(s, option.value, true)){
+ option.selected = true;
+ }//if
+ }//for j
+ }
+ //checklist
+ else if(fltColType === this.fltTypeCheckList){
+ searcharg = Str.matchCase(searcharg, this.caseSensitive);
+ let sarg = searcharg.split(' '+this.orOperator+' ');
+ let lisNb = Dom.tag(slc,'li').length;
+
+ slc.setAttribute('value', '');
+ slc.setAttribute('indexes', '');
+
+ for(let k=0; k<lisNb; k++){
+ let li = Dom.tag(slc,'li')[k],
+ lbl = Dom.tag(li,'label')[0],
+ chk = Dom.tag(li,'input')[0],
+ lblTxt = Str.matchCase(
+ Dom.getText(lbl), this.caseSensitive);
+ if(lblTxt !== '' && Arr.has(sarg, lblTxt, true)){
+ chk.checked = true;
+ this.Mod.checkList.setCheckListValues(chk);
+ }
+ else{
+ chk.checked = false;
+ this.Mod.checkList.setCheckListValues(chk);
+ }
+ }
+ }
+ }
+
+ /**
+ * Set them columns' widths as per configuration
+ * @param {Number} rowIndex Optional row index to apply the widths to
+ * @param {Element} tbl DOM element
+ */
+ setColWidths(rowIndex, tbl){
+ if(!this.fltGrid || !this.hasColWidths){
+ return;
+ }
+ tbl = tbl || this.tbl;
+ let rIndex;
+ if(rowIndex===undefined){
+ rIndex = tbl.rows[0].style.display!='none' ? 0 : 1;
+ } else{
+ rIndex = rowIndex;
+ }
+
+ setWidths.call(this);
+
+ function setWidths(){
+ /*jshint validthis:true */
+ let nbCols = this.nbCells;
+ let colWidths = this.colWidths;
+ let colTags = Dom.tag(tbl, 'col');
+ let tblHasColTag = colTags.length > 0;
+ let frag = !tblHasColTag ? doc.createDocumentFragment() : null;
+ for(let k=0; k<nbCols; k++){
+ let col;
+ if(tblHasColTag){
+ col = colTags[k];
+ } else {
+ col = Dom.create('col', ['id', this.id+'_col_'+k]);
+ frag.appendChild(col);
+ }
+ col.style.width = colWidths[k];
+ }
+ if(!tblHasColTag){
+ tbl.insertBefore(frag, tbl.firstChild);
+ }
+ }
+ }
+
+ /**
+ * Makes defined rows always visible
+ */
+ enforceVisibility(){
+ if(!this.hasVisibleRows){
+ return;
+ }
+ for(let i=0, len=this.visibleRows.length; i<len; i++){
+ let row = this.visibleRows[i];
+ //row index cannot be > nrows
+ if(row <= this.nbRows){
+ this.validateRow(row, true);
+ }
+ }
+ }
+
+ clearFilters(){
+ this.EvtManager(this.Evt.name.clear);
+ }
+
+ /**
+ * Clear all the filters' values
+ */
+ _clearFilters(){
+ if(!this.fltGrid){
+ return;
+ }
+ if(this.onBeforeReset){
+ this.onBeforeReset.call(null, this, this.getFiltersValue());
+ }
+ for(let i=0, len=this.fltIds.length; i<len; i++){
+ this.setFilterValue(i, '');
+ }
+ if(this.linkedFilters){
+ this.linkFilters();
+ }
+ if(this.rememberPageLen){ Cookie.remove(this.pgLenCookie); }
+ if(this.rememberPageNb){ Cookie.remove(this.pgNbCookie); }
+ if(this.onAfterReset){ this.onAfterReset.call(null, this); }
+ }
+
+ /**
+ * Clears filtered columns visual indicator (background color)
+ */
+ clearActiveColumns(){
+ for(let i=0, len=this.getCellsNb(this.headersRow); i<len; i++){
+ Dom.removeClass(
+ this.getHeaderElement(i), this.activeColumnsCssClass);
+ }
+ }
+
+ /**
+ * Refresh the filters subject to linking ('select', 'multiple',
+ * 'checklist' type)
+ */
+ linkFilters(){
+ if(!this.activeFilterId){
+ return;
+ }
+ let slcA1 = this.getFiltersByType(this.fltTypeSlc, true),
+ slcA2 = this.getFiltersByType(this.fltTypeMulti, true),
+ slcA3 = this.getFiltersByType(this.fltTypeCheckList, true),
+ slcIndex = slcA1.concat(slcA2);
+ slcIndex = slcIndex.concat(slcA3);
+
+ let activeFlt = this.activeFilterId.split('_')[0];
+ activeFlt = activeFlt.split(this.prfxFlt)[1];
+ let slcSelectedValue;
+ for(let i=0, len=slcIndex.length; i<len; i++){
+ let curSlc = Dom.id(this.fltIds[slcIndex[i]]);
+ slcSelectedValue = this.getFilterValue(slcIndex[i]);
+
+ // Welcome to cyclomatic complexity hell :)
+ // TODO: simplify/refactor if statement
+ if(activeFlt!==slcIndex[i] ||
+ (this.paging && slcA1.indexOf(slcIndex[i]) != -1 &&
+ activeFlt === slcIndex[i] ) ||
+ (!this.paging && (slcA3.indexOf(slcIndex[i]) != -1 ||
+ slcA2.indexOf(slcIndex[i]) != -1)) ||
+ slcSelectedValue === this.displayAllText ){
+
+ if(slcA3.indexOf(slcIndex[i]) != -1){
+ this.Mod.checkList.checkListDiv[slcIndex[i]].innerHTML = '';
+ } else {
+ curSlc.innerHTML = '';
+ }
+
+ //1st option needs to be inserted
+ if(this.loadFltOnDemand) {
+ let opt0 = Dom.createOpt(this.displayAllText, '');
+ if(curSlc){
+ curSlc.appendChild(opt0);
+ }
+ }
+
+ if(slcA3.indexOf(slcIndex[i]) != -1){
+ this.Mod.checkList._build(slcIndex[i]);
+ } else {
+ this.Mod.dropdown._build(slcIndex[i], true);
+ }
+
+ this.setFilterValue(slcIndex[i], slcSelectedValue);
+ }
+ }// for i
+ }
+
+ /**
+ * Re-generate the filters grid bar when previously removed
+ */
+ _resetGrid(){
+ if(this.isFirstLoad){
+ return;
+ }
+
+ let Mod = this.Mod;
+ let tbl = this.tbl;
+ let rows = tbl.rows;
+ let filtersRowIndex = this.filtersRowIndex;
+ let filtersRow = rows[filtersRowIndex];
+
+ // grid was removed, grid row element is stored in fltGridEl property
+ if(!this.gridLayout){
+ // If table has a thead ensure the filters row is appended in the
+ // thead element
+ if(tbl.tHead){
+ var tempRow = tbl.tHead.insertRow(this.filtersRowIndex);
+ tbl.tHead.replaceChild(this.fltGridEl, tempRow);
+ } else {
+ filtersRow.parentNode.insertBefore(this.fltGridEl, filtersRow);
+ }
+ }
+
+ // filters are appended in external placeholders elements
+ if(this.isExternalFlt){
+ let externalFltTgtIds = this.externalFltTgtIds;
+ for(let ct=0, len=externalFltTgtIds.length; ct<len; ct++){
+ let extFlt = Dom.id(externalFltTgtIds[ct]);
+
+ if(!extFlt){ continue; }
+
+ let externalFltEl = this.externalFltEls[ct];
+ extFlt.appendChild(externalFltEl);
+ let colFltType = this.getFilterType(ct);
+ //IE special treatment for gridLayout, appended filters are
+ //empty
+ if(this.gridLayout &&
+ externalFltEl.innerHTML === '' &&
+ colFltType !== this.fltTypeInp){
+ if(colFltType === this.fltTypeSlc ||
+ colFltType === this.fltTypeMulti){
+ Mod.dropdown.build(ct);
+ }
+ if(colFltType === this.fltTypeCheckList){
+ Mod.checkList.build(ct);
+ }
+ }
+ }
+ }
+
+ this.nbFilterableRows = this.getRowsNb();
+ this.nbVisibleRows = this.nbFilterableRows;
+ this.nbRows = rows.length;
+
+ if(this.popupFilters){
+ this.headersRow++;
+ Mod.popupFilter.reset();
+ }
+
+ if(!this.gridLayout){
+ Dom.addClass(this.tbl, this.prfxTf);
+ }
+ this._hasGrid = true;
+ }
+
+ /**
+ * Determines if passed filter column implements exact query match
+ * @param {Number} colIndex [description]
+ * @return {Boolean} [description]
+ */
+ isExactMatch(colIndex){
+ let fltType = this.getFilterType(colIndex);
+ return this.exactMatchByCol[colIndex] || this.exactMatch ||
+ (fltType!==this.fltTypeInp);
+ }
+
+ /**
+ * Checks if passed data contains the searched arg
+ * @param {String} arg Search term
+ * @param {String} data Data string
+ * @param {Boolean} exactMatch Exact match
+ * @return {Boolean]}
+ *
+ * TODO: move into string module, remove fltType in order to decouple it
+ * from TableFilter module
+ */
+ _containsStr(arg, data, exactMatch){
+ // Improved by Cedric Wartel (cwl)
+ // automatic exact match for selects and special characters are now
+ // filtered
+ let regexp,
+ modifier = this.caseSensitive ? 'g' : 'gi';
+ if(exactMatch){
+ regexp = new RegExp(
+ '(^\\s*)'+ Str.rgxEsc(arg) +'(\\s*$)', modifier);
+ } else {
+ regexp = new RegExp(Str.rgxEsc(arg), modifier);
+ }
+ return regexp.test(data);
+ }
+
+ /**
+ * Check if passed script or stylesheet is already imported
+ * @param {String} filePath Ressource path
+ * @param {String} type Possible values: 'script' or 'link'
+ * @return {Boolean}
+ */
+ isImported(filePath, type){
+ let imported = false,
+ importType = !type ? 'script' : type,
+ attr = importType == 'script' ? 'src' : 'href',
+ files = Dom.tag(doc, importType);
+ for (let i=0, len=files.length; i<len; i++){
+ if(files[i][attr] === undefined){
+ continue;
+ }
+ if(files[i][attr].match(filePath)){
+ imported = true;
+ break;
+ }
+ }
+ return imported;
+ }
+
+ /**
+ * Import script or stylesheet
+ * @param {String} fileId Ressource ID
+ * @param {String} filePath Ressource path
+ * @param {Function} callback Callback
+ * @param {String} type Possible values: 'script' or 'link'
+ */
+ import(fileId, filePath, callback, type){
+ let ftype = !type ? 'script' : type,
+ imported = this.isImported(filePath, ftype);
+ if(imported){
+ return;
+ }
+ let o = this,
+ isLoaded = false,
+ file,
+ head = Dom.tag(doc, 'head')[0];
+
+ if(Str.lower(ftype) === 'link'){
+ file = Dom.create(
+ 'link',
+ ['id', fileId], ['type', 'text/css'],
+ ['rel', 'stylesheet'], ['href', filePath]
+ );
+ } else {
+ file = Dom.create(
+ 'script', ['id', fileId],
+ ['type', 'text/javascript'], ['src', filePath]
+ );
+ }
+
+ //Browser <> IE onload event works only for scripts, not for stylesheets
+ file.onload = file.onreadystatechange = function(){
+ if(!isLoaded &&
+ (!this.readyState || this.readyState === 'loaded' ||
+ this.readyState === 'complete')){
+ isLoaded = true;
+ if(typeof callback === 'function'){
+ callback.call(null, o);
+ }
+ }
+ };
+ file.onerror = function(){
+ throw new Error('TF script could not load: ' + filePath);
+ };
+ head.appendChild(file);
+ }
+
+ /**
+ * Check if table has filters grid
+ * @return {Boolean}
+ */
+ hasGrid(){
+ return this._hasGrid;
+ }
+
+ /**
+ * Get list of filter IDs
+ * @return {[type]} [description]
+ */
+ getFiltersId(){
+ return this.fltIds || [];
+ }
+
+ /**
+ * Get filtered (valid) rows indexes
+ * @param {Boolean} reCalc Force calculation of filtered rows list
+ * @return {Array} List of row indexes
+ */
+ getValidRows(reCalc){
+ if(!reCalc){
+ return this.validRowsIndex;
+ }
+
+ this.validRowsIndex = [];
+ for(let k=this.refRow; k<this.getRowsNb(true); k++){
+ let r = this.tbl.rows[k];
+ if(!this.paging){
+ if(this.getRowDisplay(r) !== 'none'){
+ this.validRowsIndex.push(r.rowIndex);
+ }
+ } else {
+ if(r.getAttribute('validRow') === 'true' ||
+ r.getAttribute('validRow') === null){
+ this.validRowsIndex.push(r.rowIndex);
+ }
+ }
+ }
+ return this.validRowsIndex;
+ }
+
+ /**
+ * Get the index of the row containing the filters
+ * @return {Number}
+ */
+ getFiltersRowIndex(){
+ return this.filtersRowIndex;
+ }
+
+ /**
+ * Get the index of the headers row
+ * @return {Number}
+ */
+ getHeadersRowIndex(){
+ return this.headersRow;
+ }
+
+ /**
+ * Get the row index from where the filtering process start (1st filterable
+ * row)
+ * @return {Number}
+ */
+ getStartRowIndex(){
+ return this.refRow;
+ }
+
+ /**
+ * Get the index of the last row
+ * @return {Number}
+ */
+ getLastRowIndex(){
+ if(!this._hasGrid){
+ return;
+ }
+ return (this.nbRows-1);
+ }
+
+ /**
+ * Get the header DOM element for a given column index
+ * @param {Number} colIndex Column index
+ * @return {Object}
+ */
+ getHeaderElement(colIndex){
+ let table = this.gridLayout ? this.Mod.gridLayout.headTbl : this.tbl;
+ let tHead = Dom.tag(table, 'thead');
+ let headersRow = this.headersRow;
+ let header;
+ for(let i=0; i<this.nbCells; i++){
+ if(i !== colIndex){
+ continue;
+ }
+ if(tHead.length === 0){
+ header = table.rows[headersRow].cells[i];
+ }
+ if(tHead.length === 1){
+ header = tHead[0].rows[headersRow].cells[i];
+ }
+ break;
+ }
+ return header;
+ }
+
+ /**
+ * Return the list of headers' text
+ * @return {Array} list of headers' text
+ */
+ getHeadersText(){
+ let headers = [];
+ for(let j=0; j<this.nbCells; j++){
+ let header = this.getHeaderElement(j);
+ let headerText = Dom.getText(header);
+ headers.push(headerText);
+ }
+ return headers;
+ }
+
+ /**
+ * Return the filter type for a specified column
+ * @param {Number} colIndex Column's index
+ * @return {String}
+ */
+ getFilterType(colIndex){
+ let colType = this.cfg['col_'+colIndex];
+ return !colType ? this.fltTypeInp : Str.lower(colType);
+ }
+
+ /**
+ * Get the total number of filterable rows
+ * @return {Number}
+ */
+ getFilterableRowsNb(){
+ return this.getRowsNb(false);
+ }
+
+ /**
+ * Get the configuration object (literal object)
+ * @return {Object}
+ */
+ config(){
+ return this.cfg;
+ }
+}
--- /dev/null
+/**
+ * Types utilities
+ */
+
+const UNDEFINED = void 0;
+
+export default {
+ /**
+ * Check if argument is an object or a global object
+ * @param {String or Object} v
+ * @return {Boolean}
+ */
+ isObj(v){
+ let isO = false;
+ if(typeof v === 'string'){
+ if(window[v] && typeof window[v] === 'object'){
+ isO = true;
+ }
+ } else {
+ if(v && typeof v === 'object'){
+ isO = true;
+ }
+ }
+ return isO;
+ },
+
+ /**
+ * Check if argument is a function
+ * @param {Function} fn
+ * @return {Boolean}
+ */
+ isFn(fn){
+ return (fn && fn.constructor == Function);
+ },
+
+ /**
+ * Check if argument is an array
+ * @param {Array} obj
+ * @return {Boolean}
+ */
+ isArray(obj){
+ return (obj && obj.constructor == Array);
+ },
+
+ /**
+ * Determine if argument is undefined
+ * @param {Any} o
+ * @return {Boolean}
+ */
+ isUndef(o){
+ return o === UNDEFINED;
+ },
+
+ /**
+ * Determine if argument is null
+ * @param {Any} o
+ * @return {Boolean}
+ */
+ isNull(o){
+ return o === null;
+ },
+
+ /**
+ * Determine if argument is empty (undefined, null or empty string)
+ * @param {Any} o
+ * @return {Boolean}
+ */
+ isEmpty(o){
+ return this.isUndef(o) || this.isNull(o) || o.length===0;
+ }
+};
--- /dev/null
+Description: Allow building 0ad as root/fakeroot
+Forwarded: not-needed
+Author: Rico Tzschichholz <ricotz@ubuntu.com>
+Last-Update: 2014-10-13
+
+--- a/build/workspaces/update-workspaces.sh
++++ b/build/workspaces/update-workspaces.sh
+@@ -1,10 +1,5 @@
+ #!/bin/sh
+
+-if [ "$(id -u)" = "0" ]; then
+- echo "Running as root will mess up file permissions. Aborting ..." 1>&2
+- exit 1
+-fi
+-
+ die()
+ {
+ echo ERROR: $*
--- /dev/null
+Description: Fix search path for pyrogenesis
+ Modify launcher script to execute /usr/games/pyrogenesis if /usr/games is
+ not in the user's $PATH.
+Forwarded: http://trac.wildfiregames.com/ticket/1424
+Author: Vincent Cheng <vcheng@debian.org>
+Bug-Debian: https://bugs.debian.org/679033
+Bug-Ubuntu: https://bugs.launchpad.net/bugs/1380737
+Last-Update: 2014-10-13
+
+--- a/build/resources/0ad.sh
++++ b/build/resources/0ad.sh
+@@ -3,6 +3,9 @@
+ pyrogenesis=$(which pyrogenesis 2> /dev/null)
+ if [ -x "$pyrogenesis" ] ; then
+ "$pyrogenesis" "$@"
++elif [ -x /usr/games/pyrogenesis ] ; then
++ # Fallback in case /usr/games is not in $PATH; see #679033 and LP: #1380737
++ /usr/games/pyrogenesis "$@"
+ else
+ echo "Error: pyrogenesis not found in ($PATH)"
+ exit 1
--- /dev/null
+Description: Fix FTBFS with gcc-10
+Origin: upstream, https://trac.wildfiregames.com/changeset/23794
+Bug: https://trac.wildfiregames.com/ticket/5756
+Bug-Debian: https://bugs.debian.org/956967
+
+--- a/libraries/source/fcollada/src/FColladaPlugins/FArchiveXML/FAXPhysicsExport.cpp
++++ b/libraries/source/fcollada/src/FColladaPlugins/FArchiveXML/FAXPhysicsExport.cpp
+@@ -329,16 +329,3 @@
+ FArchiveXML::LetWriteObject(physicsRigidBodyParameters->GetPhysicsShape(i), techniqueNode);\r
+ }\r
+ }\r
+-\r
+-template <class TYPE, int QUAL>\r
+-xmlNode* FArchiveXML::AddPhysicsParameter(xmlNode* parentNode, const char* name, FCDParameterAnimatableT<TYPE,QUAL>& value)\r
+-{\r
+- xmlNode* paramNode = AddChild(parentNode, name);\r
+- AddContent(paramNode, FUStringConversion::ToString((TYPE&) value));\r
+- if (value.IsAnimated())\r
+- {\r
+- const FCDAnimated* animated = value.GetAnimated();\r
+- FArchiveXML::WriteAnimatedValue(animated, paramNode, name);\r
+- }\r
+- return paramNode;\r
+-}\r
+--- a/libraries/source/fcollada/src/FColladaPlugins/FArchiveXML/FArchiveXML.h
++++ b/libraries/source/fcollada/src/FColladaPlugins/FArchiveXML/FArchiveXML.h
+@@ -553,7 +553,16 @@
+ \r
+ static void WritePhysicsRigidBodyParameters(FCDPhysicsRigidBodyParameters* physicsRigidBodyParameters, xmlNode* techniqueNode);\r
+ template <class TYPE, int QUAL>\r
+- static xmlNode* AddPhysicsParameter(xmlNode* parentNode, const char* name, FCDParameterAnimatableT<TYPE,QUAL>& value);\r
++ static xmlNode* AddPhysicsParameter(xmlNode* parentNode, const char* name, FCDParameterAnimatableT<TYPE,QUAL>& value) {\r
++ xmlNode* paramNode = AddChild(parentNode, name);\r
++ AddContent(paramNode, FUStringConversion::ToString((TYPE&) value));\r
++ if (value.IsAnimated())\r
++ {\r
++ const FCDAnimated* animated = value.GetAnimated();\r
++ FArchiveXML::WriteAnimatedValue(animated, paramNode, name);\r
++ }\r
++ return paramNode;\r
++ }\r
+ \r
+ \r
+ //\r
+--- a/source/simulation2/helpers/HierarchicalPathfinder.cpp
++++ b/source/simulation2/helpers/HierarchicalPathfinder.cpp
+@@ -697,28 +697,6 @@
+ jGoal = jBest;
+ }
+
+-void HierarchicalPathfinder::FindReachableRegions(RegionID from, std::set<RegionID>& reachable, pass_class_t passClass)
+-{
+- // Flood-fill the region graph, starting at 'from',
+- // collecting all the regions that are reachable via edges
+-
+- std::vector<RegionID> open;
+- open.push_back(from);
+- reachable.insert(from);
+-
+- while (!open.empty())
+- {
+- RegionID curr = open.back();
+- open.pop_back();
+-
+- for (const RegionID& region : m_Edges[passClass][curr])
+- // Add to the reachable set; if this is the first time we added
+- // it then also add it to the open list
+- if (reachable.insert(region).second)
+- open.push_back(region);
+- }
+-}
+-
+ void HierarchicalPathfinder::FindPassableRegions(std::set<RegionID>& regions, pass_class_t passClass)
+ {
+ // Construct a set of all regions of all chunks for this pass class
+--- a/source/simulation2/helpers/HierarchicalPathfinder.h
++++ b/source/simulation2/helpers/HierarchicalPathfinder.h
+@@ -148,7 +148,27 @@
+
+ void FindEdges(u8 ci, u8 cj, pass_class_t passClass, EdgesMap& edges);
+
+- void FindReachableRegions(RegionID from, std::set<RegionID>& reachable, pass_class_t passClass);
++ void FindReachableRegions(RegionID from, std::set<RegionID>& reachable, pass_class_t passClass)
++ {
++ // Flood-fill the region graph, starting at 'from',
++ // collecting all the regions that are reachable via edges
++
++ std::vector<RegionID> open;
++ open.push_back(from);
++ reachable.insert(from);
++
++ while (!open.empty())
++ {
++ RegionID curr = open.back();
++ open.pop_back();
++
++ for (const RegionID& region : m_Edges[passClass][curr])
++ // Add to the reachable set; if this is the first time we added
++ // it then also add it to the open list
++ if (reachable.insert(region).second)
++ open.push_back(region);
++ }
++ }
+
+ void FindPassableRegions(std::set<RegionID>& regions, pass_class_t passClass);
+
--- /dev/null
+From: Nicolas Auvray <na.itms76@gmail.com>
+Date: Thu, 13 Aug 2020 18:27:44 +0200
+Subject: Fix #949699 on 0.0.23b
+Description: Workaround for L3 cache detection of Ryzen 3000
+Bug: https://trac.wildfiregames.com/ticket/4360
+Bug-Debian: https://bugs.debian.org/949699
+
+--- a/source/lib/sysdep/arch/x86_x64/cache.cpp
++++ b/source/lib/sysdep/arch/x86_x64/cache.cpp
+@@ -89,7 +89,8 @@
+ static const size_t associativityTable[16] =
+ {
+ 0, 1, 2, 0, 4, 0, 8, 0,
+- 16, 0, 32, 48, 64, 96, 128, x86_x64::Cache::fullyAssociative
++ // TODO: The second '16' does not obey to the specifications and is only a workaround. For a correct implementation please look here: https://community.amd.com/thread/244207
++ 16, 16, 32, 48, 64, 96, 128, x86_x64::Cache::fullyAssociative
+ };
+
+ static x86_x64::Cache L2Cache(u32 reg, x86_x64::Cache::Type type)
--- /dev/null
+Description: Update cxxtestgen shebang.
+Author: Dimitri John Ledkov <xnox@ubuntu.com>
+Bug-Debian: https://bugs.debian.org/967118
+Forwarded: not-needed
+Last-Update: 2020-08-18
+
+--- a/libraries/source/cxxtest-4.4/bin/cxxtestgen
++++ b/libraries/source/cxxtest-4.4/bin/cxxtestgen
+@@ -1,4 +1,4 @@
+-#! /usr/bin/env python
++#! /usr/bin/env python2
+ #
+ # The CxxTest driver script, which uses the cxxtest Python package.
+ #
--- /dev/null
+allow-build-with-root.patch
+fix-bindir.patch
+fix-ftbfs-gcc-10.patch
+python2.patch
+fix-ryzen-l3-cache-error.patch
--- /dev/null
+.so man6/0ad.6
--- /dev/null
+#!/usr/bin/make -f
+
+# mozjs' build process does not seem to be compatible with other shells
+# like zsh
+export SHELL = /bin/sh
+
+export DEB_BUILD_MAINT_OPTIONS = hardening=+all
+DPKG_EXPORT_BUILDFLAGS = 1
+include /usr/share/dpkg/buildflags.mk
+
+include /usr/share/dpkg/architecture.mk
+
+PARALLEL_JOBS=$(shell getconf _NPROCESSORS_ONLN)
+ifeq ($(findstring parallel=,$(DEB_BUILD_OPTIONS)),)
+ export DEB_BUILD_OPTIONS+=parallel=$(PARALLEL_JOBS)
+endif
+
+%:
+ dh $@
+
+override_dh_auto_clean:
+ build/workspaces/clean-workspaces.sh
+ # Clean up some extra cruft not picked up by clean-workspaces.sh
+ find binaries/system/ -type f ! -name readme.txt -delete
+ rm -f libraries/fcollada/lib/*.a
+ rm -f build/premake/.*.tmp
+ rm -f libraries/source/spidermonkey/lib/*
+ rm -f libraries/source/cxxtest-4.4/python/cxxtest/*.pyc
+ rm -f libraries/source/fcollada/lib/*
+ rm -rf libraries/source/spidermonkey/include-unix-*
+ rm -f source/ps/tests/stub_impl_hack.cpp
+ dh_auto_clean
+
+override_dh_auto_build:
+ mkdir -p libraries/source/fcollada/lib
+ build/workspaces/update-workspaces.sh \
+ --bindir=/usr/games \
+ --libdir=/usr/lib/games/0ad \
+ --datadir=/usr/share/games/0ad \
+ --with-system-nvtt \
+ -j$(PARALLEL_JOBS)
+
+ $(MAKE) config=release verbose=1 -C build/workspaces/gcc \
+ -j$(PARALLEL_JOBS)
+
+override_dh_auto_test:
+ # Note: Avoid running tests from root dir of build, otherwise certain
+ # tests (i.e. in testsuite MeshManager) may not work as intended and
+ # create spurious directories above root dir (../data/mods).
+ifeq (,$(filter armhf arm64 kfreebsd-amd64 kfreebsd-i386, $(DEB_HOST_ARCH)))
+ cd binaries/system/ && LD_LIBRARY_PATH=. ./test -libdir .
+endif
+
+override_dh_auto_install:
+ install -Dm 0755 build/resources/0ad.sh $(CURDIR)/debian/tmp/usr/games/0ad
+ dh_auto_install
+
+override_dh_strip:
+ dh_strip --dbgsym-migration='0ad-dbg (<< 0.0.20-2~)'
+
+override_dh_makeshlibs:
+ dh_makeshlibs -Xusr/lib/games/0ad
--- /dev/null
+3.0 (quilt)
--- /dev/null
+# Not a problem since 0ad and 0ad-data are uploaded at the same time, and
+# dpkg-gencontrol doesn't provide any other substitution variables for packages
+# to depend on versioned packages built from a separate source package
+0ad source: version-substvar-for-external-package
+# Source for the following files can be found in debian/missing-sources/
+# (they trigger lintian errors because they are not named the same as the
+# non-minified files in debian/missing-sources/, or contain inline
+# javascript dependencies). See debian/missing-sources/README.txt for details.
+0ad source: insane-line-length-in-source-file source/tools/replayprofile/jquery.flot.js line length is 2981 characters (>512)
+0ad source: source-contains-prebuilt-javascript-object source/tools/replayprofile/jquery.flot.js line length is 2981 characters (>512)
+0ad source: source-is-missing source/tools/replayprofile/jquery.flot.js line length is 2981 characters (>512)
+0ad source: insane-line-length-in-source-file source/tools/replayprofile/jquery.flot.navigate.js line length is 1984 characters (>512)
+0ad source: source-contains-prebuilt-javascript-object source/tools/replayprofile/jquery.flot.navigate.js line length is 1984 characters (>512)
+0ad source: source-is-missing source/tools/replayprofile/jquery.flot.navigate.js line length is 1984 characters (>512)
+0ad source: insane-line-length-in-source-file source/tools/templatesanalyzer/tablefilter/tablefilter.js line length is 32005 characters (>512)
+0ad source: source-contains-prebuilt-javascript-object source/tools/templatesanalyzer/tablefilter/tablefilter.js line length is 32005 characters (>512)
+0ad source: source-is-missing source/tools/templatesanalyzer/tablefilter/tablefilter.js line length is 32005 characters (>512)
+0ad source: insane-line-length-in-source-file source/tools/templatesanalyzer/tablefilter/tf-1.js line length is 32004 characters (>512)
+0ad source: source-contains-prebuilt-javascript-object source/tools/templatesanalyzer/tablefilter/tf-1.js line length is 32004 characters (>512)
+0ad source: source-is-missing source/tools/templatesanalyzer/tablefilter/tf-1.js line length is 32004 characters (>512)
+
+## Currently unused overrides/comments:
+# These files actually are non-minified source code. Silly lintian assumes
+# that all javascript files with lines that have more than 512 characters must
+# be minified javascript files, which is a faulty assumption here.
+#
+# This is where the lintian source-is-missing tag suggests putting non-minified
+# files in, in place of files in the source tarball that only have minified
+# versions. I know for a fact that none of the javascript files in this
+# directory are minified (lintian is just being stupid here...).
+#0ad source: source-is-missing debian/missing-sources/*.js
--- /dev/null
+Name: 0 A.D.
+Bug-Database: https://trac.wildfiregames.com/report
+Contact: https://play0ad.com/about/contact-us/
+Documentation: https://trac.wildfiregames.com/wiki
+FAQ: https://trac.wildfiregames.com/wiki
+Repository: https://svn.wildfiregames.com/public/ps/trunk/
+Repository-Browse: https://trac.wildfiregames.com/browser
--- /dev/null
+version=3
+http://sf.net/zero-ad/0ad-([\d\.]+)-.*-unix-build\.tar\.xz