0ad (0.0.25b-2) unstable; urgency=medium
authorSimon McVittie <smcv@debian.org>
Mon, 28 Mar 2022 11:29:22 +0000 (12:29 +0100)
committerSimon McVittie <smcv@debian.org>
Mon, 28 Mar 2022 11:29:22 +0000 (12:29 +0100)
  * Team upload
  * Add patches from Ubuntu (Closes: #1008075)
    - d/p/fix-build-mozjs-with-python-3.10.patch:
      Add patch from upstream via Ubuntu to fix FTBFS with Python 3.10
    - d/p/fix-build-atlas-gcc11-glibc-2.35.patch:
      Add patch from upstream via Ubuntu to fix future FTBFS with glibc 2.35

[dgit import unpatched 0ad 0.0.25b-2]

55 files changed:
1  2 
debian/0ad.6
debian/changelog
debian/control
debian/copyright
debian/docs
debian/install
debian/manpages
debian/missing-sources/README.txt
debian/missing-sources/jquery.colorhelpers.js
debian/missing-sources/jquery.event.drag.js
debian/missing-sources/jquery.mousewheel.js
debian/missing-sources/tablefilter/array.js
debian/missing-sources/tablefilter/cookie.js
debian/missing-sources/tablefilter/date.js
debian/missing-sources/tablefilter/dom.js
debian/missing-sources/tablefilter/event.js
debian/missing-sources/tablefilter/extensions/advancedGrid/adapterEzEditTable.js
debian/missing-sources/tablefilter/extensions/advancedGrid/advancedGrid.js
debian/missing-sources/tablefilter/extensions/colOps/colOps.js
debian/missing-sources/tablefilter/extensions/colsVisibility/colsVisibility.js
debian/missing-sources/tablefilter/extensions/filtersVisibility/filtersVisibility.js
debian/missing-sources/tablefilter/extensions/sort/adapterSortabletable.js
debian/missing-sources/tablefilter/extensions/sort/sort.js
debian/missing-sources/tablefilter/helpers.js
debian/missing-sources/tablefilter/modules/alternateRows.js
debian/missing-sources/tablefilter/modules/checkList.js
debian/missing-sources/tablefilter/modules/clearButton.js
debian/missing-sources/tablefilter/modules/dropdown.js
debian/missing-sources/tablefilter/modules/feature.js
debian/missing-sources/tablefilter/modules/gridLayout.js
debian/missing-sources/tablefilter/modules/help.js
debian/missing-sources/tablefilter/modules/highlightKeywords.js
debian/missing-sources/tablefilter/modules/loader.js
debian/missing-sources/tablefilter/modules/paging.js
debian/missing-sources/tablefilter/modules/popupFilter.js
debian/missing-sources/tablefilter/modules/rowsCounter.js
debian/missing-sources/tablefilter/modules/statusBar.js
debian/missing-sources/tablefilter/modules/store.js
debian/missing-sources/tablefilter/sort.js
debian/missing-sources/tablefilter/string.js
debian/missing-sources/tablefilter/tablefilter.js
debian/missing-sources/tablefilter/types.js
debian/patches/Fix-build-mozjs-on-armhf.patch
debian/patches/TestStunClient
debian/patches/allow-build-with-root.patch
debian/patches/fix-bindir.patch
debian/patches/fix-build-atlas-gcc11-glibc-2.35.patch
debian/patches/fix-build-mozjs-with-python-3.10.patch
debian/patches/series
debian/pyrogenesis.6
debian/rules
debian/source/format
debian/source/lintian-overrides
debian/upstream/metadata
debian/watch

diff --cc debian/0ad.6
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ab71e275c18e5bad440939b8821deca12c665536
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,147 @@@
++.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.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9feaf15ad1e86fd0074abec50279c996d08815c5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,443 @@@
++0ad (0.0.25b-2) unstable; urgency=medium
++
++  * Team upload
++  * Add patches from Ubuntu (Closes: #1008075)
++    - d/p/fix-build-mozjs-with-python-3.10.patch:
++      Add patch from upstream via Ubuntu to fix FTBFS with Python 3.10
++    - d/p/fix-build-atlas-gcc11-glibc-2.35.patch:
++      Add patch from upstream via Ubuntu to fix future FTBFS with glibc 2.35
++
++ -- Simon McVittie <smcv@debian.org>  Mon, 28 Mar 2022 12:29:22 +0100
++
++0ad (0.0.25b-1.1) unstable; urgency=medium
++
++  * Non-maintainer upload.
++  * Fix building mozjs on armhf (Closes: #1001882)
++
++ -- Shengjing Zhu <zhsj@debian.org>  Thu, 23 Dec 2021 01:34:12 +0800
++
++0ad (0.0.25b-1) unstable; urgency=medium
++
++  [David W. Kennedy <dave_k@reasoned.us>]
++  * Package new upstream release. 
++  * d/control: Update required versions of dependencies.
++  * d/copyright: Update and correct copyright information of debian package
++    and embedded libraries
++  * d/install: Install binaries/system/readme.txt as README.command-line.txt,
++    also mark install executable to support renaming README.txt with dh-exec.
++  * d/rules: Clean up build files for libnvtt and spidermonkey in 
++    dh_auto_clean.
++  * d/rules: Exclude libmozjs78-ps-release.so from dh_dwz in order to work 
++    around a crash in dwz.
++  * d/source/local-options: Abort on changes to the upstream source code 
++    before committing to the upstream branch of Debian Salsa VCS
++  * d/watch: Update URL for releases.
++
++  [ Ludovic Rousseau ]
++  * Fix "New upstream release of 0ad - version 0.0.25" (Closes: #992017)
++  * d/control: use https:// for Homepage: URL
++  * d/install: make the script executable
++  * New upstream release 0.0.25b
++  * d/patches/TestStunClient: remove failing test
++
++ -- Ludovic Rousseau <rousseau@debian.org>  Fri, 27 Aug 2021 15:28:30 +0200
++
++0ad (0.0.24b-2) UNRELEASED; urgency=medium
++
++  [ Pino Toscano ]
++  * Install the AppStream file to /usr/share/metainfo, as /usr/share/appdata
++    is a long time deprecated location.
++  * Install the application icon in the XDG hicolor icon theme, rather than
++    the legacy pixmaps location.
++
++ -- Debian Games Team <pkg-games-devel@lists.alioth.debian.org>  Tue, 11 May 2021 08:48:59 +0200
++
++0ad (0.0.24b-1) experimental; urgency=medium
++
++  [ Phil Morrell ]
++  * New upstream release.
++  * Drop upstreamed patches.
++  * Update build-deps. (Closes: #936101, #975379)
++
++  [ Ludovic Rousseau ]
++  * version 0.0.24b
++  * d/copyright: remove unused entry
++    lintian: unused-file-paragraph-in-dep5-copyright paragraph at line 72
++  * d/copyright: remove unused entry
++    lintian: unused-file-paragraph-in-dep5-copyright paragraph at line 22
++  * use embedded nvtt since version 2.1.0 is not yet in unstable
++  * Fix "New upstream release 0.0.24" (Closes: #983408)
++
++ -- Ludovic Rousseau <rousseau@debian.org>  Sun, 07 Mar 2021 10:53:17 +0100
++
++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
diff --cc debian/control
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a9a9677f2648abb85e5645fa25d4a959e4065400
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,62 @@@
++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,
++ automake,
++ cargo,
++ cmake,
++ debhelper-compat (= 12),
++ dh-exec (>= 0.1),
++ dpkg-dev (>= 1.15.5),
++ libboost-dev (>= 1.57.0.1),
++ libboost-filesystem-dev (>= 1.57.0.1),
++ libcurl4-gnutls-dev (>= 7.32.0) | libcurl4-dev (>= 7.32.0),
++ libenet-dev (>= 1.3),
++ libfmt-dev (>= 4.0.0),
++ libgloox-dev (>= 1.0.10),
++ libicu-dev (>= 67.1-4~),
++ libminiupnpc-dev (>= 1.6),
++ libogg-dev,
++ libopenal-dev,
++ libpng-dev,
++ libsdl2-dev (>= 2.0.5),
++ libsodium-dev (>= 1.0.14),
++ libvorbis-dev,
++ libwxgtk3.0-gtk3-dev,
++ libxcursor-dev,
++ libxml2-dev,
++ llvm,
++ pkg-config,
++ python3,
++ rustc (>= 1.41),
++ tzdata,
++ zlib1g-dev (>= 1:1.2.3)
++Standards-Version: 4.5.0
++Homepage: https://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.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a47bd49268bc21dbaa1bf02260fae382404a15ce
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,581 @@@
++Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
++Upstream-Name: 0ad
++Source: https://releases.wildfiregames.com/
++
++Files: *
++Copyright: 2000-2021 Wildfire Games
++License: GPL-2.0+
++
++
++Files: debian/*
++Copyright: 2010-2012 Rico Tzschichholz <ricotz@ubuntu.com>
++           2010      Bertrand Marc <BeberKing@gmail.com>
++           2011-2020 Vincent Cheng <vcheng@debian.org>
++           2011-2012 Ansgar Burchardt <ansgar@debian.org>
++           2017-2021 Ludovic Rousseau <rousseau@debian.org>
++           2019      Bruno Kleinert <fuddl@debian.org>
++           2021      Phil Morrell <debian@emorrp1.name>
++           2021      Pino Toscano <pino@debian.org>
++           2021      David W. Kennedy <dave_k@reasoned.us>
++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/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: 2006 Simon Brown <si@sjbrown.co.uk>
++           2007-2009 NVIDIA Corporation
++           2006, 2008-2016 Ignacio Castano <castano@gmail.com>
++License: Expat
++
++Files: libraries/source/nvtt/src/src/nvtt/squish/*
++Copyright: 2006 Simon Brown <si@sjbrown.co.uk>
++           2006 Ignacio Castano <castano@gmail.com>
++           2016 Raptor Engineering, LLC
++License: Expat
++
++Files: libraries/source/nvtt/src/src/nvcore/Array.h
++       libraries/source/nvtt/src/src/nvcore/Array.inl
++       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/DefsVcWin32.h
++       libraries/source/nvtt/src/src/nvcore/FileSystem.cpp
++       libraries/source/nvtt/src/src/nvcore/FileSystem.h
++       libraries/source/nvtt/src/src/nvcore/ForEach.h
++       libraries/source/nvtt/src/src/nvcore/Hash.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/RefCounted.h
++       libraries/source/nvtt/src/src/nvcore/StdStream.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/Timer.cpp
++       libraries/source/nvtt/src/src/nvcore/Timer.h
++       libraries/source/nvtt/src/src/nvcore/Tokenizer.cpp
++       libraries/source/nvtt/src/src/nvcore/Tokenizer.h
++       libraries/source/nvtt/src/src/nvcore/Utils.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/ColorSpace.cpp
++       libraries/source/nvtt/src/src/nvimage/ColorSpace.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/KtxFile.cpp
++       libraries/source/nvtt/src/src/nvimage/KtxFile.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.cpp
++       libraries/source/nvtt/src/src/nvmath/Box.h
++       libraries/source/nvtt/src/src/nvmath/Color.cpp
++       libraries/source/nvtt/src/src/nvmath/Color.h
++       libraries/source/nvtt/src/src/nvmath/Color.inl
++       libraries/source/nvtt/src/src/nvmath/Fitting.cpp
++       libraries/source/nvtt/src/src/nvmath/Fitting.h
++       libraries/source/nvtt/src/src/nvmath/Matrix.h
++       libraries/source/nvtt/src/src/nvmath/Matrix.inl
++       libraries/source/nvtt/src/src/nvmath/Montecarlo.cpp
++       libraries/source/nvtt/src/src/nvmath/Montecarlo.h
++       libraries/source/nvtt/src/src/nvmath/PackedFloat.cpp
++       libraries/source/nvtt/src/src/nvmath/PackedFloat.h
++       libraries/source/nvtt/src/src/nvmath/Plane.cpp
++       libraries/source/nvtt/src/src/nvmath/Plane.h
++       libraries/source/nvtt/src/src/nvmath/Plane.inl
++       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/SimdVector.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/Vector.inl
++       libraries/source/nvtt/src/src/nvmath/ftoi.h
++       libraries/source/nvtt/src/src/nvmath/nvmath.h
++       libraries/source/nvtt/src/src/nvthread/Atomic.h
++       libraries/source/nvtt/src/src/nvthread/Event.cpp
++       libraries/source/nvtt/src/src/nvthread/Event.h
++       libraries/source/nvtt/src/src/nvthread/Mutex.cpp
++       libraries/source/nvtt/src/src/nvthread/Mutex.h
++       libraries/source/nvtt/src/src/nvthread/nvthread.cpp
++       libraries/source/nvtt/src/src/nvthread/nvthread.h
++       libraries/source/nvtt/src/src/nvthread/ParallelFor.cpp
++       libraries/source/nvtt/src/src/nvthread/ParallelFor.h
++       libraries/source/nvtt/src/src/nvthread/Thread.cpp
++       libraries/source/nvtt/src/src/nvthread/Thread.h
++       libraries/source/nvtt/src/src/nvthread/ThreadPool.cpp
++       libraries/source/nvtt/src/src/nvthread/ThreadPool.h
++       libraries/source/nvtt/src/src/nvthread/Win32.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/nvtt/src/src/nvmath/Gamma.*
++Copyright: 2017 Ken Cooke <ken@highfidelity.io>
++License: Expat
++
++Files: libraries/source/nvtt/src/src/nvmath/Half.*
++Copyright: 2006 Mike Acton <macton@gmail.com>
++License: Expat
++
++Files: libraries/source/nvtt/src/src/nvmath/SimdVector_SSE.h
++Copyright: 2006 Simon Brown <si@sjbrown.co.uk>
++License: Expat
++
++Files: libraries/source/nvtt/src/src/nvmath/SimdVector_VE.h
++Copyright: 2006 Simon Brown <si@sjbrown.co.uk>
++           2016 Raptor Engineering
++License: Expat
++
++Files: libraries/source/nvtt/src/src/nvmath/Matrix.cpp
++Copyright: Paul Heckbert <ph@cs.cmu.edu>
++           1999-2004 Michael Garland
++           Ignacio Castano <castanyo@yahoo.es>
++License: Expat
++
++Files: libraries/source/nvtt/src/extern/poshlib/posh.*
++Copyright: 2002-2006 Brian Hook
++License: BSD-3-clause
++
++Files: libraries/source/spidermonkey/*
++Copyright: 1998-2020 Mozilla Corporation
++License: MPL-2.0 and GPL-2.0 and LGPL-2.1
++
++Files: libraries/source/valgrind/*
++Copyright: 2003-2012 Josef Weidendorfer
++           2000-2012 Julian Seward
++License: BSD-4-clause
++
++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-2.0
++   This Source Code Form is subject to the terms of the Mozilla Public
++   License, v. 2.0. If a copy of the MPL was not distributed with this
++   file, You can obtain one at http://mozilla.org/MPL/2.0/.
++   .
++   On Debian systems, the complete text of the Mozilla Public License,
++   version 2 can be found in "/usr/share/common-licenses/MPL-2.0".
++
++License: BSD-4-clause
++   Redistribution and use in source and binary forms, with or without
++   modification, are permitted provided that the following conditions
++   are met:
++   .
++   1. Redistributions of source code must retain the above copyright
++      notice, this list of conditions and the following disclaimer.
++   .
++   2. The origin of this software must not be misrepresented; you must
++      not claim that you wrote the original software.  If you use this
++      software in a product, an acknowledgment in the product
++      documentation would be appreciated but is not required.
++   .
++   3. Altered source versions must be plainly marked as such, and must
++      not be misrepresented as being the original software.
++   .
++   4. The name of the author may not be used to endorse or promote
++      products derived from this software without specific prior written
++      permission.
++   .
++   THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
++   OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
++   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++   ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR 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.
diff --cc debian/docs
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..71dfd5bac5a9df12a99ae681d974a58845c43fae
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++README.txt
diff --cc debian/install
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6fa8d8a103c4016549d51d7e073824fe153b85f2
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++#!/usr/bin/dh-exec --with=install
++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/libmozjs78-ps-release.so        usr/lib/games/0ad/
++binaries/system/ActorEditor                     usr/lib/games/0ad/
++binaries/system/libnvtt.so                      usr/lib/games/0ad/
++binaries/system/libnvcore.so                    usr/lib/games/0ad/
++binaries/system/libnvimage.so                   usr/lib/games/0ad/
++binaries/system/libnvmath.so                    usr/lib/games/0ad/
++binaries/system/pyrogenesis                     usr/games/
++build/resources/0ad.appdata.xml                 usr/share/metainfo/
++build/resources/0ad.desktop                     usr/share/applications/
++build/resources/0ad.png                         usr/share/icons/hicolor/128x128/apps/
++usr/games/0ad
++binaries/system/readme.txt =>                   usr/share/doc/0ad/README.command-line.txt
diff --cc debian/manpages
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4ff5397f2be0b3000bec79e7267052b82206a30e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2 @@@
++debian/0ad.6
++debian/pyrogenesis.6
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..427fdd40a715ff98dc321ffb47697051bbf449c3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++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)
++
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fa44961217ad9ff0eaa637db0186804c50450ed9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,174 @@@
++/* 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]
++    };    
++})();
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ad66b10f85e2ea9b288ce66f38f0dd579efaf1b6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,137 @@@
++/*! \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
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..528defee05476540f7ceb66f0457b536921386cd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,79 @@@
++/*! 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);
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dd10bc570642b99114a85c0f0d51bded0b791c33
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++/**
++ * 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;
++    }
++};
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..eeb3a93cc9f34d575553f16eb15329fca1ba5ccb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,58 @@@
++/**
++ * 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];
++    }
++
++};
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1eafe48fc27dbe3be3d36cde3aaf7a400c8ffbd7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,172 @@@
++/**
++ * 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;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..175bda9220c962c12f51f3c04c82f65464bf9380
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,142 @@@
++/**
++ * 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;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0b93b59b1e760a02d2fa8698052f49e69a8c5697
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,53 @@@
++/**
++ * 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));
++    }
++};
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..98dc80207ee6cffde13bc1e01aecad60bf528be7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,375 @@@
++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;
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e548ef442dbb4c740fc1c89c61534f61ee5ad3f7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++import AdapterEzEditTable from './adapterEzEditTable';
++
++export default AdapterEzEditTable;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..aa9ca6682c69cc69528b3bee17c2ba65aaef63ea
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,315 @@@
++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(){}
++
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..51818613349992eef69f57137a1af2d066b30422
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,520 @@@
++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&#9660;';
++        //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' : '';
++            }
++        }
++    }
++
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..962079bd7c55b72c754c864a996eafed9c14cb85
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,184 @@@
++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;
++    }
++
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9b5e19931fd1ad8117801b0f7cfa5bafce7cc743
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,460 @@@
++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;
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8ccf00122abee89c5edf742269ab2fc8ce0eaf74
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++// import 'script!sortabletable';
++import AdapterSortableTable from './adapterSortabletable';
++
++if(!window.SortableTable){
++    require('script!sortabletable');
++}
++
++export default AdapterSortableTable;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0c2e420e54870f5db4dd0e4b837ed5460b410756
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++/**
++ * 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;
++    }
++};
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c9099b48ae1b4460439ac1508d1be696e7b10ced
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,95 @@@
++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;
++    }
++
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9753834d26b6bbee46e00f17c0d5350d6c33a76f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,419 @@@
++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);
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6af1d844c3d004bc3f6e02c1633f6e728abbec24
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,95 @@@
++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;
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a033e650ce634ef19402f9945e70eb2746560f0a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,342 @@@
++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;
++    }
++
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f7861ce2bfe55a5eb5e6796970ce72a442975ec5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,37 @@@
++
++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;
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..959d674af0c514e80e0429d9744a42a99f8952c1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,333 @@@
++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;
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7f0ae77f6cd391837b74db9561678a788ea38f11
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,155 @@@
++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>&lt;</b>, <b>&lt;=</b>, <b>&gt;</b>, ' +
++            '<b>&gt;=</b>, <b>=</b>, <b>*</b>, <b>!</b>, <b>{</b>, <b>}</b>, ' +
++            '<b>||</b>,<b>&amp;&amp;</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>&copy;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;
++    }
++
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ff4417c8b16c32ba6d9048b3a4434b59a3e52653
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,108 @@@
++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 = [];
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fb7a583f344ca993621ac368b5306aed65e6a270
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,102 @@@
++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;
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a5473c8baed60238805fa53faf576332add2d80d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,784 @@@
++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;
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f2de5b220c9a49445a66a1b793219f1d2275b336
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,253 @@@
++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;
++    }
++
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c110cd9ff7825afdabc828ff2c6e108b55e7e57e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,139 @@@
++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;
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..44bac19831db75b06112bb402f6d4cc5ffc6b0a4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,125 @@@
++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;
++    }
++
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d24757dd3c8990165dd9f91e9271e5f8c17a77da
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,100 @@@
++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);
++    }
++
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fc7cb79ce06255a9d38c19f76950e2481c99f129
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++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));
++    }
++};
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..452de37ff43d98be9be66e690da526701172d78f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,39 @@@
++/**
++ * 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;
++    }
++
++};
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bdd323cc8c1d4ed8f9016b0b5f0ab034ed501d40
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2762 @@@
++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;
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..771ff4852af81e05eae0677bff23014425148566
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,71 @@@
++/**
++ * 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;
++    }
++};
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9476ce16c09a15da2a93982a3f457f8196e788ae
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,75 @@@
++From: Shengjing Zhu <zhsj@debian.org>
++Date: Thu, 23 Dec 2021 01:31:00 +0800
++Subject: Fix build mozjs on armhf
++
++---
++ .../Fix-armhf-build-for-GCC-11-627.patch           | 45 ++++++++++++++++++++++
++ libraries/source/spidermonkey/patch.sh             |  3 ++
++ 2 files changed, 48 insertions(+)
++ create mode 100644 libraries/source/spidermonkey/Fix-armhf-build-for-GCC-11-627.patch
++
++diff --git a/libraries/source/spidermonkey/Fix-armhf-build-for-GCC-11-627.patch b/libraries/source/spidermonkey/Fix-armhf-build-for-GCC-11-627.patch
++new file mode 100644
++index 0000000..6d9364a
++--- /dev/null
+++++ b/libraries/source/spidermonkey/Fix-armhf-build-for-GCC-11-627.patch
++@@ -0,0 +1,45 @@
+++From 9b97780f1b2f936d960b74b8f7a3b851f607a0f4 Mon Sep 17 00:00:00 2001
+++From: Ximin Luo <infinity0@pwned.gg>
+++Date: Thu, 14 Oct 2021 15:23:48 +0100
+++Subject: [PATCH] Fix armhf build for GCC-11 (#627)
+++
+++GCC 11 changed the default flag to put the FP option in the -march flag (arm7-a+fp) rather than a specific FPU flag. Therefore it needs to be given separately now.
+++
+++See https://bugs.launchpad.net/ubuntu/+source/gcc-defaults/+bug/1939379 for some more details.
+++
+++Origin: https://github.com/alexcrichton/cc-rs/commit/b2f6b146b75299c444e05bbde50d03705c7c4b6e
+++Forwarded: https://github.com/alexcrichton/cc-rs/pull/627
+++Bug-Debian: https://bugs.debian.org/1001314
+++---
+++ third_party/rust/cc/.cargo-checksum.json | 2 +-
+++ third_party/rust/cc/src/lib.rs           | 5 +++++
+++ 2 files changed, 6 insertions(+), 1 deletion(-)
+++
+++diff --git a/third_party/rust/cc/.cargo-checksum.json b/third_party/rust/cc/.cargo-checksum.json
+++index 417fde795..43992977a 100644
+++--- a/third_party/rust/cc/.cargo-checksum.json
++++++ b/third_party/rust/cc/.cargo-checksum.json
+++@@ -1 +1 @@
+++-{"files":{"Cargo.lock":"3aff5f8b0a7f4d72852b11b0526f0002e6bf55f19f1ebd6470d7f97fbd540e60","Cargo.toml":"6ab10d9b6a9c6f0909074e6698c90c6b6a7223661ec2e83174d2593117cbe7f2","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"7184fbdf375a057e673257348f6d7584c0dd11b66318d98f3647f69eb610b097","src/bin/gcc-shim.rs":"b77907875029494b6288841c3aed2e4939ed40708c7f597fca5c9e2570490ca6","src/com.rs":"bcdaf1c28b71e6ef889c6b08d1ce9d7c0761344a677f523bc4c3cd297957f804","src/lib.rs":"4753929dbb7b676c19d7cfa06d0a47e37003554b80c536cbf2b892d591ef61c2","src/registry.rs":"3cc1b5a50879fa751572878ae1d0afbfc960c11665258492754b2c8bccb0ff5d","src/setup_config.rs":"7014103587d3382eac599cb76f016e2609b8140970861b2237982d1db24af265","src/winapi.rs":"ea8b7edbb9ff87957254f465c2334e714c5d6b3b19a8d757c48ea7ca0881c50c","src/windows_registry.rs":"388e79dcf3e84078ae0b086c6cdee9cf9eb7e3ffafdcbf3e2df26163661f5856","tests/cc_env.rs":"e02b3b0824ad039b47e4462c5ef6dbe6c824c28e7953af94a0f28f7b5158042e","tests/cflags.rs":"57f06eb5ce1557e5b4a032d0c4673e18fbe6f8d26c1deb153126e368b96b41b3","tests/cxxflags.rs":"c2c6c6d8a0d7146616fa1caed26876ee7bc9fcfffd525eb4743593cade5f3371","tests/support/mod.rs":"71620b178583b6e6e5e0d4cac14e2cef6afc62fb6841e0c72ed1784543abf8ac","tests/test.rs":"1605640c9b94a77f48fc92e1dc0485bdf1960da5626e2e00279e4703691656bc"},"package":"aa87058dce70a3ff5621797f1506cb837edd02ac4c0ae642b4542dce802908b8"}
+++\ No newline at end of file
++++{"files":{"Cargo.lock":"3aff5f8b0a7f4d72852b11b0526f0002e6bf55f19f1ebd6470d7f97fbd540e60","Cargo.toml":"6ab10d9b6a9c6f0909074e6698c90c6b6a7223661ec2e83174d2593117cbe7f2","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"7184fbdf375a057e673257348f6d7584c0dd11b66318d98f3647f69eb610b097","src/bin/gcc-shim.rs":"b77907875029494b6288841c3aed2e4939ed40708c7f597fca5c9e2570490ca6","src/com.rs":"bcdaf1c28b71e6ef889c6b08d1ce9d7c0761344a677f523bc4c3cd297957f804","src/lib.rs":"bb2df0dc5fd2acf8c474bab64080bd374cca9b3568aaf16cfe8fc6b5253fdeb3","src/registry.rs":"3cc1b5a50879fa751572878ae1d0afbfc960c11665258492754b2c8bccb0ff5d","src/setup_config.rs":"7014103587d3382eac599cb76f016e2609b8140970861b2237982d1db24af265","src/winapi.rs":"ea8b7edbb9ff87957254f465c2334e714c5d6b3b19a8d757c48ea7ca0881c50c","src/windows_registry.rs":"388e79dcf3e84078ae0b086c6cdee9cf9eb7e3ffafdcbf3e2df26163661f5856","tests/cc_env.rs":"e02b3b0824ad039b47e4462c5ef6dbe6c824c28e7953af94a0f28f7b5158042e","tests/cflags.rs":"57f06eb5ce1557e5b4a032d0c4673e18fbe6f8d26c1deb153126e368b96b41b3","tests/cxxflags.rs":"c2c6c6d8a0d7146616fa1caed26876ee7bc9fcfffd525eb4743593cade5f3371","tests/support/mod.rs":"71620b178583b6e6e5e0d4cac14e2cef6afc62fb6841e0c72ed1784543abf8ac","tests/test.rs":"1605640c9b94a77f48fc92e1dc0485bdf1960da5626e2e00279e4703691656bc"},"package":"aa87058dce70a3ff5621797f1506cb837edd02ac4c0ae642b4542dce802908b8"}
+++\ No newline at end of file
+++diff --git a/third_party/rust/cc/src/lib.rs b/third_party/rust/cc/src/lib.rs
+++index 621d31d6b..f6a3f750c 100644
+++--- a/third_party/rust/cc/src/lib.rs
++++++ b/third_party/rust/cc/src/lib.rs
+++@@ -1457,6 +1457,11 @@ impl Build {
+++                     && target.contains("-linux-")
+++                 {
+++                     cmd.args.push("-march=armv7-a".into());
++++
++++                    if target.ends_with("eabihf") {
++++                        // lowest common denominator FPU
++++                        cmd.args.push("-mfpu=vfpv3-d16".into());
++++                    }
+++                 }
+++ 
+++                 // (x86 Android doesn't say "eabi")
+++-- 
+++2.34.1
+++
++diff --git a/libraries/source/spidermonkey/patch.sh b/libraries/source/spidermonkey/patch.sh
++index 879964b..9059fd6 100644
++--- a/libraries/source/spidermonkey/patch.sh
+++++ b/libraries/source/spidermonkey/patch.sh
++@@ -43,6 +43,9 @@ patch -p1 < ../FixRpiUnalignedFpAccess.diff
++ # Note that this isn't quite the upstream patch to match our version.
++ patch -p1 < ../FixRust150.diff
++ 
+++# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1001882
+++patch -p1 < ../Fix-armhf-build-for-GCC-11-627.patch
+++
++ # Patch those separately, as they might interfere with normal behaviour.
++ if [ "$(uname -s)" = "FreeBSD" ];
++ then
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..faa0e3b6d1dead107151bc551caa9a15324322c4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,90 @@@
++Description: remove this test since it fails in the build environement
++ The failure is:
++ In TestStunClient::test_local_ip:
++ ./source/network/tests/test_StunClient.h:43: Error: Assertion failed: StunClient::FindLocalIP(ip)
++ ./source/network/tests/test_StunClient.h:47: Error: Test failed: StunClient::FindLocalIP did not return a valid IPV4 address: wrong size
++Author: Ludovic Rousseau <rousseau@debian.org>
++Last-Update: 2021-08-27
++
++--- a/source/network/tests/test_StunClient.h
+++++ /dev/null
++@@ -1,79 +0,0 @@
++-/* Copyright (C) 2021 Wildfire Games.
++- * This file is part of 0 A.D.
++- *
++- * 0 A.D. 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.
++- *
++- * 0 A.D. 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 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
++- */
++-
++-#include "lib/self_test.h"
++-
++-#include "network/StunClient.h"
++-
++-#include "lib/external_libraries/enet.h"
++-#include "ps/ConfigDB.h"
++-#include "ps/CStr.h"
++-
++-class TestStunClient : public CxxTest::TestSuite
++-{
++-public:
++-     void setUp()
++-     {
++-             // Sets networking up in a cross-platform manner.
++-             enet_initialize();
++-     }
++-
++-     void tearDown()
++-     {
++-             enet_deinitialize();
++-     }
++-
++-     void test_local_ip()
++-     {
++-             CStr ip;
++-             TS_ASSERT(StunClient::FindLocalIP(ip));
++-             // Quick validation that this looks like a valid IP address.
++-             if (ip.size() < 8 || ip.size() > 15)
++-             {
++-                     TS_FAIL("StunClient::FindLocalIP did not return a valid IPV4 address: wrong size");
++-                     return;
++-             }
++-             int dots = 0;
++-             for (char c : ip)
++-             {
++-                     if (c == '.')
++-                             ++dots;
++-                     else if (c < '0' && c > '9')
++-                     {
++-                             TS_FAIL("StunClient::FindLocalIP did not return a valid IPV4 address: wrong character");
++-                             return;
++-                     }
++-             }
++-             if (dots != 3)
++-                     TS_FAIL("StunClient::FindLocalIP did not return a valid IPV4 address: wrong separators");
++-     }
++-
++-     void test_stun_DISABLED()
++-     {
++-             // Disabled test -> should return your external IP by connecting to our STUN server.
++-             CConfigDB::Initialise();
++-             CStr ip;
++-             u16 port;
++-             g_ConfigDB.SetValueString(CFG_COMMAND, "lobby.stun.server", "lobby.wildfiregames.com");
++-             g_ConfigDB.SetValueString(CFG_COMMAND, "lobby.stun.port", "3478");
++-             ENetAddress addr { ENET_HOST_ANY, ENET_PORT_ANY };
++-             ENetHost* host = enet_host_create(&addr, 1, 1, 0, 0);
++-             StunClient::FindPublicIP(*host, ip, port);
++-             LOGWARNING("%s %i", ip.c_str(), port);
++-             CConfigDB::Shutdown();
++-     }
++-};
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..076b5245cb5d2a6c471f1bbf289fc4836df5116a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++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: $*
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a9eb42afdaee87084e019a867847f2fa84823231
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++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
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7926f9ca6b0066103ebdb43d75ba5be0d97ef424
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,157 @@@
++From: Vladislav Belov <vladislavbelov>
++Date: Thu, 3 Mar 2022 19:10:05 +0100
++Subject: Replaces M_PIf by M_PI in Atlas,
++ fixes compilation with gcc 11.2.0 and glibc 2.35.
++
++There was added a workaround in glibc to fix tests.
++
++Refs:
++https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103735
++https://sourceware.org/bugzilla/show_bug.cgi?id=28713
++
++Origin: upstream, commit:https://trac.wildfiregames.com/changeset/26536
++Bug-Debian: https://bugs.debian.org/1008075
++Bug-RedHat: https://bugzilla.redhat.com/show_bug.cgi?id=2045149
++---
++ .../Sections/Environment/Environment.cpp           | 88 ++++++++++++++--------
++ 1 file changed, 57 insertions(+), 31 deletions(-)
++
++diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.cpp
++index d4796ec..2cc2652 100644
++--- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.cpp
+++++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.cpp
++@@ -1,4 +1,4 @@
++-/* Copyright (C) 2021 Wildfire Games.
+++/* Copyright (C) 2022 Wildfire Games.
++  * This file is part of 0 A.D.
++  *
++  * 0 A.D. is free software: you can redistribute it and/or modify
++@@ -29,8 +29,6 @@ using AtlasMessage::Shareable;
++ 
++ static Observable<AtlasMessage::sEnvironmentSettings> g_EnvironmentSettings;
++ 
++-const float M_PIf = 3.14159265f;
++-
++ //////////////////////////////////////////////////////////////////////////
++ 
++ class VariableSliderBox : public wxPanel
++@@ -85,12 +83,15 @@ public:
++              : wxPanel(parent),
++              m_Var(var)
++      {
++-             m_Conn = g_EnvironmentSettings.RegisterObserver(0, &VariableListBox::OnSettingsChange, this);
+++             m_Conn = g_EnvironmentSettings.RegisterObserver(
+++                     0, &VariableListBox::OnSettingsChange, this);
++ 
++              m_Sizer = new wxStaticBoxSizer(wxVERTICAL, this, label);
++              SetSizer(m_Sizer);
++ 
++-             m_Combo = new wxComboBox(this, -1, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxArrayString(), wxCB_READONLY),
+++             m_Combo = new wxComboBox(
+++                     this, -1, wxEmptyString, wxDefaultPosition, wxDefaultSize,
+++                     wxArrayString(), wxCB_READONLY),
++              m_Sizer->Add(m_Combo, wxSizerFlags().Expand());
++      }
++ 
++@@ -206,8 +207,9 @@ static void SendToGame(const AtlasMessage::sEnvironmentSettings& settings)
++      POST_COMMAND(SetEnvironmentSettings, (settings));
++ }
++ 
++-EnvironmentSidebar::EnvironmentSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebarContainer, wxWindow* bottomBarContainer)
++-: Sidebar(scenarioEditor, sidebarContainer, bottomBarContainer)
+++EnvironmentSidebar::EnvironmentSidebar(
+++     ScenarioEditor& scenarioEditor, wxWindow* sidebarContainer, wxWindow* bottomBarContainer)
+++     : Sidebar(scenarioEditor, sidebarContainer, bottomBarContainer)
++ {
++      wxSizer* scrollSizer = new wxBoxSizer(wxVERTICAL);
++      wxScrolledWindow* scrolledWindow = new wxScrolledWindow(this);
++@@ -217,15 +219,24 @@ EnvironmentSidebar::EnvironmentSidebar(ScenarioEditor& scenarioEditor, wxWindow*
++ 
++      wxSizer* waterSizer = new wxStaticBoxSizer(wxVERTICAL, scrolledWindow, _T("Water settings"));
++      scrollSizer->Add(waterSizer, wxSizerFlags().Expand());
++-     waterSizer->Add(new wxButton(scrolledWindow, ID_RecomputeWaterData, _("Reset Water Data")), wxSizerFlags().Expand());
++-     waterSizer->Add(m_WaterTypeList = new VariableListBox(scrolledWindow, _("Water Type"), g_EnvironmentSettings.watertype), wxSizerFlags().Expand());
++-     waterSizer->Add(new VariableSliderBox(scrolledWindow, _("Water height"), g_EnvironmentSettings.waterheight, 0.f, 1.2f), wxSizerFlags().Expand());
++-     waterSizer->Add(new wxButton(scrolledWindow, ID_PickWaterHeight, _("Pick Water Height")), wxSizerFlags().Expand());
++-     waterSizer->Add(new VariableSliderBox(scrolledWindow, _("Water waviness"), g_EnvironmentSettings.waterwaviness, 0.f, 10.f), wxSizerFlags().Expand());
++-     waterSizer->Add(new VariableSliderBox(scrolledWindow, _("Water murkiness"), g_EnvironmentSettings.watermurkiness, 0.f, 1.f), wxSizerFlags().Expand());
++-     waterSizer->Add(new VariableSliderBox(scrolledWindow, _("Wind angle"), g_EnvironmentSettings.windangle, -M_PIf, M_PIf), wxSizerFlags().Expand());
++-     waterSizer->Add(new VariableColorBox(scrolledWindow, _("Water color"), g_EnvironmentSettings.watercolor), wxSizerFlags().Expand());
++-     waterSizer->Add(new VariableColorBox(scrolledWindow, _("Water tint"), g_EnvironmentSettings.watertint), wxSizerFlags().Expand());
+++     waterSizer->Add(new wxButton(
+++             scrolledWindow, ID_RecomputeWaterData, _("Reset Water Data")), wxSizerFlags().Expand());
+++     waterSizer->Add(m_WaterTypeList = new VariableListBox(
+++             scrolledWindow, _("Water Type"), g_EnvironmentSettings.watertype), wxSizerFlags().Expand());
+++     waterSizer->Add(new VariableSliderBox(
+++             scrolledWindow, _("Water height"), g_EnvironmentSettings.waterheight, 0.f, 1.2f), wxSizerFlags().Expand());
+++     waterSizer->Add(new wxButton(
+++             scrolledWindow, ID_PickWaterHeight, _("Pick Water Height")), wxSizerFlags().Expand());
+++     waterSizer->Add(new VariableSliderBox(
+++             scrolledWindow, _("Water waviness"), g_EnvironmentSettings.waterwaviness, 0.f, 10.f), wxSizerFlags().Expand());
+++     waterSizer->Add(new VariableSliderBox(
+++             scrolledWindow, _("Water murkiness"), g_EnvironmentSettings.watermurkiness, 0.f, 1.f), wxSizerFlags().Expand());
+++     waterSizer->Add(new VariableSliderBox(
+++             scrolledWindow, _("Wind angle"), g_EnvironmentSettings.windangle, -static_cast<float>(M_PI), static_cast<float>(M_PI)), wxSizerFlags().Expand());
+++     waterSizer->Add(new VariableColorBox(
+++             scrolledWindow, _("Water color"), g_EnvironmentSettings.watercolor), wxSizerFlags().Expand());
+++     waterSizer->Add(new VariableColorBox(
+++             scrolledWindow, _("Water tint"), g_EnvironmentSettings.watertint), wxSizerFlags().Expand());
++ 
++      std::vector<std::wstring> list;
++      list.push_back(L"ocean"); list.push_back(L"lake"); list.push_back(L"clap");
++@@ -235,25 +246,40 @@ EnvironmentSidebar::EnvironmentSidebar(ScenarioEditor& scenarioEditor, wxWindow*
++      wxSizer* sunSizer = new wxStaticBoxSizer(wxVERTICAL, scrolledWindow, _T("Sun / lighting settings"));
++      scrollSizer->Add(sunSizer, wxSizerFlags().Expand().Border(wxTOP, 8));
++ 
++-     sunSizer->Add(new VariableSliderBox(scrolledWindow, _("Sun rotation"), g_EnvironmentSettings.sunrotation, -M_PIf, M_PIf), wxSizerFlags().Expand());
++-     sunSizer->Add(new VariableSliderBox(scrolledWindow, _("Sun elevation"), g_EnvironmentSettings.sunelevation, -M_PIf/2, M_PIf/2), wxSizerFlags().Expand());
++-     sunSizer->Add(new VariableSliderBox(scrolledWindow, _("Sun overbrightness"), g_EnvironmentSettings.sunoverbrightness, 1.0f, 3.0f), wxSizerFlags().Expand());
++-     sunSizer->Add(new LightControl(scrolledWindow, wxSize(150, 150), g_EnvironmentSettings));
++-     sunSizer->Add(new VariableColorBox(scrolledWindow, _("Sun color"), g_EnvironmentSettings.suncolor), wxSizerFlags().Expand());
++-     sunSizer->Add(m_SkyList = new VariableListBox(scrolledWindow, _("Sky set"), g_EnvironmentSettings.skyset), wxSizerFlags().Expand());
++-     sunSizer->Add(new VariableSliderBox(scrolledWindow, _("Fog Factor"), g_EnvironmentSettings.fogfactor, 0.0f, 0.01f), wxSizerFlags().Expand());
++-     sunSizer->Add(new VariableSliderBox(scrolledWindow, _("Fog Thickness"), g_EnvironmentSettings.fogmax, 0.5f, 0.0f), wxSizerFlags().Expand());
++-     sunSizer->Add(new VariableColorBox(scrolledWindow, _("Fog color"), g_EnvironmentSettings.fogcolor), wxSizerFlags().Expand());
++-     sunSizer->Add(new VariableColorBox(scrolledWindow, _("Ambient color"), g_EnvironmentSettings.ambientcolor), wxSizerFlags().Expand());
+++     sunSizer->Add(new VariableSliderBox(
+++             scrolledWindow, _("Sun rotation"), g_EnvironmentSettings.sunrotation, -static_cast<float>(M_PI), static_cast<float>(M_PI)), wxSizerFlags().Expand());
+++     sunSizer->Add(new VariableSliderBox(
+++             scrolledWindow, _("Sun elevation"), g_EnvironmentSettings.sunelevation, -static_cast<float>(M_PI) / 2.0f, static_cast<float>(M_PI) / 2.0f), wxSizerFlags().Expand());
+++     sunSizer->Add(new VariableSliderBox(
+++             scrolledWindow, _("Sun overbrightness"), g_EnvironmentSettings.sunoverbrightness, 1.0f, 3.0f), wxSizerFlags().Expand());
+++     sunSizer->Add(new LightControl(
+++             scrolledWindow, wxSize(150, 150), g_EnvironmentSettings));
+++     sunSizer->Add(new VariableColorBox(
+++             scrolledWindow, _("Sun color"), g_EnvironmentSettings.suncolor), wxSizerFlags().Expand());
+++     sunSizer->Add(m_SkyList = new VariableListBox(
+++             scrolledWindow, _("Sky set"), g_EnvironmentSettings.skyset), wxSizerFlags().Expand());
+++     sunSizer->Add(new VariableSliderBox(
+++             scrolledWindow, _("Fog Factor"), g_EnvironmentSettings.fogfactor, 0.0f, 0.01f), wxSizerFlags().Expand());
+++     sunSizer->Add(new VariableSliderBox(
+++             scrolledWindow, _("Fog Thickness"), g_EnvironmentSettings.fogmax, 0.5f, 0.0f), wxSizerFlags().Expand());
+++     sunSizer->Add(new VariableColorBox(
+++             scrolledWindow, _("Fog color"), g_EnvironmentSettings.fogcolor), wxSizerFlags().Expand());
+++     sunSizer->Add(new VariableColorBox(
+++             scrolledWindow, _("Ambient color"), g_EnvironmentSettings.ambientcolor), wxSizerFlags().Expand());
++ 
++      wxSizer* postProcSizer = new wxStaticBoxSizer(wxVERTICAL, scrolledWindow, _T("Post-processing settings"));
++      scrollSizer->Add(postProcSizer, wxSizerFlags().Expand().Border(wxTOP, 8));
++ 
++-     postProcSizer->Add(m_PostEffectList = new VariableListBox(scrolledWindow, _("Post Effect"), g_EnvironmentSettings.posteffect), wxSizerFlags().Expand());
++-     postProcSizer->Add(new VariableSliderBox(scrolledWindow, _("Brightness"), g_EnvironmentSettings.brightness, -0.5f, 0.5f), wxSizerFlags().Expand());
++-     postProcSizer->Add(new VariableSliderBox(scrolledWindow, _("Contrast (HDR)"), g_EnvironmentSettings.contrast, 0.5f, 1.5f), wxSizerFlags().Expand());
++-     postProcSizer->Add(new VariableSliderBox(scrolledWindow, _("Saturation"), g_EnvironmentSettings.saturation, 0.0f, 2.0f), wxSizerFlags().Expand());
++-     postProcSizer->Add(new VariableSliderBox(scrolledWindow, _("Bloom"), g_EnvironmentSettings.bloom, 0.2f, 0.0f), wxSizerFlags().Expand());
+++     postProcSizer->Add(m_PostEffectList = new VariableListBox(
+++             scrolledWindow, _("Post Effect"), g_EnvironmentSettings.posteffect), wxSizerFlags().Expand());
+++     postProcSizer->Add(new VariableSliderBox(
+++             scrolledWindow, _("Brightness"), g_EnvironmentSettings.brightness, -0.5f, 0.5f), wxSizerFlags().Expand());
+++     postProcSizer->Add(new VariableSliderBox(
+++             scrolledWindow, _("Contrast (HDR)"), g_EnvironmentSettings.contrast, 0.5f, 1.5f), wxSizerFlags().Expand());
+++     postProcSizer->Add(new VariableSliderBox(
+++             scrolledWindow, _("Saturation"), g_EnvironmentSettings.saturation, 0.0f, 2.0f), wxSizerFlags().Expand());
+++     postProcSizer->Add(new VariableSliderBox(
+++             scrolledWindow, _("Bloom"), g_EnvironmentSettings.bloom, 0.2f, 0.0f), wxSizerFlags().Expand());
++ 
++      m_Conn = g_EnvironmentSettings.RegisterObserver(0, &SendToGame);
++ }
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..df685ef24984f796d683c1cdda42b59766322f11
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,167 @@@
++From: s0600204 <s0600204>
++Date: Wed, 23 Feb 2022 21:30:38 +0100
++Subject: Fix building spidermonkey on systems with python 3.10
++
++Tested by:
++
++Langbart - macOS 10.15.7: homebrewed python 3.9.9 & 3.10.1
++andy5995 - Manjaro 21.2.3: python 3.10.2
++s0600204 - ArchLinux: python 3.10.2
++
++Bug: https://wildfiregames.com/forum/topic/65901-christmas-testing-bundle/#comment-472056
++Origin: upstream, commit:https://trac.wildfiregames.com/changeset/26475
++Origin: https://code.wildfiregames.com/D4437
++Bug-Debian: https://bugs.debian.org/1008075
++---
++ .../spidermonkey/FixPythonCollectionABC.diff       | 87 ++++++++++++++++++++++
++ .../spidermonkey/FixVirtualenvForPython310.diff    | 15 ++++
++ libraries/source/spidermonkey/patch.sh             | 13 ++++
++ 3 files changed, 115 insertions(+)
++ create mode 100644 libraries/source/spidermonkey/FixPythonCollectionABC.diff
++ create mode 100644 libraries/source/spidermonkey/FixVirtualenvForPython310.diff
++
++diff --git a/libraries/source/spidermonkey/FixPythonCollectionABC.diff b/libraries/source/spidermonkey/FixPythonCollectionABC.diff
++new file mode 100644
++index 0000000..536a534
++--- /dev/null
+++++ b/libraries/source/spidermonkey/FixPythonCollectionABC.diff
++@@ -0,0 +1,87 @@
+++--- a/python/mach/mach/config.py
++++++ b/python/mach/mach/config.py
+++@@ -144,7 +144,7 @@
+++     return _
+++ 
+++ 
+++-class ConfigSettings(collections.Mapping):
++++class ConfigSettings(collections.abc.Mapping):
+++     """Interface for configuration settings.
+++ 
+++     This is the main interface to the configuration.
+++@@ -190,7 +190,7 @@
+++     will result in exceptions being raised.
+++     """
+++ 
+++-    class ConfigSection(collections.MutableMapping, object):
++++    class ConfigSection(collections.abc.MutableMapping, object):
+++         """Represents an individual config section."""
+++         def __init__(self, config, name, settings):
+++             object.__setattr__(self, '_config', config)
+++--- a/python/mach/mach/decorators.py
++++++ b/python/mach/mach/decorators.py
+++@@ -159,7 +159,7 @@
+++               'Conditions argument must take a list ' + \
+++               'of functions. Found %s instead.'
+++ 
+++-        if not isinstance(command.conditions, collections.Iterable):
++++        if not isinstance(command.conditions, collections.abc.Iterable):
+++             msg = msg % (command.name, type(command.conditions))
+++             raise MachError(msg)
+++ 
+++--- a/python/mach/mach/main.py
++++++ b/python/mach/mach/main.py
+++@@ -16,7 +16,7 @@
+++ import sys
+++ import traceback
+++ import uuid
+++-from collections import Iterable
++++from collections.abc import Iterable
+++ 
+++ from six import string_types
+++ 
+++--- a/python/mozbuild/mozbuild/backend/configenvironment.py
++++++ b/python/mozbuild/mozbuild/backend/configenvironment.py
+++@@ -9,7 +9,8 @@
+++ import sys
+++ import json
+++ 
+++-from collections import Iterable, OrderedDict
++++from collections import OrderedDict
++++from collections.abc import Iterable
+++ from types import ModuleType
+++ 
+++ import mozpack.path as mozpath
+++--- a/python/mozbuild/mozbuild/makeutil.py
++++++ b/python/mozbuild/mozbuild/makeutil.py
+++@@ -7,7 +7,7 @@
+++ import os
+++ import re
+++ import six
+++-from collections import Iterable
++++from collections.abc import Iterable
+++ 
+++ 
+++ class Makefile(object):
+++--- a/python/mozbuild/mozbuild/util.py
++++++ b/python/mozbuild/mozbuild/util.py
+++@@ -782,7 +782,7 @@
+++         self._strings = StrictOrderingOnAppendList()
+++         self._children = {}
+++ 
+++-    class StringListAdaptor(collections.Sequence):
++++    class StringListAdaptor(collections.abc.Sequence):
+++         def __init__(self, hsl):
+++             self._hsl = hsl
+++ 
+++--- a/testing/mozbase/manifestparser/manifestparser/filters.py
++++++ b/testing/mozbase/manifestparser/manifestparser/filters.py
+++@@ -15,1 +15,2 @@
+++-from collections import defaultdict, MutableSequence
++++from collections import defaultdict
++++from collections.abc import MutableSequence
+++--- a/third_party/python/pipenv/pipenv/vendor/jinja2/sandbox.py
++++++ b/third_party/python/pipenv/pipenv/vendor/jinja2/sandbox.py
+++@@ -82,1 +82,1 @@
+++-from collections import MutableSet, MutableMapping, MutableSequence
++++from collections.abc import MutableSet, MutableMapping, MutableSequence
++diff --git a/libraries/source/spidermonkey/FixVirtualenvForPython310.diff b/libraries/source/spidermonkey/FixVirtualenvForPython310.diff
++new file mode 100644
++index 0000000..d023c63
++--- /dev/null
+++++ b/libraries/source/spidermonkey/FixVirtualenvForPython310.diff
++@@ -0,0 +1,15 @@
+++--- a/third_party/python/virtualenv/virtualenv.py
++++++ b/third_party/python/virtualenv/virtualenv.py
+++@@ -1804,7 +1804,11 @@
+++         pass
+++     else:
+++         # noinspection PyProtectedMember
+++-        if sysconfig._get_default_scheme() == "posix_local":
++++        try: # Python >= 3.10
++++            default_scheme = sysconfig.get_default_scheme()
++++        except: # Python < 3.10
++++            default_scheme = sysconfig._get_default_scheme()
++++        if default_scheme == "posix_local":
+++             local_path = os.path.join(home_dir, "local")
+++             if not os.path.exists(local_path):
+++                 os.mkdir(local_path)
++diff --git a/libraries/source/spidermonkey/patch.sh b/libraries/source/spidermonkey/patch.sh
++index 9059fd6..ca2b67c 100644
++--- a/libraries/source/spidermonkey/patch.sh
+++++ b/libraries/source/spidermonkey/patch.sh
++@@ -19,6 +19,14 @@ patch -p1 < ../FixSharedArray.diff
++ # (mentionned in the comments, no patch/commit found)
++ patch -p1 < ../FixPublicExport.diff
++ 
+++# In python 3.10 `sysconfig._get_default_scheme()` was renamed to
+++# `sysconfig.get_default_scheme()`. This breaks the version of
+++# `virtualenv` bundled with the spidermonkey source code.
+++#
+++# It is assumed that the updated version fetched for macOS systems
+++# above does not have this problem.
+++patch -p1 < ../FixVirtualenvForPython310.diff
+++
++ # Fix Rooted<void*> not working on VS17
++ # https://bugzilla.mozilla.org/show_bug.cgi?id=1679736
++ # (Landed in 85)
++@@ -34,6 +42,11 @@ patch -p1 < ../FixMSVCRootedVoid.diff
++ # so this patches it to an arbitrarily high Mac OS 11
++ patch -p1 < ../FixMacBuild.diff
++ 
+++# In python 3.3, the Collections' Abstract Base Classes were moved from `collections` to
+++# `collections.abc`, and aliases were set up for backwards compatibility.
+++# In python 3.10, these aliases were removed, requiring all code that used them to update.
+++patch -p1 < ../FixPythonCollectionABC.diff
+++
++ # Fix FP access breaking compilation on RPI3+
++ # https://bugzilla.mozilla.org/show_bug.cgi?id=1526653
++ # https://bugzilla.mozilla.org/show_bug.cgi?id=1536491
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..03f34253237666c4cee44998bcfe46f79b5e7e3f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++TestStunClient
++allow-build-with-root.patch
++fix-bindir.patch
++Fix-build-mozjs-on-armhf.patch
++fix-build-mozjs-with-python-3.10.patch
++fix-build-atlas-gcc11-glibc-2.35.patch
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..513070fe5cfa8ecc1bb1e4146b21836275f95a78
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++.so man6/0ad.6
diff --cc debian/rules
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..24085ff9c5677f0a80640043ac03d441fdf6731d
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,66 @@@
++#!/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 -rf 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 -rf libraries/source/spidermonkey/mozjs-78.6.0
++      rm -f libraries/source/nvtt/lib/*.so
++      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 \
++              -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
++
++override_dh_dwz:
++      dh_dwz -Xlibmozjs78-ps-release.so
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..163aaf8d82b6c54f23c45f32895dbdfdcc27b047
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++3.0 (quilt)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0a1f48eb5c71d16cebdee8c5437dc745c3084a0b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++# 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
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3ff486b79ecb55973eb8ef45cc74e48f99a97c48
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++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
diff --cc debian/watch
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7b0938a75d325b9a1250bce05f12874839448ac4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2 @@@
++version=3
++https://releases.wildfiregames.com/0ad-([\d\.]+)-.*-unix-build\.tar\.gz