Passage à npm pour les dependances

This commit is contained in:
Xavier Leune
2017-10-08 14:54:53 +02:00
parent c563bb9c0f
commit 899d951ab2
364 changed files with 37 additions and 79320 deletions
+1
View File
@@ -16,6 +16,7 @@ deps.lock
/htdocs/cache
/htdocs/tmp/twig
node_modules
/configs/application/config.php
/tests/simpletest
/tests/unitaires/config.php
-15
View File
@@ -1,15 +0,0 @@
# editorconfig.org
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
# Trailing whitespace is significant in markdown files.
[*.md]
trim_trailing_whitespace = false
-37
View File
@@ -1,37 +0,0 @@
{
"parser": "babel-eslint",
"parserOptions": {
"sourceType": "module",
"ecmaFeatures": { "modules": true }
},
"rules": {
"max-len": [2, 80, 2, {"ignoreUrls": true}],
"indent": [2, 4, {"SwitchCase": 1}],
"semi": ["error", "always"],
"no-trailing-spaces": 2,
"no-multi-spaces": 2,
"array-bracket-spacing": 2,
"keyword-spacing": ["error", { "after": true, "before": true }],
"max-depth": [2, 7],
"max-statements": [2, 143],
"complexity": [2, 32],
"no-unused-vars": 2,
"no-eval": 2,
"no-underscore-dangle": 0,
"no-loop-func": 2,
"no-floating-decimal": 2,
"curly": 2,
"eqeqeq": [2, "smart"],
"quotes": [2, "single"],
"new-cap": 2,
"radix": [2, "always"]
},
"env": {
"es6": true,
"browser": true,
"node": false
},
"globals": {
"__webpack_public_path__": false
}
}
-10
View File
@@ -1,10 +0,0 @@
demos
docs
node_modules
report
.grunt
npm-debug.log
.codio
.settings
.vscode
-17
View File
@@ -1,17 +0,0 @@
language: node_js
node_js:
- '6.3.0'
before_script:
- npm install grunt-cli -g
- npm install codecov -g
script:
- npm test
- npm run codecov
- npm run deploy
branches:
only:
- master
- /^greenkeeper/.*$/
env:
global:
- secure: A1G8GvJkV0rjy7XCTVdOpTHy3xaoSZZAbMWhI+ikrqBqd8mRz+sB71FhRusouTcYdsT5VfF9Io2doS8LKAeP0TNC34Pp0uvjtsvarzn8a/oNEOuqR3Ub0ws2bmbZIZc+wOpgErKOj1H1QSJAUpd6ZjIuEAbOVXlhGBJz3zUCmcpRDh32CpFKC62oFWeGlvttxPciLLzBfKgkVKEGhPtdGP/xCHL1MCQptYVHZiXwWsaIQ5wHFO6KCVlRrPgdfOL+Yce3mT02iXH6ZjW6U6zA6vYQVQZVD873AkU5RmirYblW+jW1wdvu4UXI71lSH6Z3uXRVnrw1b0TsLVTjP9ZUbCtkTHtLbxYzeDjEukxKoCjpAppIhOtaNIxrdA8oKJAabQYp5X+QK6lkosy0zdT5u2B1+g8unZhsf0y//7lgLUe04iQ7sc1Q6AHiiEGtByaXg4BHNW53bUfKgNnbV4+IbXf8rz5wWOxL2/yWAU/GoiszjqRQfajAXCpSf6SyMjXjhhvQdeFn+Cz6FwdtaxH+tOIY0Hq9Gqy1xrLIkv/httd3O+AbhLrU1c/M0MwlFQue7GeJb7ZyF3KsK7bXvoz2dEqvzHd98NZXiQEqFXCIs77uVh4eZMoYrbEyrkOAgkUZNQYhHh9fuvfynJ/zgUvyA0v3GUvBebq3ybYKD/vqX7s=
-130
View File
@@ -1,130 +0,0 @@
# Contributing to TableFilter
:+1::tada: First off, thanks for taking the time to contribute! :tada::+1:
## Reporting bugs
Bugs are tracked as [GitHub issues](https://guides.github.com/features/issues/).
Create an issue and provide the following information.
Explain the problem and include additional details to help maintainers reproduce
the problem:
* **Use a clear, concise and descriptive title** for the issue to identify the
problem.
* **Describe the exact steps which reproduce the problem** in as many details
as possible.
* **Provide specific examples to demonstrate the steps**. Include links to
files or GitHub projects, or copy/pasteable snippets, which you use in those
examples. If you're providing snippets in the issue, use
[Markdown code blocks](https://guides.github.com/features/mastering-markdown/#GitHub-flavored-markdown).
Do not paste large snippets completely unrelated to the issue.
* **Describe the behavior you observed after following the steps** and point
out what exactly is the problem with that behavior.
* **Explain which behavior you expected to see instead and why.**
* Do not hesitate to **include screenshots and animated GIFs** which show you
following the described steps and clearly demonstrate the problem.
You can use [this tool](http://www.cockos.com/licecap/) to record GIFs on macOS
and Windows, and [this tool](https://github.com/colinkeenan/silentcast) or
[this tool](https://github.com/GNOME/byzanz) on Linux.
### Template for submitting bug reports
[Short description of problem here]
**Reproduction steps:**
1. [First Step]
2. [Second Step]
3. [Other Steps...]
**Expected behavior:**
[Describe expected behavior here]
**Observed behavior:**
[Describe observed behavior here]
**Screenshots and GIFs**
![Screenshots and GIFs which follow reproduction steps to demonstrate the
problem](url)
**TableFilter version:** [Enter TableFilter version here]
**Browser and version:** [Enter Browser name and version here]
**OS and version:** [Enter OS name and version here]
**Additional information:**
* Problem started happening recently, didn't happen in an older version of
TableFilter: [Yes/No]
* Problem can be reliably reproduced, doesn't happen randomly: [Yes/No]
## Suggesting enhancements and features
Enhancement and feature suggestions are tracked as
[GitHub issues](https://guides.github.com/features/issues/).
Create an issue and provide the following information:
* **Use a clear and descriptive title** for the issue to identify the
suggestion.
* **Provide a step-by-step description of the suggested enhancement/feature**
in as many details as possible.
* When applicable **describe the current behavior** and
**explain which behavior you expected to see instead** and why.
* **Include screenshots and animated GIFs** which help you demonstrate the
steps or point out the part of TableFilter which the suggestion is related to.
You can use [this tool](http://www.cockos.com/licecap/) to record GIFs on macOS
and Windows, and [this tool](https://github.com/colinkeenan/silentcast) or
[this tool](https://github.com/GNOME/byzanz) on Linux.
* **Explain why this enhancement would be useful** to most TableFilter users.
### Template for submitting enhancement and feature suggestions
[Short description of suggestion]
**Steps which explain the enhancement/feature**
1. [First Step]
2. [Second Step]
3. [Other Steps...]
**Current and suggested behavior**
[Describe current and suggested behavior here]
**Why would the enhancement be useful to most users**
[Explain why the enhancement would be useful to most users]
**Screenshots and GIFs**
![Screenshots and GIFs which demonstrate the steps or part of TableFilter
the enhancement suggestion is related to](url)
**TableFilter Version:** [Enter TableFilter version here]
**Browser and version:** [Enter Browser name and version here]
**OS and version:** [Enter OS name and version here]
## Code contribution
TableFilter welcomes contributions from anyone and everyone. If you want to get
your hands dirty:
### Pull requests
In general, we follow the "fork-and-pull" Git workflow.
1. **Fork** the repo on GitHub
2. **Clone** the project to your own machine
3. **Commit** changes to your own branch
- 3.1. **Add** unit tests covering your change in the `test` folder
- 3.2. **Run** the tests with the `npm run eslint test` command
- 3.3. **Ensure** build is working with `npm run dist` command
4. **Push** your work back up to your fork
5. Submit a **Pull request** so that we can review your changes
NOTE: Be sure to merge the latest from "upstream" before making a pull request!
## Donations
To support this project simply locate the `Donate` button in the [project's
website](http://koalyptus.github.io/TableFilter/)
which points to PayPal.
Thanks to all those who supported this project in the past and to those who will
in the future!
-456
View File
@@ -1,456 +0,0 @@
module.exports = function (grunt) {
var webpackConfig = require('./webpack.config.js');
var webpackDevConfig = require('./webpack.dev.config.js');
var webpackTestConfig = require('./webpack.test.config.js');
var fs = require('fs');
var path = require('path');
var testDir = 'test';
var testHost = 'http://localhost:8000/';
var pkg = grunt.file.readJSON('package.json');
var repo = 'github.com/koalyptus/TableFilter';
var styleDirDist = 'dist/tablefilter/style/';
grunt.initConfig({
eslint: {
options: {
configFile: '.eslintrc'
},
target: [
'Gruntfile.js',
'webpack.config.js',
'webpack.dev.config.js',
'webpack.test.config.js',
'src/**/*.js',
'test/*.js'
]
},
qunit: {
options: {
'--web-security': 'no',
coverage: {
disposeCollector: true,
src: ['dist/tablefilter/*.js'],
instrumentedFiles: 'temp/',
htmlReport: 'report/coverage',
coberturaReport: 'report/',
lcovReport: 'report/',
jsonReport: 'report',
linesThresholdPct: 80
}
},
all: {
options: {
urls: getTestFiles(testDir, testHost)
}
},
only: {
options: {
urls: []
}
}
},
connect: {
server: {
options: {
port: 8000,
base: '.'
}
}
},
copy: {
dist: {
src: [
'**',
'!**/*.styl',
'!**/extensions/**',
'!**/mixins/**'
],
cwd: 'static/style',
dest: 'dist/tablefilter/style',
expand: true
},
templates: {
src: ['**'],
cwd: 'static/templates',
dest: 'demos',
expand: true
},
assets: {
src: ['**'],
cwd: 'static/demos-assets',
dest: 'demos/assets',
expand: true
},
starter: {
src: ['demos/starter.html'],
dest: 'dist/starter.html'
}
},
'string-replace': {
demos: {
files: { 'demos/': 'demos/*.html' },
options: {
replacements: [
{
pattern: /{NAME}/ig,
replacement: pkg.name
}, {
pattern: /{VERSION}/ig,
replacement: pkg.version
}, {
pattern: /{EZEDITTABLE_LINK}/ig,
replacement: '<a href="http://edittable.free.fr/' +
'zip.php?f=ezEditTable.zip&amp;p=1"' +
'target="_blank" title="ezEditTable is a ' +
'javascript code aimed at enhancing regular ' +
'HTML tables by adding features such as ' +
'inline editing components, advanced ' +
'selection and keyboard navigation ' +
'- Developed by ' + pkg.author.name + '">' +
'ezEditTable</a>'
}, {
pattern: /<!-- @import (.*?) -->/ig,
replacement: function (match, p1) {
return grunt.file.read('static/' + p1);
}
}
]
}
}
},
clean: ['demos/starter.html'],
'webpack-dev-server': {
options: {
webpack: webpackDevConfig,
publicPath: '/dist/'
},
start: {
webpack: {
devtool: 'eval'
}
}
},
webpack: {
build: webpackConfig,
dev: webpackDevConfig,
test: webpackTestConfig
},
watch: {
app: {
files: ['src/**/*', 'static/style/**/*'],
tasks: ['dev'],
options: {
spawn: false
}
},
templates: {
files: ['static/templates/**/*', 'static/partials/**/*'],
tasks: ['build-demos'],
options: {
spawn: false
}
}
},
babel: {
options: {
sourceMap: true,
modules: 'amd',
compact: false,
presets: ['es2015']
},
dist: {
files: [{
expand: true,
cwd: 'src',
src: ['**/*.js'],
dest: 'dist/tablefilter'
}]
}
},
esdoc: {
dist: {
options: {
source: 'src',
destination: 'docs/docs',
title: pkg.name + ' v' + pkg.version
}
}
},
stylus: {
compile: {
options: {
banner: '/** \n' +
' *\t ' + pkg.name + ' v' + pkg.version +
' by ' + pkg.author.name + ' \n' +
' *\t build date: ' + new Date().toISOString() + ' \n' +
' *\t MIT License \n' +
' */ \n'
},
files: [
{
src: ['static/style/*.styl'],
dest: styleDirDist + 'tablefilter.css'
}, {
src: ['static/style/extensions/colsVisibility.styl'],
dest: styleDirDist + 'colsVisibility.css'
}, {
src: ['static/style/extensions/filtersVisibility.styl'],
dest: styleDirDist + 'filtersVisibility.css'
}, {
src: ['static/style/themes/default/*.styl'],
dest: styleDirDist + 'themes/default/default.css'
}, {
src: ['static/style/themes/mytheme/*.styl'],
dest: styleDirDist + 'themes/mytheme/mytheme.css'
}, {
src: ['static/style/themes/skyblue/*.styl'],
dest: styleDirDist + 'themes/skyblue/skyblue.css'
}, {
src: ['static/style/themes/transparent/*.styl'],
dest:
styleDirDist + 'themes/transparent/transparent.css'
}
]
}
},
'gh-pages': {
options: {
branch: 'gh-pages',
add: true
},
'publish-lib': {
options: {
base: 'dist',
repo: 'https://' + repo,
message: 'publish TableFilter lib to gh-pages (cli)'
},
src: ['**/*']
},
'publish-readme': {
options: {
base: './',
repo: 'https://' + repo,
message: 'publish README and LICENSE to gh-pages (cli)'
},
src: ['README.md', 'LICENSE']
},
'publish-docs': {
options: {
base: 'docs',
repo: 'https://' + repo,
message: 'publish Docs to gh-pages (cli)'
},
src: ['**/*']
},
'deploy-lib': {
options: {
user: {
name: 'koalyptus'
},
base: 'dist',
repo: 'https://' + process.env.GH_TOKEN + '@' + repo,
message: 'publish TableFilter to gh-pages (auto)' +
getDeployMessage(),
silent: true
},
src: ['**/*']
},
'deploy-readme': {
options: {
user: {
name: 'koalyptus'
},
base: './',
repo: 'https://' + process.env.GH_TOKEN + '@' + repo,
message: 'publish README to gh-pages (auto)' +
getDeployMessage(),
silent: true
},
src: ['README.md', 'LICENSE']
},
'deploy-docs': {
options: {
user: {
name: 'koalyptus'
},
base: 'docs',
repo: 'https://' + process.env.GH_TOKEN + '@' + repo,
message: 'publish Docs to gh-pages (auto)' +
getDeployMessage(),
silent: true
},
src: ['**/*']
}
}
});
grunt.loadNpmTasks('grunt-eslint');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-string-replace');
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks('grunt-webpack');
grunt.loadNpmTasks('grunt-babel');
grunt.loadNpmTasks('grunt-esdoc');
grunt.loadNpmTasks('grunt-qunit-istanbul');
grunt.loadNpmTasks('grunt-contrib-stylus');
grunt.loadNpmTasks('grunt-gh-pages');
grunt.registerTask('default', ['test', 'build', 'build-demos']);
// Development server
grunt.registerTask('server', ['webpack-dev-server:start']);
// Dev dev/build/watch cycle
grunt.registerTask('dev',
['eslint', 'webpack:dev', 'copy:dist', 'stylus:compile', 'watch:app']);
// Production build
grunt.registerTask('build',
['eslint', 'webpack:build', 'copy:dist', 'stylus:compile']);
// Build demos
grunt.registerTask('dev-demos', ['build-demos', 'watch:templates']);
grunt.registerTask('build-demos', ['copy:templates', 'copy:assets',
'string-replace:demos', 'copy:starter', 'clean']);
// Build tests
grunt.registerTask('build-test',
['eslint', 'webpack:test', 'copy:dist', 'stylus:compile']);
// Transpile with Babel
grunt.registerTask('dev-modules', ['babel', 'copy:dist']);
// Tests with coverage
grunt.registerTask('test', ['build-test', 'connect', 'qunit:all']);
// Publish to gh-pages
grunt.registerTask('publish', 'Publish from CLI', [
'build', 'build-demos', 'esdoc', 'gh-pages:publish-lib',
'gh-pages:publish-readme', 'gh-pages:publish-docs'
]);
// Deploy to gh-pages
grunt.registerTask('deploy', 'Publish from Travis', [
'build', 'esdoc', 'check-deploy'
]);
// Custom task running QUnit tests for specified files.
// Usage example: grunt test-only:test.html,test-help.html
grunt.registerTask('test-only',
'A task that runs only specified tests.',
function (tests) {
if (!tests) {
return;
}
tests = tests.split(',');
var res = [];
tests.forEach(function (itm) {
var filePath = path.resolve(testDir, itm);
var parts = filePath.split(path.sep);
res.push(buildTestUrl(testHost, testDir, parts));
});
grunt.task.run('connect');
grunt.config('qunit.only.options.urls', res);
grunt.task.run('qunit:only');
}
);
function isTestFile(pth) {
var allowedExts = ['.html', '.htm'];
for (var i = 0, len = allowedExts.length; i < len; i++) {
var ext = allowedExts[i];
if (pth.indexOf(ext) !== -1) {
return true;
}
}
return false;
}
function buildTestUrl(host, testDir, parts) {
var idx = parts.indexOf(testDir);
var fileIdx = parts.length;
var relParts = parts.slice(idx, fileIdx);
return host.concat(relParts.join('/'));
}
// Returns the list of test files from the test folder for qunit task
function getTestFiles(testDir, host) {
var getFiles = function (dir, host) {
var res = [];
var items = fs.readdirSync(dir);
items.forEach(function (itm) {
var fileOrDir = path.resolve(dir, itm);
if (isTestFile(fileOrDir)) {
var parts = fileOrDir.split(path.sep);
res.push(buildTestUrl(host, testDir, parts));
} else {
if (fs.lstatSync(fileOrDir).isDirectory()) {
res = res.concat(getFiles(fileOrDir, host));
}
}
});
return res;
};
return getFiles(testDir, host);
}
grunt.registerTask('check-deploy', function () {
var env = process.env;
// need this
this.requires(['build', 'esdoc']);
// only deploy under these conditions
if (env.TRAVIS === 'true' &&
env.TRAVIS_SECURE_ENV_VARS === 'true' &&
env.TRAVIS_PULL_REQUEST === 'false') {
grunt.log.writeln('executing deployment');
// queue deploy
grunt.task.run([
'gh-pages:deploy-lib',
'gh-pages:deploy-readme',
'gh-pages:deploy-docs'
]);
} else {
grunt.log.writeln('skipped deployment');
}
});
// Get a formatted commit message to review changes from the commit log
// github will turn some of these into clickable links
function getDeployMessage() {
var ret = '\n\n';
var env = process.env;
if (env.TRAVIS !== 'true') {
ret += 'missing env vars for travis-ci';
return ret;
}
ret += 'branch: ' + env.TRAVIS_BRANCH + '\n';
ret += 'SHA: ' + env.TRAVIS_COMMIT + '\n';
ret += 'range SHA: ' + env.TRAVIS_COMMIT_RANGE + '\n';
ret += 'build id: ' + env.TRAVIS_BUILD_ID + '\n';
ret += 'build number: ' + env.TRAVIS_BUILD_NUMBER + '\n';
return ret;
}
};
-22
View File
@@ -1,22 +0,0 @@
The MIT License (MIT)
Copyright (c) 2015 Max Guglielmi
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.
-173
View File
@@ -1,173 +0,0 @@
[![Build Status](https://api.travis-ci.org/koalyptus/TableFilter.svg?branch=master)](https://travis-ci.org/koalyptus/TableFilter)
[![Document](https://koalyptus.github.io/TableFilter/docs/badge.svg)](https://koalyptus.github.io/TableFilter/docs/source.html)
[![codecov](https://codecov.io/gh/koalyptus/TableFilter/branch/master/graph/badge.svg)](https://codecov.io/gh/koalyptus/TableFilter)
[![Greenkeeper badge](https://badges.greenkeeper.io/koalyptus/TableFilter.svg)](https://greenkeeper.io/)
# TableFilter
> A Javascript library making HTML tables filterable
TableFilter is a modernised version of the [HTML Table Filter generator](http://tablefilter.free.fr) javascript plugin.
This library adds to any html table a "filter by column" feature that enables
users to filter and limit the data displayed within a long table. By default, the script automatically adds a filter grid bar at the top of the desired table.
## Features
* Convert a regular HTML table into an advanced grid component providing:
* Advanced columns filtering model
* Sorting and pagination capabilities
* Complete selection model ([ezEditTable](http://codecanyon.net/item/ezedittable-enhance-html-tables/2425123?ref=koalyptus) extension)
* Extended keyboard navigation ([ezEditTable](http://codecanyon.net/item/ezedittable-enhance-html-tables/2425123?ref=koalyptus) extension)
* Inline cell or row editing ([ezEditTable](http://codecanyon.net/item/ezedittable-enhance-html-tables/2425123?ref=koalyptus) extension)
* Row insertion or deleting ([ezEditTable](http://codecanyon.net/item/ezedittable-enhance-html-tables/2425123?ref=koalyptus) extension)
* And even more features...
* Attach to an existing HTML table
* Integration with any server-side technology as this is a pure client-side
solution
* Exhaustive documentation and poweful API
## Getting started
* Clone the repo using Git:
```shell
git clone https://github.com/koalyptus/TableFilter.git
```
* You can [download](https://github.com/koalyptus/TableFilter/archive/master.zip) this repository.
* TableFilter is available on [npm repository](https://www.npmjs.com/package/tablefilter), you can install it from the command line using the following command:
```shell
npm install tablefilter --save-dev
```
* or get the future features from the ``next`` release channel:
```shell
npm install tablefilter@next --save-dev
```
* Alternatively you can also [access these files from unpkg CDN](https://unpkg.com/tablefilter/), download them or point your package manager to them.
## Setup
### Using modules
Require `TableFilter`:
```javascript
// ES2015 modules
import TableFilter from 'tablefilter';
// CommonJS or AMD modules
var TableFilter = require('tablefilter');
```
### Using distribution scripts
If you are not using a module system, you can reference the distribution scripts directly in your html pages:
```html
<script src="path_to/node_modules/tablefilter/dist/tablefilter/tablefilter.js"></script>
```
### Placing manually the distribution scripts in your project
Copy the ``tablefilter`` directory under ``dist`` and place it at desired location in your project. Then include the main js file in your page:
```shell
<script src="path/to/my/scripts/tablefilter/tablefilter.js"></script>
```
### Usage
Place the following snippet just under the HTML table and always define a ``base_path`` property in the configuration object to reflect the path to the script
```shell
<script>
var tf = new TableFilter(document.querySelector('.my-table'), {
base_path: 'path/to/my/scripts/tablefilter/'
});
tf.init();
</script>
```
If the ``base_path`` property is not specified, it will default to ``/tablefilter`` directory:
```shell
your-page.html
|— tablefilter
```
## Development
This project requires node.js and Grunt to be installed:
- install [node.js](https://nodejs.org/)
- install [Grunt](http://gruntjs.com/getting-started) from the command line using npm (comes with node.js):
```shell
npm install -g grunt-cli
```
Once ``Grunt`` is sorted out you can follow the instructions below.
Start by installing any dependencies.
```shell
npm install
```
Use
```shell
npm run dev
```
command to launch a build / watch cycle and start the local
sever on port ``8080``.
Use
```shell
npm run build
```
command to generate a production build.
The
```shell
npm run dist
```
command will create a production build, run the tests and finally generate
the demos:
To run all the tests and generate the coverage report:
```shell
npm test
```
or to run specific test(s):
```shell
grunt test-only:test.html
grunt test-only:test.html,test-sort.html
```
to view the coverage report(s), open the `index.html` under the
`report/coverage` folder or
[online](https://codecov.io/gh/koalyptus/TableFilter).
## Demos
Check out the online [examples](http://koalyptus.github.io/TableFilter/examples)
or generate the demos locally:
```shell
npm run build:demos
```
then run the local webserver:
```shell
npm start
```
then pick a demo from:
```shell
http://localhost:8080/demos/
```
## Documentation
Find exhaustive documentation on the configuration options in the [WIKI](https://github.com/koalyptus/TableFilter/wiki) section.
Autogenerated documentation of the ES6 modules is available on the website: [docs](http://koalyptus.github.io/TableFilter/docs)
If you previously used the HTML Table Filter Generator plugin, verify the configuration
options you are using are still supported: [Obsolete](https://github.com/koalyptus/TableFilter/wiki/Obsolete)
Run this task to generate the documentation in the ``docs/docs`` directory:
```shell
npm run esdoc
```
## Support
* GitHub for [reporting bugs](https://github.com/koalyptus/TableFilter/blob/master/CONTRIBUTING.md#reporting-bugs) and [feature requests](https://github.com/koalyptus/TableFilter/blob/master/CONTRIBUTING.md#suggesting-enhancements-and-features).
## License
[MIT](LICENSE)
-514
View File
@@ -1,514 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>tablefilter v0.5.41 - Starter</title>
</head>
<body>
<h1>tablefilter v0.5.41</h1>
<table id="demo">
<thead>
<tr>
<th>country</th>
<th>iso</th>
<th>year</th>
<th>POP</th>
<th>XRAT</th>
<th>PPP</th>
<th>cgdp</th>
<th>cc</th>
<th>ci</th>
</tr>
</thead>
<tbody>
<tr>
<td>Benin</td>
<td>BEN</td>
<td>1998</td>
<td>5950.33</td>
<td>589.9517822</td>
<td>190.95</td>
<td>1178.46</td>
<td>90.98</td>
<td>7.55</td>
</tr>
<tr>
<td>Benin</td>
<td>BEN</td>
<td>1999</td>
<td>6109.53</td>
<td>615.6990967</td>
<td>200.19</td>
<td>1174.90</td>
<td>92.61</td>
<td>7.86</td>
</tr>
<tr>
<td>Benin</td>
<td>BEN</td>
<td>2000</td>
<td>6272.00</td>
<td>711.9763184</td>
<td>200.61</td>
<td>1224.74</td>
<td>92.27</td>
<td>8.25</td>
</tr>
<tr>
<td>Burkina Faso</td>
<td>BFA</td>
<td>1994</td>
<td>9755.03</td>
<td>555.2047119</td>
<td>125.76</td>
<td>838.76</td>
<td>79.81</td>
<td>6.57</td>
</tr>
<tr>
<td>Burkina Faso</td>
<td>BFA</td>
<td>1995</td>
<td>9988.00</td>
<td>499.148407</td>
<td>136.65</td>
<td>860.89</td>
<td>80.41</td>
<td>9.29</td>
</tr>
<tr>
<td>Burkina Faso</td>
<td>BFA</td>
<td>1996</td>
<td>10225.00</td>
<td>511.5523987</td>
<td>144.18</td>
<td>881.11</td>
<td>80.40</td>
<td>12.12</td>
</tr>
<tr>
<td>Burkina Faso</td>
<td>BFA</td>
<td>1997</td>
<td>10473.53</td>
<td>583.6693726</td>
<td>141.78</td>
<td>899.01</td>
<td>76.94</td>
<td>14.94</td>
</tr>
<tr>
<td>Russia</td>
<td>RUS</td>
<td>1998</td>
<td>146899.01</td>
<td>9.705082893</td>
<td>2.64</td>
<td>7086.39</td>
<td>67.73</td>
<td>9.20</td>
</tr>
<tr>
<td>Russia</td>
<td>RUS</td>
<td>1999</td>
<td>146308.99</td>
<td>24.6199398</td>
<td>4.03</td>
<td>8074.70</td>
<td>57.35</td>
<td>8.81</td>
</tr>
<tr>
<td>Russia</td>
<td>RUS</td>
<td>2000</td>
<td>145555.01</td>
<td>28.12916946</td>
<td>4.85</td>
<td>9995.91</td>
<td>54.74</td>
<td>8.79</td>
</tr>
<tr>
<td>Rwanda</td>
<td>RWA</td>
<td>1994</td>
<td>6230.00</td>
<td>194.517</td>
<td>50.39</td>
<td>529.48</td>
<td>132.16</td>
<td>4.48</td>
</tr>
<tr>
<td>Rwanda</td>
<td>RWA</td>
<td>1995</td>
<td>6400.00</td>
<td>262.1975098</td>
<td>70.67</td>
<td>746.34</td>
<td>92.77</td>
<td>3.08</td>
</tr>
<tr>
<td>Rwanda</td>
<td>RWA</td>
<td>1996</td>
<td>6727.00</td>
<td>306.8200073</td>
<td>77.90</td>
<td>823.25</td>
<td>89.71</td>
<td>3.21</td>
</tr>
<tr>
<td>Rwanda</td>
<td>RWA</td>
<td>1997</td>
<td>7895.18</td>
<td>301.5297852</td>
<td>84.13</td>
<td>847.79</td>
<td>91.44</td>
<td>3.72</td>
</tr>
<tr>
<td>Rwanda</td>
<td>RWA</td>
<td>1998</td>
<td>8105.00</td>
<td>312.3140869</td>
<td>85.32</td>
<td>914.22</td>
<td>89.34</td>
<td>4.22</td>
</tr>
<tr>
<td>El Salvador</td>
<td>SLV</td>
<td>1995</td>
<td>5669.00</td>
<td>8.754583359</td>
<td>3.54</td>
<td>4143.59</td>
<td>89.31</td>
<td>10.35</td>
</tr>
<tr>
<td>El Salvador</td>
<td>SLV</td>
<td>1996</td>
<td>5798.00</td>
<td>8.755000114</td>
<td>3.69</td>
<td>4235.79</td>
<td>88.77</td>
<td>7.79</td>
</tr>
<tr>
<td>El Salvador</td>
<td>SLV</td>
<td>1997</td>
<td>5911.00</td>
<td>8.756250381</td>
<td>3.74</td>
<td>4408.41</td>
<td>87.05</td>
<td>7.90</td>
</tr>
<tr>
<td>Slovak Republic</td>
<td>SVK</td>
<td>2000</td>
<td>5401.00</td>
<td>46.0352</td>
<td>13.02</td>
<td>12618.53</td>
<td>52.22</td>
<td>24.78</td>
</tr>
<tr>
<td>Slovenia</td>
<td>SVN</td>
<td>1994</td>
<td>1988.90</td>
<td>128.8085938</td>
<td>79.97</td>
<td>11662.12</td>
<td>53.21</td>
<td>19.87</td>
</tr>
<tr>
<td>Slovenia</td>
<td>SVN</td>
<td>1995</td>
<td>1990.00</td>
<td>118.5185013</td>
<td>89.03</td>
<td>12574.96</td>
<td>54.46</td>
<td>23.11</td>
</tr>
<tr>
<td>Slovenia</td>
<td>SVN</td>
<td>1996</td>
<td>1991.00</td>
<td>135.3643036</td>
<td>97.27</td>
<td>13151.51</td>
<td>54.32</td>
<td>23.23</td>
</tr>
<tr>
<td>Slovenia</td>
<td>SVN</td>
<td>1997</td>
<td>1985.96</td>
<td>159.6882935</td>
<td>103.96</td>
<td>14143.02</td>
<td>53.24</td>
<td>24.14</td>
</tr>
<tr>
<td>Slovenia</td>
<td>SVN</td>
<td>1998</td>
<td>1982.60</td>
<td>166.134201</td>
<td>108.93</td>
<td>15067.34</td>
<td>52.82</td>
<td>25.35</td>
</tr>
<tr>
<td>Zambia</td>
<td>ZMB</td>
<td>1994</td>
<td>8740.72</td>
<td>669.3706055</td>
<td>307.48</td>
<td>834.19</td>
<td>75.43</td>
<td>9.86</td>
</tr>
<tr>
<td>Zambia</td>
<td>ZMB</td>
<td>1995</td>
<td>8980.00</td>
<td>864.1192017</td>
<td>402.40</td>
<td>829.37</td>
<td>76.55</td>
<td>10.31</td>
</tr>
<tr>
<td>Zambia</td>
<td>ZMB</td>
<td>1996</td>
<td>9214.40</td>
<td>1207.900024</td>
<td>515.01</td>
<td>838.05</td>
<td>78.74</td>
<td>10.99</td>
</tr>
<tr>
<td>Zambia</td>
<td>ZMB</td>
<td>1997</td>
<td>9443.21</td>
<td>1314.498047</td>
<td>596.97</td>
<td>851.47</td>
<td>77.59</td>
<td>12.15</td>
</tr>
<tr>
<td>Zambia</td>
<td>ZMB</td>
<td>1998</td>
<td>9665.71</td>
<td>1862.06897</td>
<td>744.91</td>
<td>800.69</td>
<td>85.12</td>
<td>13.75</td>
</tr>
<tr>
<td>Zambia</td>
<td>ZMB</td>
<td>1999</td>
<td>9881.21</td>
<td>2388.019043</td>
<td>941.87</td>
<td>765.24</td>
<td>91.82</td>
<td>15.30</td>
</tr>
<tr>
<td>Zambia</td>
<td>ZMB</td>
<td>2000</td>
<td>10089.00</td>
<td>3110.843994</td>
<td>1157.63</td>
<td>840.97</td>
<td>86.33</td>
<td>15.38</td>
</tr>
<tr>
<td>Zimbabwe</td>
<td>ZWE</td>
<td>1994</td>
<td>10775.35</td>
<td>8.151538849</td>
<td>2.01</td>
<td>2586.84</td>
<td>60.10</td>
<td>18.53</td>
</tr>
<tr>
<td>Zimbabwe</td>
<td>ZWE</td>
<td>1995</td>
<td>11011.00</td>
<td>8.66537571</td>
<td>2.15</td>
<td>2603.37</td>
<td>66.89</td>
<td>15.03</td>
</tr>
<tr>
<td>Zimbabwe</td>
<td>ZWE</td>
<td>1996</td>
<td>11242.16</td>
<td>10.00234985</td>
<td>2.64</td>
<td>2860.23</td>
<td>72.06</td>
<td>11.32</td>
</tr>
<tr>
<td>Zimbabwe</td>
<td>ZWE</td>
<td>1997</td>
<td>11923.52</td>
<td>12.11128998</td>
<td>3.17</td>
<td>2727.06</td>
<td>82.82</td>
<td>11.01</td>
</tr>
<tr>
<td>Zimbabwe</td>
<td>ZWE</td>
<td>1998</td>
<td>12153.85</td>
<td>23.67910957</td>
<td>4.06</td>
<td>2799.85</td>
<td>77.66</td>
<td>10.75</td>
</tr>
<tr>
<td>Zimbabwe</td>
<td>ZWE</td>
<td>1999</td>
<td>12388.32</td>
<td>38.30120087</td>
<td>6.12</td>
<td>2770.48</td>
<td>76.89</td>
<td>10.73</td>
</tr>
<tr>
<td>Zimbabwe</td>
<td>ZWE</td>
<td>2000</td>
<td>12627.00</td>
<td>44.41791916</td>
<td>9.48</td>
<td>2607.03</td>
<td>69.23</td>
<td>8.62</td>
</tr>
</tbody>
</table>
<script src="tablefilter/tablefilter.js"></script>
<script data-config>
var filtersConfig = {
base_path: 'tablefilter/',
col_1: 'select',
col_2: 'select',
col_3: 'select',
alternate_rows: true,
rows_counter: true,
btn_reset: true,
loader: true,
status_bar: true,
mark_active_columns: true,
highlight_keywords: true,
col_types: [
'string', 'string', 'number',
'number', 'number', 'number',
'number', 'number', 'number'
],
custom_options: {
cols:[3],
texts: [[
'0 - 25 000',
'100 000 - 1 500 000'
]],
values: [[
'>0 && <=25000',
'>100000 && <=1500000'
]],
sorts: [false]
},
col_widths: [
'150px', '100px', '100px',
'70px', '70px', '70px',
'70px', '60px', '60px'
],
extensions:[{ name: 'sort' }]
};
var tf = new TableFilter('demo', filtersConfig);
tf.init();
</script>
<pre></pre>
<!--
DO NOT COPY: NOT PART OF TABLEFILTER
-->
<script>
var configs = document.querySelectorAll('script[data-config]');
var pre = document.body.getElementsByTagName('pre')[0];
[].forEach.call(configs, function(config) {
if(pre){
pre.innerHTML +=
config.innerHTML.replace('<', '&lt;').replace('>', '&gt;');
}
});
</script>
<!-- -->
</body>
</html>
@@ -1,6 +0,0 @@
/**
* tablefilter v0.5.41 by Max Guglielmi
* build date: 2017-09-08T07:54:00.964Z
* MIT License
*/
span.colVisSpan{text-align:left;}span.colVisSpan a.colVis{display:inline-block;padding:7px 5px 0;font-size:inherit;font-weight:inherit;vertical-align:top}div.colVisCont{position:relative;background:#fff;-webkit-box-shadow:3px 3px 2px #888;-moz-box-shadow:3px 3px 2px #888;box-shadow:3px 3px 2px #888;position:absolute;display:none;border:1px solid #ccc;height:auto;width:250px;background-color:#fff;margin:35px 0 0 -100px;z-index:10000;padding:10px 10px 10px 10px;text-align:left;font-size:inherit;}div.colVisCont:after,div.colVisCont:before{bottom:100%;left:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}div.colVisCont:after{border-color:rgba(255,255,255,0);border-bottom-color:#fff;border-width:10px;margin-left:-10px}div.colVisCont:before{border-color:rgba(255,255,255,0);border-bottom-color:#ccc;border-width:12px;margin-left:-12px}div.colVisCont p{margin:6px auto 6px auto}div.colVisCont a.colVis{display:initial;font-weight:inherit}ul.cols_checklist{padding:0;margin:0;list-style-type:none;}ul.cols_checklist label{display:block}ul.cols_checklist input{vertical-align:middle;margin:2px 5px 2px 1px}li.cols_checklist_item{padding:4px;margin:0;}li.cols_checklist_item:hover{background-color:#335ea8;color:#fff}.cols_checklist_slc_item{background-color:#335ea8;color:#fff}
@@ -1,6 +0,0 @@
/**
* tablefilter v0.5.41 by Max Guglielmi
* build date: 2017-09-08T07:54:00.964Z
* MIT License
*/
span.expClpFlt a.btnExpClpFlt{width:35px;height:35px;display:inline-block;}span.expClpFlt a.btnExpClpFlt:hover{background-color:#f4f4f4}span.expClpFlt img{padding:8px 11px 11px 11px}
@@ -1,23 +0,0 @@
/**
* tablefilter v0.5.41 by Max Guglielmi
* build date: 2017-09-08T07:54:00.964Z
* MIT License
*/
.activeHeader{background-color:#66afe9 !important;color:#fff !important}
.even{background-color:#fff}.odd{background-color:#f9f9f9}
.ezActiveRow{background-color:#2852a8 !important;color:#fff}.ezSelectedRow{background-color:#316ac5 !important;color:#fff}.ezActiveCell{background-color:#d9e8fb !important;color:#000 !important;font-weight:bold}.ezETSelectedCell{background-color:#ffdc61 !important;font-weight:bold;color:#000 !important}.ezUnselectable{-moz-user-select:-moz-none;-khtml-user-select:none;-webkit-user-select:none;-o-user-select:none;user-select:none}.ezInputEditor{width:95%;height:auto;font-size:inherit;border:1px solid #aaccf6}.ezTextareaEditor{width:95%;height:35px;font-size:inherit;border:1px solid #aaccf6}.ezSelectEditor{width:100%;font-size:inherit;border:1px solid #aaccf6}.ezModifiedCell{background:transparent url("themes/bg_mod_cell.png") 0 0 no-repeat}select[multiple="multiple"].ezSelectEditor{height:35px}.ezCommandEditor{margin:2px;}.ezCommandEditor button,.ezCommandEditor input[type="button"]{min-height:22px;margin:1px;padding:3px;border:1px solid #ccc;background:#fff;border-radius:4px 4px 4px 4px;-moz-border-radius:4px 4px 4px 4px;}.ezCommandEditor button:hover,.ezCommandEditor input[type="button"]:hover{border:1px solid #999}.ezCommandEditor img{border:0;vertical-align:middle;margin:2px}.ezOpacity{opacity:.6}.alignLeft{text-align:left}.alignCenter{text-align:center}.alignRight{text-align:right}
.div_checklist{width:100%;height:90px;line-height:24px;border:1px solid #f4f4f4;overflow:auto;text-align:left;background-color:#fff;color:#444;}.div_checklist ul.flt_checklist{padding:0 !important;margin:0 !important;list-style:none !important}.div_checklist li.flt_checklist_item{padding:1px !important;margin:0 !important;font-size:10px !important;border-bottom:1px solid #f4f4f4 !important;}.div_checklist li.flt_checklist_item:hover{background-color:#335ea8 !important;color:#fff !important}.div_checklist label{display:block !important}.div_checklist input{vertical-align:middle !important;margin:2px 5px 2px 1px !important}.flt_checklist_item_disabled{background-color:#e5e5e5}.flt_checklist_slc_item{background-color:#335ea8 !important;color:#fff !important}
.fltrow{height:1em;background-color:#eaeaea;}.fltrow td{border-bottom:1px solid #ccc !important;border-top:1px solid #f4f4f4;border-left:1px solid #ccc;border-right:1px solid #f4f4f4;padding:.2em !important;}.fltrow td:last-child{border-right:1px solid #ccc}.btnflt{height:35px;font-family:inherit;font-size:inherit;vertical-align:middle;margin:0 2px 0 2px;padding:0 1px 0 1px}.btnflt_icon{font-family:inherit;font-size:inherit;width:35px;height:35px;cursor:pointer !important;border:0 !important;vertical-align:middle;background:transparent url("themes/btn_filter.png") center center no-repeat !important}.flt,.flt_s,.single_flt{font-family:inherit;display:block;color:#444;background-color:#fff;border:1px inset #f4f4f4;margin:0;padding:0 0 0 .2em;width:100%;height:35px;vertical-align:middle;border-radius:2px;box-sizing:border-box;}.flt:focus,.flt_s:focus,.single_flt:focus{border-color:#66afe9;outline:0 none;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.075) inset,0 0 8px rgba(102,175,233,0.6);-moz-box-shadow:0 1px 1px rgba(0,0,0,0.075) inset,0 0 8px rgba(102,175,233,0.6);box-shadow:0 1px 1px rgba(0,0,0,0.075) inset,0 0 8px rgba(102,175,233,0.6)}select.flt_multi{font-family:inherit;color:#444;background-color:#fff;border:1px solid #f4f4f4;margin:0;padding:.2em;width:100%;height:90px;vertical-align:middle;box-sizing:border-box;}select.flt_multi option{padding-top:5px;padding-bottom:5px}.flt_s{width:60%;box-sizing:initial;display:initial}.single_flt{width:70%;box-sizing:initial;display:initial}div.popUpFilter{position:relative;background:#fff;-webkit-box-shadow:3px 3px 2px #888;-moz-box-shadow:3px 3px 2px #888;box-shadow:3px 3px 2px #888;margin:30px auto 0 0;position:absolute;display:none;width:100px;background-color:#eaeaea;border:1px solid #eaeaea;padding:0}div.popUpFilter:after,div.popUpFilter:before{bottom:100%;left:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}div.popUpFilter:after{border-color:rgba(255,255,255,0);border-bottom-color:#fff;border-width:10px;margin-left:-10px}div.popUpFilter:before{border-color:rgba(255,255,255,0);border-bottom-color:#eaeaea;border-width:12px;margin-left:-12px}.popUpPlaceholder{position:relative}
div.grd_Cont{-webkit-box-shadow:4px 4px 10px 0 rgba(50,50,50,0.75);-moz-box-shadow:4px 4px 10px 0 rgba(50,50,50,0.75);box-shadow:4px 4px 10px 0 rgba(50,50,50,0.75);width:800px;height:auto;overflow:hidden;background-color:#c8e0fb;border:1px solid #99bbe8;}div.grd_Cont .fltrow{background-color:transparent}div.grd_Cont .flt{border:1px solid #99bbe8;width:100%;}div.grd_Cont .flt :focus{border:1px solid #558dd9}div.grd_Cont .even{background-color:#fff}div.grd_Cont .odd{background-color:#dfe8f6}div.grd_Cont .no-results{background-color:transparent}div.grd_Cont .sort-arrow{position:initial}div.grd_tblCont{height:400px;width:800px;background:#fff;overflow-x:auto;overflow-y:scroll}div.grd_headTblCont{display:block;margin-right:20px;height:auto;overflow:hidden;border-bottom:1px solid #99bbe8;background-color:#c8e0fb}div.grd_tblCont table,div.grd_headTblCont table{border-collapse:collapse;table-layout:fixed;box-sizing:initial}div.grd_tblCont table th,div.grd_headTblCont table th,div.grd_headTblCont table td{height:35px;background-color:#c8e0fb;padding:.1em .5em;color:#333;border-right:1px solid #99bbe8 !important;overflow:hidden;text-overflow:ellipsis}div.grd_headTblCont table td{padding:.2em .2em}div.grd_tblCont table td{padding:.5em .7em;border-bottom:1px solid #99bbe8;overflow:hidden;text-overflow:ellipsis}.grd_inf{clear:both;width:auto;height:35px;background-color:#c8e0fb;margin:0;padding:1px 3px 1px 3px;border-top:1px solid #99bbe8;}.grd_inf a{color:#333;text-decoration:none;font-weight:bold;}.grd_inf a:hover{text-decoration:underline;background-color:transparent}.grd_inf input.reset:hover{background-color:transparent}.grd_inf .mdiv{width:40% !important}.grd_inf .ldiv div{border:0}.grd_inf .helpBtn{border:0 !important}.grd_inf div.status{position:absolute;float:none !important;height:auto !important;margin:19px 0 !important;font-size:12px;color:#333;border:0 !important}.grd_inf div.tot{border:0 !important}
.helpBtn{display:inline-block;height:27px;margin:0;padding:8px 15px 0 15px;vertical-align:top;}.helpBtn:hover{background-color:#f4f4f4}div.helpCont{position:relative;background:#fff;-webkit-box-shadow:3px 3px 2px #888;-moz-box-shadow:3px 3px 2px #888;box-shadow:3px 3px 2px #888;position:absolute;display:none;width:300px;padding:10px;margin:45px 0 0 -150px;border:1px solid #ccc;line-height:20px;font-size:inherit;color:#333;background:#fff;text-align:left;}div.helpCont:after,div.helpCont:before{bottom:100%;left:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}div.helpCont:after{border-color:rgba(255,255,255,0);border-bottom-color:#fff;border-width:10px;margin-left:-10px}div.helpCont:before{border-color:rgba(255,255,255,0);border-bottom-color:#ccc;border-width:12px;margin-left:-12px}div.helpCont a{color:#c00;text-decoration:underline;font-weight:normal}div.helpCont a.close{color:#333 !important;text-decoration:none !important;font-weight:bold;}div.helpCont a.close:hover{text-decoration:none}div.helpCont hr{border:1px solid #ccc}div.helpFooter{margin:10px 0 0 0;}div.helpFooter h4{margin:2px 2px 2px 2px;color:#333}
span.keyword{font-weight:700;font-style:italic;border-bottom:1px dotted #ccc}
.loader{position:absolute;padding:.5em .7em;margin:10em 0 0 3em;width:auto;z-index:1000;font-weight:600;background-color:#a7a7a8;vertical-align:middle;border-radius:10px;color:#fff;text-shadow:1px 1px #333}
.no-results{display:none;color:#333;margin:0;padding:1em 0;text-align:center;max-height:5em;background-color:#f4f4f4}
select.pgSlc{height:35px;margin:0;border:1px solid #f4f4f4;background-color:#fff;vertical-align:middle}select.pgSlc:focus{border-color:#66afe9;outline:0 none;box-shadow:0 1px 1px rgba(0,0,0,0.075) inset,0 0 8px rgba(102,175,233,0.6)}input.pgNbInp{height:35px;margin:0;border:1px solid #f4f4f4;background-color:#fff;width:35px}input.pgNbInp:focus{border-color:#66afe9;outline:0 none;box-shadow:0 1px 1px rgba(0,0,0,0.075) inset,0 0 8px rgba(102,175,233,0.6)}input.pgInp,.nextPage,.previousPage,.firstPage,.lastPage{height:35px;margin:0;border:1px solid #f4f4f4;background-color:#fff;vertical-align:middle;width:35px;border:0;font-weight:bold}input.pgInp:focus,.nextPage:focus,.previousPage:focus,.firstPage:focus,.lastPage:focus{border-color:#66afe9;outline:0 none;box-shadow:0 1px 1px rgba(0,0,0,0.075) inset,0 0 8px rgba(102,175,233,0.6)}.nextPage{background:transparent url("themes/btn_next_page.gif") center center no-repeat !important;}.nextPage:hover{background-color:#f4f4f4 !important}.previousPage{background:transparent url("themes/btn_previous_page.gif") center center no-repeat !important;}.previousPage:hover{background-color:#f4f4f4 !important}.firstPage{background:transparent url("themes/btn_first_page.gif") center center no-repeat !important;}.firstPage:hover{background-color:#f4f4f4 !important}.lastPage{background:transparent url("themes/btn_last_page.gif") center center no-repeat !important;}.lastPage:hover{background-color:#f4f4f4 !important}span.nbpg{padding:0 5px}select.rspg{height:35px;margin:0;border:1px solid #f4f4f4;background-color:#fff;margin:0 0 0 5px;vertical-align:middle}select.rspg:focus{border-color:#66afe9;outline:0 none;box-shadow:0 1px 1px rgba(0,0,0,0.075) inset,0 0 8px rgba(102,175,233,0.6)}span.rspgSpan{font-size:inherit}
input.reset{display:inline-block;width:35px;height:35px;border:0;background:transparent url("themes/btn_clear_filters.png") center center no-repeat;vertical-align:top;}input.reset:hover{background-color:#f4f4f4}
div.tot{float:left;overflow:hidden;min-width:150px;height:100%;margin:0;padding:.5em;vertical-align:middle;}div.tot span{font-weight:500}
.sort-arrow{position:absolute;display:none;width:11px;height:11px;margin:0;background-position:center center;background-repeat:no-repeat}.descending{display:inline;background-image:url("themes/downsimple.png")}.ascending{display:inline;background-image:url("themes/upsimple.png")}
div.status{float:left;overflow:hidden;min-width:120px;height:100%;margin:0;padding:.5em;}div.status span{font-size:inherit}
table.TF{font-family:inherit;border-spacing:0;border:0;}table.TF th{height:35px;margin:0;background-color:#eaeaea;border-bottom:1px solid #ccc;border-top:1px solid #f4f4f4;border-left:1px solid #ccc;border-right:1px solid #f4f4f4;padding:.1em .7em;color:#333;}table.TF th:last-child{border-right:1px solid #ccc}table.TF td{margin:0;padding:.5em .7em;border-bottom:1px solid #c6c6c6}table.TF.resp{display:block;overflow-x:auto;}table.TF.resp .sort-arrow{position:initial}
.inf{clear:both;width:auto;height:35px;background-color:#fff;font-size:inherit;margin:0;padding:0;border-top:1px solid #ccc;border-bottom:1px solid #ccc;border-left:1px solid #ccc;border-right:1px solid #ccc;overflow:hidden;border-top-left-radius:3px;border-top-right-radius:3px;}.inf a{color:#333;text-decoration:none;font-weight:bold;box-sizing:initial;}.inf a:hover{text-decoration:underline}.ldiv{float:left;width:30%;position:inherit;text-align:left}.mdiv{float:left;width:38%;position:inherit;text-align:center;padding:0}.rdiv{float:right;width:30%;position:inherit;text-align:right}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 360 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 325 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 B

@@ -1,6 +0,0 @@
/**
* tablefilter v0.5.41 by Max Guglielmi
* build date: 2017-09-08T07:54:00.964Z
* MIT License
*/
table.TF{border-left:1px solid #ccc;border-top:none;border-right:none;border-bottom:none;}table.TF th{background:#ebecee url("images/bg_th.jpg") left top repeat-x;border-bottom:1px solid #d0d0d0;border-right:1px solid #d0d0d0;border-left:1px solid #fff;border-top:1px solid #fff;color:#333}table.TF td{border-bottom:1px dotted #999;padding:5px}.fltrow{background-color:#ebecee !important;}.fltrow th,.fltrow td{border-bottom:1px dotted #666 !important;padding:1px 3px 1px 3px !important}.flt,select.flt,select.flt_multi,.flt_s,.single_flt,.div_checklist{border:1px solid #999 !important}input.flt{width:99% !important}.inf{height:$min-height;background:#d7d7d7 url("images/bg_infDiv.jpg") 0 0 repeat-x !important}input.reset{background:transparent url("images/btn_eraser.gif") center center no-repeat !important}.helpBtn:hover{background-color:transparent}.nextPage{background:transparent url("images/btn_next_page.gif") center center no-repeat !important;}.nextPage:hover{background:transparent url("images/btn_over_next_page.gif") center center no-repeat !important}.previousPage{background:transparent url("images/btn_previous_page.gif") center center no-repeat !important;}.previousPage:hover{background:transparent url("images/btn_over_previous_page.gif") center center no-repeat !important}.firstPage{background:transparent url("images/btn_first_page.gif") center center no-repeat !important;}.firstPage:hover{background:transparent url("images/btn_over_first_page.gif") center center no-repeat !important}.lastPage{background:transparent url("images/btn_last_page.gif") center center no-repeat !important;}.lastPage:hover{background:transparent url("images/btn_over_last_page.gif") center center no-repeat !important}div.grd_Cont{background-color:#ebecee !important;border:1px solid #ccc !important;padding:0 !important;}div.grd_Cont .even{background-color:#fff}div.grd_Cont .odd{background-color:#d5d5d5}div.grd_headTblCont{background-color:#ebecee !important;border-bottom:none !important;}div.grd_headTblCont table{border-right:none !important}div.grd_tblCont table th,div.grd_headTblCont table th,div.grd_headTblCont table td{background:#ebecee url("images/bg_th.jpg") left top repeat-x !important;border-bottom:1px solid #d0d0d0 !important;border-right:1px solid #d0d0d0 !important;border-left:1px solid #fff !important;border-top:1px solid #fff !important}div.grd_tblCont table td{border-bottom:1px solid #999 !important}.grd_inf{background:#d7d7d7 url("images/bg_infDiv.jpg") 0 0 repeat-x !important;border-top:1px solid #d0d0d0 !important}.loader{border:1px solid #999}.defaultLoader{width:32px;height:32px;background:transparent url("images/img_loading.gif") 0 0 no-repeat !important}.even{background-color:#fff}.odd{background-color:#d5d5d5}span.expClpFlt a.btnExpClpFlt:hover{background-color:transparent !important}.activeHeader{background:#999 !important}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 303 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 356 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 332 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 331 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 440 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 640 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 427 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 393 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 395 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 290 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 201 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 441 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 469 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 300 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 303 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 928 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

@@ -1,6 +0,0 @@
/**
* tablefilter v0.5.41 by Max Guglielmi
* build date: 2017-09-08T07:54:00.964Z
* MIT License
*/
table.TF{border-left:1px dotted #81963b;border-top:none;border-right:0;border-bottom:none;}table.TF th{background:#39424b url("images/bg_headers.jpg") left top repeat-x;border-bottom:0;border-right:1px dotted #d0d0d0;border-left:0;border-top:0;color:#fff}table.TF td{border-bottom:1px dotted #81963b;border-right:1px dotted #81963b;padding:5px}.fltrow{background-color:#81963b !important;}.fltrow th,.fltrow td{border-bottom:1px dotted #39424b !important;border-right:1px dotted #fff !important;border-left:0 !important;border-top:0 !important;padding:1px 3px 1px 3px !important}.flt,select.flt,select.flt_multi,.flt_s,.single_flt,.div_checklist{border:1px solid #687830 !important}input.flt{width:99% !important}.inf{background:#d8d8d8;height:$min-height}input.reset{width:53px;background:transparent url("images/btn_filter.png") center center no-repeat !important}.helpBtn:hover{background-color:transparent}.nextPage{background:transparent url("images/btn_next_page.gif") center center no-repeat !important}.previousPage{background:transparent url("images/btn_previous_page.gif") center center no-repeat !important}.firstPage{background:transparent url("images/btn_first_page.gif") center center no-repeat !important}.lastPage{background:transparent url("images/btn_last_page.gif") center center no-repeat !important}div.grd_Cont{background:#81963b url("images/bg_headers.jpg") left top repeat-x !important;border:1px solid #ccc !important;padding:0 1px 1px 1px !important;}div.grd_Cont .even{background-color:#bccd83}div.grd_Cont .odd{background-color:#fff}div.grd_headTblCont{background-color:#ebecee !important;border-bottom:none !important}div.grd_tblCont table{border-right:none !important;}div.grd_tblCont table td{border-bottom:1px dotted #81963b;border-right:1px dotted #81963b}div.grd_tblCont table th,div.grd_headTblCont table th{background:transparent url("images/bg_headers.jpg") 0 0 repeat-x !important;border-bottom:0 !important;border-right:1px dotted #d0d0d0 !important;border-left:0 !important;border-top:0 !important;padding:0 4px 0 4px !important;color:#fff !important;height:35px !important}div.grd_headTblCont table td{border-bottom:1px dotted #39424b !important;border-right:1px dotted #fff !important;border-left:0 !important;border-top:0 !important;background-color:#81963b !important;padding:1px 3px 1px 3px !important}.grd_inf{background-color:#d8d8d8;border-top:1px solid #d0d0d0 !important}.loader{border:0 !important;background:#81963b !important}.defaultLoader{width:32px;height:32px;background:transparent url("images/img_loading.gif") 0 0 no-repeat !important}.even{background-color:#bccd83}.odd{background-color:#fff}span.expClpFlt a.btnExpClpFlt:hover{background-color:transparent !important}.activeHeader{background:#81963b !important}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 554 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 601 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 847 B

@@ -1,6 +0,0 @@
/**
* tablefilter v0.5.41 by Max Guglielmi
* build date: 2017-09-08T07:54:00.964Z
* MIT License
*/
table.TF{padding:0;color:#000;border-right:1px solid #a4bed4;border-top:1px solid #a4bed4;border-left:1px solid #a4bed4;border-bottom:0;}table.TF th{margin:0;color:inherit;background:#d1e5fe url("images/bg_skyblue.gif") 0 0 repeat-x;border-color:#fdfdfd #a4bed4 #a4bed4 #fdfdfd;border-width:1px;border-style:solid}table.TF td{margin:0;padding:5px;color:inherit;border-bottom:1px solid #a4bed4;border-left:0;border-top:0;border-right:0}.fltrow{background-color:#d1e5fe !important;}.fltrow th,.fltrow td{padding:1px 3px 1px 3px !important}.flt,select.flt,select.flt_multi,.flt_s,.single_flt,.div_checklist{border:1px solid #a4bed4 !important}input.flt{width:99% !important}.inf{background-color:#e3efff !important;border:1px solid #a4bed4;height:$min-height;color:#004a6f}div.tot,div.status{border-right:0 !important}.helpBtn:hover{background-color:transparent}input.reset{background:transparent url("images/icn_clear_filters.png") center center no-repeat !important}.nextPage{background:transparent url("images/btn_next_page.gif") center center no-repeat !important;border:1px solid transparent !important;}.nextPage:hover{background:#ffe4ab url("images/btn_next_page.gif") center center no-repeat !important;border:1px solid #ffb552 !important}.previousPage{background:transparent url("images/btn_prev_page.gif") center center no-repeat !important;border:1px solid transparent !important;}.previousPage:hover{background:#ffe4ab url("images/btn_prev_page.gif") center center no-repeat !important;border:1px solid #ffb552 !important}.firstPage{background:transparent url("images/btn_first_page.gif") center center no-repeat !important;border:1px solid transparent !important;}.firstPage:hover{background:#ffe4ab url("images/btn_first_page.gif") center center no-repeat !important;border:1px solid #ffb552 !important}.lastPage{background:transparent url("images/btn_last_page.gif") center center no-repeat !important;border:1px solid transparent !important;}.lastPage:hover{background:#ffe4ab url("images/btn_last_page.gif") center center no-repeat !important;border:1px solid #ffb552 !important}.activeHeader{background:#ffe4ab !important;border:1px solid #ffb552 !important;color:inherit !important}div.grd_Cont{background-color:#d9eaed !important;border:1px solid #9cc !important;padding:0 !important;}div.grd_Cont .even{background-color:#fff}div.grd_Cont .odd{background-color:#e3efff}div.grd_headTblCont{background-color:#d9eaed !important;border-bottom:none !important}div.grd_tblCont table{border-right:none !important}div.grd_tblCont table th,div.grd_headTblCont table th,div.grd_headTblCont table td{background:#d9eaed url("images/bg_skyblue.gif") left top repeat-x;border-bottom:1px solid #a4bed4;border-right:1px solid #a4bed4 !important;border-left:1px solid #fff !important;border-top:1px solid #fff !important}div.grd_tblCont table td{border-bottom:1px solid #a4bed4 !important;border-right:0 !important;border-left:0 !important;border-top:0 !important}.grd_inf{background-color:#cce2fe;color:#004a6f;border-top:1px solid #9cc !important;}.grd_inf a{text-decoration:none;font-weight:bold}.loader{background-color:#2d8eef;border:1px solid #cce2fe;border-radius:5px}.even{background-color:#fff}.odd{background-color:#e3efff}span.expClpFlt a.btnExpClpFlt:hover{background-color:transparent !important}.ezActiveRow{background-color:#ffdc61 !important;color:inherit}.ezSelectedRow{background-color:#ffe4ab !important;color:inherit}.ezActiveCell{background-color:#fff !important;color:#000 !important;font-weight:bold}.ezETSelectedCell{background-color:#fff !important;font-weight:bold;color:#000 !important}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 601 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 847 B

@@ -1,6 +0,0 @@
/**
* tablefilter v0.5.41 by Max Guglielmi
* build date: 2017-09-08T07:54:00.964Z
* MIT License
*/
table.TF{padding:0;color:inherit;border-right:1px solid transparent;border-top:1px solid transparent;border-left:1px solid transparent;border-bottom:0;}table.TF th{margin:0;color:inherit;background-color:transparent;border-color:transparent;border-width:1px;border-style:solid;}table.TF th:last-child{border-right:1px solid transparent}table.TF td{margin:0;padding:5px;color:inherit;border-bottom:1px solid transparent;border-left:0;border-top:0;border-right:0}.fltrow{background-color:transparent;}.fltrow th,.fltrow td{padding:1px 3px 1px 3px;border-bottom:1px solid transparent !important;}.fltrow th:last-child,.fltrow td:last-child{border-right:1px solid transparent}.flt,select.flt,select.flt_multi,.flt_s,.single_flt,.div_checklist{border:1px solid #a4bed4}input.flt{width:99% !important}.inf{background-color:transparent;border:1px solid transparent;height:$min-height;color:inherit}div.tot,div.status{border-right:0 !important}.helpBtn:hover{background-color:transparent}input.reset{background:transparent url("images/icn_clear_filters.png") center center no-repeat !important}.nextPage{background:transparent url("images/btn_next_page.gif") center center no-repeat !important;border:1px solid transparent !important;}.nextPage:hover{background:#f7f7f7 url("images/btn_next_page.gif") center center no-repeat !important;border:1px solid #f7f7f7 !important}.previousPage{background:transparent url("images/btn_prev_page.gif") center center no-repeat !important;border:1px solid transparent !important;}.previousPage:hover{background:#f7f7f7 url("images/btn_prev_page.gif") center center no-repeat !important;border:1px solid #f7f7f7 !important}.firstPage{background:transparent url("images/btn_first_page.gif") center center no-repeat !important;border:1px solid transparent !important;}.firstPage:hover{background:#f7f7f7 url("images/btn_first_page.gif") center center no-repeat !important;border:1px solid #f7f7f7 !important}.lastPage{background:transparent url("images/btn_last_page.gif") center center no-repeat !important;border:1px solid transparent !important;}.lastPage:hover{background:#f7f7f7 url("images/btn_last_page.gif") center center no-repeat !important;border:1px solid #f7f7f7 !important}.activeHeader{background:#f7f7f7 !important;border:1px solid transparent;color:inherit !important}div.grd_Cont{-webkit-box-shadow:0 0 0 0 rgba(50,50,50,0.75);-moz-box-shadow:0 0 0 0 rgba(50,50,50,0.75);box-shadow:0 0 0 0 rgba(50,50,50,0.75);background-color:transparent;border:1px solid transparent;padding:0 !important;}div.grd_Cont .even{background-color:transparent}div.grd_Cont .odd{background-color:#f7f7f7}div.grd_headTblCont{background-color:transparent;border-bottom:none !important}div.grd_tblCont table{border-right:none !important}div.grd_tblCont table th,div.grd_headTblCont table th,div.grd_headTblCont table td{background:transparent;border-bottom:1px solid transparent;border-right:1px solid transparent !important;border-left:1px solid transparent;border-top:1px solid transparent}div.grd_tblCont table td{border-bottom:1px solid transparent;border-right:0 !important;border-left:0 !important;border-top:0 !important}.grd_inf{background-color:transparent;color:inherit;border-top:1px solid transparent;}.grd_inf a{text-decoration:none;font-weight:bold}.loader{background-color:#f7f7f7;border:1px solid #f7f7f7;border-radius:5px;color:#000;text-shadow:none}.even{background-color:transparent}.odd{background-color:#f7f7f7}span.expClpFlt a.btnExpClpFlt:hover{background-color:transparent !important}.ezActiveRow{background-color:#ccc !important;color:inherit}.ezSelectedRow{background-color:#ccc !important;color:inherit}.ezActiveCell{background-color:transparent;color:inherit;font-weight:bold}.ezETSelectedCell{background-color:transparent;font-weight:bold;color:inherit}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 201 B

File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-3
View File
@@ -1,3 +0,0 @@
'use strict';
module.exports = require('./dist/tablefilter/tablefilter').TableFilter;
-438
View File
@@ -1,438 +0,0 @@
/*----------------------------------------------------------------------------\
| Sortable Table 1.12 |
|-----------------------------------------------------------------------------|
| Created by Erik Arvidsson |
| (http://webfx.eae.net/contact.html#erik) |
| For WebFX (http://webfx.eae.net/) |
|-----------------------------------------------------------------------------|
| A DOM 1 based script that allows an ordinary HTML table to be sortable. |
|-----------------------------------------------------------------------------|
| Copyright (c) 1998 - 2006 Erik Arvidsson |
|-----------------------------------------------------------------------------|
| Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| use this file except in compliance with the License. You may obtain a copy |
| of the License at http://www.apache.org/licenses/LICENSE-2.0 |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| License for the specific language governing permissions and limitations |
| under the License. |
|-----------------------------------------------------------------------------|
| 2003-01-10 | First version |
| 2003-01-19 | Minor changes to the date parsing |
| 2003-01-28 | JScript 5.0 fixes (no support for 'in' operator) |
| 2003-02-01 | Sloppy typo like error fixed in getInnerText |
| 2003-07-04 | Added workaround for IE cellIndex bug. |
| 2003-11-09 | The bDescending argument to sort was not correctly working |
| | Using onclick DOM0 event if no support for addEventListener |
| | or attachEvent |
| 2004-01-13 | Adding addSortType and removeSortType which makes it a lot |
| | easier to add new, custom sort types. |
| 2004-01-27 | Switch to use descending = false as the default sort order. |
| | Change defaultDescending to suit your needs. |
| 2004-03-14 | Improved sort type None look and feel a bit |
| 2004-08-26 | Made the handling of tBody and tHead more flexible. Now you |
| | can use another tHead or no tHead, and you can chose some |
| | other tBody. |
| 2006-04-25 | Changed license to Apache Software License 2.0 |
|-----------------------------------------------------------------------------|
| Created 2003-01-10 | All changes are in the log above. | Updated 2006-04-25 |
\----------------------------------------------------------------------------*/
function SortableTable(oTable, oSortTypes) {
this.sortTypes = oSortTypes || [];
this.sortColumn = null;
this.descending = null;
var oThis = this;
this._headerOnclick = function (e) {
oThis.headerOnclick(e);
};
if (oTable) {
this.setTable( oTable );
this.document = oTable.ownerDocument || oTable.document;
}
else {
this.document = document;
}
// only IE needs this
var win = this.document.defaultView || this.document.parentWindow;
this._onunload = function () {
oThis.destroy();
};
if (win && typeof win.attachEvent != "undefined") {
win.attachEvent("onunload", this._onunload);
}
}
SortableTable.gecko = navigator.product == "Gecko";
SortableTable.msie = /msie/i.test(navigator.userAgent);
// Mozilla is faster when doing the DOM manipulations on
// an orphaned element. MSIE is not
SortableTable.removeBeforeSort = SortableTable.gecko;
SortableTable.prototype.onsort = function () {};
// default sort order. true -> descending, false -> ascending
SortableTable.prototype.defaultDescending = false;
// shared between all instances. This is intentional to allow external files
// to modify the prototype
SortableTable.prototype._sortTypeInfo = {};
SortableTable.prototype.setTable = function (oTable) {
if ( this.tHead )
this.uninitHeader();
this.element = oTable;
this.setTHead( oTable.tHead );
this.setTBody( oTable.tBodies[0] );
};
SortableTable.prototype.setTHead = function (oTHead) {
if (this.tHead && this.tHead != oTHead )
this.uninitHeader();
this.tHead = oTHead;
this.initHeader( this.sortTypes );
};
SortableTable.prototype.setTBody = function (oTBody) {
this.tBody = oTBody;
};
SortableTable.prototype.setSortTypes = function ( oSortTypes ) {
if ( this.tHead )
this.uninitHeader();
this.sortTypes = oSortTypes || [];
if ( this.tHead )
this.initHeader( this.sortTypes );
};
// adds arrow containers and events
// also binds sort type to the header cells so that reordering columns does
// not break the sort types
SortableTable.prototype.initHeader = function (oSortTypes) {
if (!this.tHead) return;
var cells = this.tHead.rows[0].cells;
var doc = this.tHead.ownerDocument || this.tHead.document;
this.sortTypes = oSortTypes || [];
var l = cells.length;
var img, c;
for (var i = 0; i < l; i++) {
c = cells[i];
if (this.sortTypes[i] != null && this.sortTypes[i] != "None") {
img = doc.createElement("IMG");
img.src = "images/blank.png";
c.appendChild(img);
if (this.sortTypes[i] != null)
c._sortType = this.sortTypes[i];
if (typeof c.addEventListener != "undefined")
c.addEventListener("click", this._headerOnclick, false);
else if (typeof c.attachEvent != "undefined")
c.attachEvent("onclick", this._headerOnclick);
else
c.onclick = this._headerOnclick;
}
else
{
c.setAttribute( "_sortType", oSortTypes[i] );
c._sortType = "None";
}
}
this.updateHeaderArrows();
};
// remove arrows and events
SortableTable.prototype.uninitHeader = function () {
if (!this.tHead) return;
var cells = this.tHead.rows[0].cells;
var l = cells.length;
var c;
for (var i = 0; i < l; i++) {
c = cells[i];
if (c._sortType != null && c._sortType != "None") {
c.removeChild(c.lastChild);
if (typeof c.removeEventListener != "undefined")
c.removeEventListener("click", this._headerOnclick, false);
else if (typeof c.detachEvent != "undefined")
c.detachEvent("onclick", this._headerOnclick);
c._sortType = null;
c.removeAttribute( "_sortType" );
}
}
};
SortableTable.prototype.updateHeaderArrows = function () {
if (!this.tHead) return;
var cells = this.tHead.rows[0].cells;
var l = cells.length;
var img;
for (var i = 0; i < l; i++) {
if (cells[i]._sortType != null && cells[i]._sortType != "None") {
img = cells[i].lastChild;
if (i == this.sortColumn)
img.className = "sort-arrow " + (this.descending ? "descending" : "ascending");
else
img.className = "sort-arrow";
}
}
};
SortableTable.prototype.headerOnclick = function (e) {
// find TD element
var el = e.target || e.srcElement;
while (el.tagName != "TD")
el = el.parentNode;
this.sort(SortableTable.msie ? SortableTable.getCellIndex(el) : el.cellIndex);
};
// IE returns wrong cellIndex when columns are hidden
SortableTable.getCellIndex = function (oTd) {
var cells = oTd.parentNode.childNodes
var l = cells.length;
var i;
for (i = 0; cells[i] != oTd && i < l; i++)
;
return i;
};
SortableTable.prototype.getSortType = function (nColumn) {
return this.sortTypes[nColumn] || "String";
};
// only nColumn is required
// if bDescending is left out the old value is taken into account
// if sSortType is left out the sort type is found from the sortTypes array
SortableTable.prototype.sort = function (nColumn, bDescending, sSortType) {
if (!this.tBody) return;
if (sSortType == null)
sSortType = this.getSortType(nColumn);
// exit if None
if (sSortType == "None")
return;
if (bDescending == null) {
if (this.sortColumn != nColumn)
this.descending = this.defaultDescending;
else
this.descending = !this.descending;
}
else
this.descending = bDescending;
this.sortColumn = nColumn;
if (typeof this.onbeforesort == "function")
this.onbeforesort();
var f = this.getSortFunction(sSortType, nColumn);
var a = this.getCache(sSortType, nColumn);
var tBody = this.tBody;
a.sort(f);
if (this.descending)
a.reverse();
if (SortableTable.removeBeforeSort) {
// remove from doc
var nextSibling = tBody.nextSibling;
var p = tBody.parentNode;
p.removeChild(tBody);
}
// insert in the new order
var l = a.length;
for (var i = 0; i < l; i++)
tBody.appendChild(a[i].element);
if (SortableTable.removeBeforeSort) {
// insert into doc
p.insertBefore(tBody, nextSibling);
}
this.updateHeaderArrows();
this.destroyCache(a);
if (typeof this.onsort == "function")
this.onsort();
};
SortableTable.prototype.asyncSort = function (nColumn, bDescending, sSortType) {
var oThis = this;
this._asyncsort = function () {
oThis.sort(nColumn, bDescending, sSortType);
};
window.setTimeout(this._asyncsort, 1);
};
SortableTable.prototype.getCache = function (sType, nColumn) {
if (!this.tBody) return [];
var rows = this.tBody.rows;
var l = rows.length;
var a = new Array(l);
var r;
for (var i = 0; i < l; i++) {
r = rows[i];
a[i] = {
value: this.getRowValue(r, sType, nColumn),
element: r
};
};
return a;
};
SortableTable.prototype.destroyCache = function (oArray) {
var l = oArray.length;
for (var i = 0; i < l; i++) {
oArray[i].value = null;
oArray[i].element = null;
oArray[i] = null;
}
};
SortableTable.prototype.getRowValue = function (oRow, sType, nColumn) {
// if we have defined a custom getRowValue use that
if (this._sortTypeInfo[sType] && this._sortTypeInfo[sType].getRowValue)
return this._sortTypeInfo[sType].getRowValue(oRow, nColumn);
var s;
var c = oRow.cells[nColumn];
if (typeof c.innerText != "undefined")
s = c.innerText;
else
s = SortableTable.getInnerText(c);
return this.getValueFromString(s, sType);
};
SortableTable.getInnerText = function (oNode) {
var s = "";
var cs = oNode.childNodes;
var l = cs.length;
for (var i = 0; i < l; i++) {
switch (cs[i].nodeType) {
case 1: //ELEMENT_NODE
s += SortableTable.getInnerText(cs[i]);
break;
case 3: //TEXT_NODE
s += cs[i].nodeValue;
break;
}
}
return s;
};
SortableTable.prototype.getValueFromString = function (sText, sType) {
if (this._sortTypeInfo[sType])
return this._sortTypeInfo[sType].getValueFromString( sText );
return sText;
/*
switch (sType) {
case "Number":
return Number(sText);
case "CaseInsensitiveString":
return sText.toUpperCase();
case "Date":
var parts = sText.split("-");
var d = new Date(0);
d.setFullYear(parts[0]);
d.setDate(parts[2]);
d.setMonth(parts[1] - 1);
return d.valueOf();
}
return sText;
*/
};
SortableTable.prototype.getSortFunction = function (sType, nColumn) {
if (this._sortTypeInfo[sType])
return this._sortTypeInfo[sType].compare;
return SortableTable.basicCompare;
};
SortableTable.prototype.destroy = function () {
this.uninitHeader();
var win = this.document.parentWindow;
if (win && typeof win.detachEvent != "undefined") { // only IE needs this
win.detachEvent("onunload", this._onunload);
}
this._onunload = null;
this.element = null;
this.tHead = null;
this.tBody = null;
this.document = null;
this._headerOnclick = null;
this.sortTypes = null;
this._asyncsort = null;
this.onsort = null;
};
// Adds a sort type to all instance of SortableTable
// sType : String - the identifier of the sort type
// fGetValueFromString : function ( s : string ) : T - A function that takes a
// string and casts it to a desired format. If left out the string is just
// returned
// fCompareFunction : function ( n1 : T, n2 : T ) : Number - A normal JS sort
// compare function. Takes two values and compares them. If left out less than,
// <, compare is used
// fGetRowValue : function( oRow : HTMLTRElement, nColumn : int ) : T - A function
// that takes the row and the column index and returns the value used to compare.
// If left out then the innerText is first taken for the cell and then the
// fGetValueFromString is used to convert that string the desired value and type
SortableTable.prototype.addSortType = function (sType, fGetValueFromString, fCompareFunction, fGetRowValue) {
this._sortTypeInfo[sType] = {
type: sType,
getValueFromString: fGetValueFromString || SortableTable.idFunction,
compare: fCompareFunction || SortableTable.basicCompare,
getRowValue: fGetRowValue
};
};
// this removes the sort type from all instances of SortableTable
SortableTable.prototype.removeSortType = function (sType) {
delete this._sortTypeInfo[sType];
};
SortableTable.basicCompare = function compare(n1, n2) {
if (n1.value < n2.value)
return -1;
if (n2.value < n1.value)
return 1;
return 0;
};
SortableTable.idFunction = function (x) {
return x;
};
SortableTable.toUpperCase = function (s) {
return s.toUpperCase();
};
SortableTable.toDate = function (s) {
var parts = s.split("-");
var d = new Date(0);
d.setFullYear(parts[0]);
d.setDate(parts[2]);
d.setMonth(parts[1] - 1);
return d.valueOf();
};
// add sort types
SortableTable.prototype.addSortType("Number", Number);
SortableTable.prototype.addSortType("CaseInsensitiveString", SortableTable.toUpperCase);
SortableTable.prototype.addSortType("Date", SortableTable.toDate);
SortableTable.prototype.addSortType("String");
// None is a special case
-136
View File
@@ -1,136 +0,0 @@
{
"_args": [
[
{
"raw": "tablefilter",
"scope": null,
"escapedName": "tablefilter",
"name": "tablefilter",
"rawSpec": "",
"spec": "latest",
"type": "tag"
},
"/var/www/afup"
]
],
"_from": "tablefilter@latest",
"_id": "tablefilter@0.5.41",
"_inCache": true,
"_location": "/tablefilter",
"_nodeVersion": "6.3.0",
"_npmOperationalInternal": {
"host": "s3://npm-registry-packages",
"tmp": "tmp/tablefilter-0.5.41.tgz_1504859207388_0.0773027497343719"
},
"_npmUser": {
"name": "koalyptus",
"email": "maxgug@hotmail.com"
},
"_npmVersion": "3.10.3",
"_phantomChildren": {},
"_requested": {
"raw": "tablefilter",
"scope": null,
"escapedName": "tablefilter",
"name": "tablefilter",
"rawSpec": "",
"spec": "latest",
"type": "tag"
},
"_requiredBy": [
"#USER"
],
"_resolved": "https://registry.npmjs.org/tablefilter/-/tablefilter-0.5.41.tgz",
"_shasum": "43a335d86b65784bec48c42b83b566737ff5777e",
"_shrinkwrap": null,
"_spec": "tablefilter",
"_where": "/var/www/afup",
"author": {
"name": "Max Guglielmi",
"url": "https://github.com/koalyptus"
},
"bugs": {
"url": "https://github.com/koalyptus/TableFilter/issues"
},
"dependencies": {},
"description": "A Javascript library making HTML tables filterable and a bit more",
"devDependencies": {
"babel-core": "^6.24.1",
"babel-eslint": "7.2.3",
"babel-loader": "^7.0.0",
"babel-plugin-transform-es2015-classes": "^6.24.1",
"babel-preset-es2015": "^6.24.1",
"clean-webpack-plugin": "^0.1.16",
"codecov": "2.3.0",
"diacritics": "1.3.0",
"format-number": "3.0.0",
"grunt": "^1.0.1",
"grunt-babel": "^7.0.0",
"grunt-cli": "1.2.0",
"grunt-contrib-clean": "^1.1.0",
"grunt-contrib-connect": "^1.0.2",
"grunt-contrib-copy": "^1.0.0",
"grunt-contrib-stylus": "^1.2.0",
"grunt-contrib-watch": "^1.0.0",
"grunt-esdoc": "^0.0.4",
"grunt-eslint": "20.1.0",
"grunt-gh-pages": "^2.0.0",
"grunt-qunit-istanbul": "1.0.0",
"grunt-string-replace": "^1.3.1",
"grunt-webpack": "^3.0.0",
"isparta-loader": "2.0.0",
"script-loader": "^0.7.0",
"string-replace-webpack-plugin": "^0.1.3",
"sugar-date": "2.0.4",
"webpack": "^3.0.0",
"webpack-dev-server": "^2.4.5"
},
"directories": {},
"dist": {
"shasum": "43a335d86b65784bec48c42b83b566737ff5777e",
"tarball": "https://registry.npmjs.org/tablefilter/-/tablefilter-0.5.41.tgz"
},
"gitHead": "e4ed4d55670bf758a0858acd477fb3aa6a694f9b",
"homepage": "http://koalyptus.github.io/TableFilter",
"keywords": [
"filter",
"table",
"javascript",
"filterable",
"grid",
"datagrid",
"sort",
"pagination"
],
"license": "MIT",
"maintainers": [
{
"name": "koalyptus",
"email": "maxgug@hotmail.com"
}
],
"name": "tablefilter",
"optionalDependencies": {},
"publishConfig": {
"tag": "next"
},
"readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git+https://github.com/koalyptus/TableFilter.git"
},
"scripts": {
"build": "grunt build",
"build:demos": "grunt build-demos",
"codecov": "codecov",
"deploy": "grunt deploy",
"dev": "grunt dev",
"dist": "grunt",
"esdoc": "grunt esdoc",
"eslint": "grunt eslint",
"server": "grunt server",
"start": "npm run server",
"test": "grunt test"
},
"version": "0.5.41"
}
-22
View File
@@ -1,22 +0,0 @@
/**
* Array utilities
*/
import {matchCase} from './string';
/**
* Checks if given item can be found in the passed collection
* @param {Array} arr collection
* @param {Any} val item to search
* @param {Boolean} caseSensitive respects case if true
* @return {Boolean}
*/
export const has = (arr, val, caseSensitive) => {
let sCase = Boolean(caseSensitive);
for (var i = 0, l = arr.length; i < l; i++) {
if (matchCase(arr[i].toString(), sCase) === val) {
return true;
}
}
return false;
};
-200
View File
@@ -1,200 +0,0 @@
import {DateType} from './modules/dateType';
import {Help} from './modules/help';
import {State} from './modules/state';
import {GridLayout} from './modules/gridLayout';
import {Loader} from './modules/loader';
import {HighlightKeyword} from './modules/highlightKeywords';
import {PopupFilter} from './modules/popupFilter';
import {MarkActiveColumns} from './modules/markActiveColumns';
import {RowsCounter} from './modules/rowsCounter';
import {StatusBar} from './modules/statusBar';
import {ClearButton} from './modules/clearButton';
import {AlternateRows} from './modules/alternateRows';
import {NoResults} from './modules/noResults';
import {Paging} from './modules/paging';
/**
* Filter types
*/
/**
* Input filter type
* @type {String}
*/
export const INPUT = 'input';
/**
* Select filter type
* @type {String}
*/
export const SELECT = 'select';
/**
* Multiple select filter type
* @type {String}
*/
export const MULTIPLE = 'multiple';
/**
* Checklist filter type
* @type {String}
*/
export const CHECKLIST = 'checklist';
/**
* None filter type
* @type {String}
*/
export const NONE = 'none';
/**
* Key codes
*/
/**
* Enter key code
* @type {Number}
*/
export const ENTER_KEY = 13;
/**
* Tab key code
* @type {Number}
*/
export const TAB_KEY = 9;
/**
* Escape key code
* @type {Number}
*/
export const ESC_KEY = 27;
/**
* Up arrow key code
* @type {Number}
*/
export const UP_ARROW_KEY = 38;
/**
* Down arrow key code
* @type {Number}
*/
export const DOWN_ARROW_KEY = 40;
/**
* HTML tags
*/
/**
* Header cell tag
* @type {String}
*/
export const HEADER_TAG = 'TH';
/**
* Cell tag
* @type {String}
*/
export const CELL_TAG = 'TD';
/**
* Data types
*/
/**
* String
* @type {String}
*/
export const STRING = 'string';
/**
* Number
* @type {String}
*/
export const NUMBER = 'number';
/**
* Formatted number
* @type {String}
*/
export const FORMATTED_NUMBER = 'formatted-number';
/**
* Date
* @type {String}
*/
export const DATE = 'date';
/**
* IP address
* @type {String}
*/
export const IP_ADDRESS = 'ipaddress';
/**
* Default values
*/
/**
* Auto filter delay in milliseconds
* @type {Number}
*/
export const AUTO_FILTER_DELAY = 750;
/**
* TableFilter features definitions
* @type {Object}
*/
export const FEATURES = {
dateType: {
class: DateType,
name: 'dateType'
},
help: {
class: Help,
name: 'help',
enforce: true
},
state: {
class: State,
name: 'state'
},
markActiveColumns: {
class: MarkActiveColumns,
name: 'markActiveColumns'
},
gridLayout: {
class: GridLayout,
name: 'gridLayout'
},
loader: {
class: Loader,
name: 'loader'
},
highlightKeyword: {
class: HighlightKeyword,
name: 'highlightKeyword',
property: 'highlightKeywords'
},
popupFilter: {
class: PopupFilter,
name: 'popupFilter',
property: 'popupFilters'
},
rowsCounter: {
class: RowsCounter,
name: 'rowsCounter'
},
statusBar: {
class: StatusBar,
name: 'statusBar'
},
clearButton: {
class: ClearButton,
name: 'clearButton',
property: 'btnReset'
},
alternateRows: {
class: AlternateRows,
name: 'alternateRows'
},
noResults: {
class: NoResults,
name: 'noResults'
},
paging: {
class: Paging,
name: 'paging'
}
};
-57
View File
@@ -1,57 +0,0 @@
import {root} from './root';
/**
* Cookie utilities
*/
const doc = root.document;
export default {
/**
* Write a cookie
* @param {String} name Name of the cookie
* @param {String} value Value of the cookie
* @param {Number} hours Cookie duration in hours
*/
write(name, value, hours) {
let expire = '';
if (hours) {
expire = new Date((new Date()).getTime() + hours * 3600000);
expire = '; expires=' + expire.toGMTString();
}
doc.cookie = name + '=' + escape(value) + expire;
},
/**
* Read a cookie
* @param {String} name Name of the cookie
* @returns {String} Value of the cookie
*/
read(name) {
let cookieValue = '',
search = name + '=';
if (doc.cookie.length > 0) {
let cookie = doc.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 a cookie
* @param {String} name Name of the cookie
*/
remove(name) {
this.write(name, '', -1);
}
};
-190
View File
@@ -1,190 +0,0 @@
import {root} from './root';
import {isArray, isString, isUndef} from './types';
import {trim} from './string';
/**
* DOM utilities
*/
const doc = root.document;
/**
* Returns text + text of children of given node
* @param {NodeElement} node
* @return {String}
*/
export const getText = (node) => {
if (isUndef(node.textContent)) {
return trim(node.innerText);
}
return trim(node.textContent);
};
/**
* Returns the first text node contained in the supplied node
* @param {NodeElement} node node
* @return {String}
*/
export const getFirstTextNode = (node) => {
for (let i = 0; i < node.childNodes.length; i++) {
let n = node.childNodes[i];
if (n.nodeType === 3) {
return n.data;
}
}
};
/**
* 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
*/
export const createElm = (...args) => {
let tag = args[0];
if (!isString(tag)) {
return null;
}
let el = doc.createElement(tag);
for (let i = 0; i < args.length; i++) {
let arg = args[i];
if (isArray(arg) && arg.length === 2) {
el.setAttribute(arg[0], arg[1]);
}
}
return el;
};
/**
* Removes passed node from DOM
* @param {DOMElement} node
* @return {DOMElement} old node reference
*/
export const removeElm = (node) => node.parentNode.removeChild(node);
/**
* Returns a text node with given text
* @param {String} txt
* @return {Object}
*/
export const createText = (txt) => doc.createTextNode(txt);
/**
* Determine whether the passed elements is assigned the given class
* @param {DOMElement} ele DOM element
* @param {String} cls CSS class name
* @returns {Boolean}
*/
export const hasClass = (ele, cls) => {
if (isUndef(ele)) {
return false;
}
if (supportsClassList()) {
return ele.classList.contains(cls);
}
return ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'));
};
/**
* Adds the specified class to the passed element
* @param {DOMElement} ele DOM element
* @param {String} cls CSS class name
*/
export const addClass = (ele, cls) => {
if (isUndef(ele)) {
return;
}
if (supportsClassList()) {
ele.classList.add(cls);
return;
}
if (ele.className === '') {
ele.className = cls;
}
else if (!hasClass(ele, cls)) {
ele.className += ' ' + cls;
}
};
/**
* Removes the specified class to the passed element
* @param {DOMElement} ele DOM element
* @param {String} cls CSS class name
*/
export const removeClass = (ele, cls) => {
if (isUndef(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
*/
export const createOpt = (text, value, isSel) => {
let isSelected = isSel ? true : false;
let opt = isSelected ?
createElm('option', ['value', value], ['selected', 'true']) :
createElm('option', ['value', value]);
opt.appendChild(createText(text));
return opt;
};
/**
* Creates and returns a checklist item
* @param {String} id index of check item
* @param {String} chkValue check item value
* @param {String} labelText check item label text
* @return {Object} li DOM element
*/
export const createCheckItem = (id, chkValue, labelText) => {
let li = createElm('li');
let label = createElm('label', ['for', id]);
let check = createElm('input',
['id', id],
['name', id],
['type', 'checkbox'],
['value', chkValue]
);
label.appendChild(check);
label.appendChild(createText(labelText));
li.appendChild(label);
li.label = label;
li.check = check;
return li;
};
/**
* Returns the element matching the supplied Id
* @param {String} id Element identifier
* @return {DOMElement}
*/
export const elm = (id) => doc.getElementById(id);
/**
* Returns list of element matching the supplied tag name
* @param {String} tagname Tag name
* @return {NodeList}
*/
export const tag = (o, tagname) => o.getElementsByTagName(tagname);
// HTML5 classList API
function supportsClassList() {
return doc.documentElement.classList;
}
-53
View File
@@ -1,53 +0,0 @@
/**
* Event emitter class
*/
export class Emitter {
/**
* Creates an instance of Emitter.
*/
constructor() {
/**
* Events object
* @type {Object}
*/
this.events = {};
}
/**
* Subscribe to an event
* @param {Array} evts Collection of event names
* @param {Function} fn Function invoked when event is emitted
*/
on(evts, fn) {
evts.forEach((evt) => {
this.events[evt] = this.events[evt] || [];
this.events[evt].push(fn);
});
}
/**
* Unsubscribe to an event
* @param {Array} evts Collection of event names
* @param {Function} fn Function invoked when event is emitted
*/
off(evts, fn) {
evts.forEach((evt) => {
if (evt in this.events) {
this.events[evt].splice(this.events[evt].indexOf(fn), 1);
}
});
}
/**
* Emit an event
* @param {String} evt Event name followed by any other argument passed to
* the invoked function
*/
emit(evt /*, args...*/) {
if (evt in this.events) {
for (let i = 0; i < this.events[evt].length; i++) {
this.events[evt][i].apply(this, [].slice.call(arguments, 1));
}
}
}
}
-101
View File
@@ -1,101 +0,0 @@
import {root} from './root';
/**
* DOM event utilities
*/
/**
* Add event handler for specified event on passed element
*
* @param {DOMElement} obj Element
* @param {String} type Event type
* @param {Function} Handler
* @param {Boolean} capture Specifiy whether the event should be executed in
* the capturing or in the bubbling phase
*/
export const addEvt = (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 event handler for specified event on passed element
*
* @param {DOMElement} obj Element
* @param {String} type Event type
* @param {Function} Handler
* @param {Boolean} capture Specifiy whether the event should be executed in
* the capturing or in the bubbling phase
*/
export const removeEvt = (obj, type, func, capture) => {
if (obj.removeEventListener) {
obj.removeEventListener(type, func, capture);
} else if (obj.detachEvent) {
obj.detachEvent('on' + type, func);
} else {
obj['on' + type] = null;
}
};
/**
* Prevents further propagation of the current event in the bubbling phase
*
* @param {Event} evt Event on the DOM
*/
export const stopEvt = (evt) => {
if (!evt) {
evt = root.event;
}
if (evt.stopPropagation) {
evt.stopPropagation();
} else {
evt.cancelBubble = true;
}
};
/**
* Cancels the event if it is cancelable, without stopping further
* propagation of the event.
*
* @param {Event} evt Event on the DOM
*/
export const cancelEvt = (evt) => {
if (!evt) {
evt = root.event;
}
if (evt.preventDefault) {
evt.preventDefault();
} else {
evt.returnValue = false;
}
};
/**
* Reference to the object that dispatched the event
*
* @param {Event} evt Event on the DOM
* @returns {DOMElement}
*/
export const targetEvt = (evt) => {
if (!evt) {
evt = root.event;
}
return evt.target || evt.srcElement;
};
/**
* Returns the Unicode value of pressed key
*
* @param {Event} evt Event on the DOM
* @returns {Number}
*/
export const keyCode = (evt) => {
return evt.charCode ? evt.charCode :
(evt.keyCode ? evt.keyCode : (evt.which ? evt.which : 0));
};
@@ -1,507 +0,0 @@
import {Feature} from '../../feature';
import {tag} from '../../dom';
import {INPUT} from '../../const';
import {defaultsStr} from '../../settings';
import {root} from '../../root';
const INSTANTIATION_ERROR = `Failed to instantiate EditTable object.
\n"ezEditTable" dependency not found.`;
/**
* 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
*/
export default class AdapterEzEditTable extends Feature {
/**
* Creates an instance of AdapterEzEditTable
*
* @param {TableFilter} tf TableFilter instance
* @param {Object} cfg Configuration options for ezEditTable library
*/
constructor(tf, cfg) {
super(tf, cfg.name);
/**
* Module description
* @type {String}
*/
this.desc = defaultsStr(cfg.description, 'ezEditTable adapter');
/**
* Filename of ezEditTable library
* @type {String}
*/
this.filename = defaultsStr(cfg.filename, 'ezEditTable.js');
/**
* Path to ezEditTable library
* @type {String}
*/
this.vendorPath = cfg.vendor_path;
/**
* Load ezEditTable stylesheet
* @type {Boolean}
*/
this.loadStylesheet = Boolean(cfg.load_stylesheet);
/**
* Path to ezEditTable stylesheet
* @type {String}
*/
this.stylesheet = defaultsStr(cfg.stylesheet,
this.vendorPath + 'ezEditTable.css');
/**
* Name of ezEditTable stylesheet
* @type {String}
*/
this.stylesheetName = defaultsStr(cfg.stylesheet_name,
'ezEditTableCss');
// Enable the ezEditTable's scroll into view behaviour if grid layout on
cfg.scroll_into_view = cfg.scroll_into_view === false ?
false : tf.gridLayout;
/**
* ezEditTable instance
* @type {EditTable}
* @private
*/
this._ezEditTable = null;
/**
* ezEditTable configuration
* @private
*/
this.cfg = cfg;
this.enable();
}
/**
* Conditionally load ezEditTable library and set advanced grid
*/
init() {
if (this.initialized) {
return;
}
let tf = this.tf;
if (root.EditTable) {
this._setAdvancedGrid();
} else {
let 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');
}
// TODO: hack to prevent ezEditTable enter key event hijaking.
// Needs to be fixed in the vendor's library
this.emitter.on(['filter-focus', 'filter-blur'],
() => this._toggleForInputFilter());
/**
* @inherited
*/
this.initialized = true;
}
/**
* Instantiate ezEditTable component for advanced grid features
* @private
*/
_setAdvancedGrid() {
let tf = this.tf;
//start row for EditTable constructor needs to be calculated
let startRow,
cfg = this.cfg,
thead = tag(tf.dom(), '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/';
let editable = cfg.editable;
let selectable = cfg.selection;
if (selectable) {
cfg.default_selection = cfg.default_selection || 'row';
}
//CSS Styles
cfg.active_cell_css = cfg.active_cell_css || 'ezETSelectedCell';
let _lastValidRowIndex = 0;
let _lastRowIndex = 0;
if (selectable) {
//Row navigation needs to be calculated according to TableFilter's
//validRowsIndex array
let onAfterSelection = function (et, selectedElm, e) {
let slc = et.Selection;
//Next valid filtered row needs to be selected
let doSelect = function (nextRowIndex) {
if (et.defaultSelection === 'row') {
/* eslint-disable */
slc.SelectRowByIndex(nextRowIndex);
/* eslint-enable */
} else {
/* eslint-disable */
et.ClearSelections();
/* eslint-enable */
let cellIndex = selectedElm.cellIndex,
row = tf.dom().rows[nextRowIndex];
if (et.defaultSelection === 'both') {
/* eslint-disable */
slc.SelectRowByIndex(nextRowIndex);
/* eslint-enable */
}
if (row) {
/* eslint-disable */
slc.SelectCell(row.cells[cellIndex]);
/* eslint-enable */
}
}
//Table is filtered
if (tf.validRowsIndex.length !== tf.getRowsNb()) {
let r = tf.dom().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;
}
let 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,
/* eslint-disable */
keyCode = e !== undefined ? et.Event.GetKey(e) : 0,
/* eslint-enable */
isRowValid = validIndexes.indexOf(row.rowIndex) !== -1,
nextRowIndex,
paging = tf.feature('paging'),
//pgup/pgdown keys
d = keyCode === 34 || keyCode === 33 ?
(paging && paging.pageLength || 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 {
let 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 {
let 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
let onBeforeSelection = function (et, selectedElm) {
let row = et.defaultSelection !== 'row' ?
selectedElm.parentNode : selectedElm;
if (tf.paging) {
if (tf.feature('paging').nbPages > 1) {
let paging = tf.feature('paging');
//page length is re-assigned in case it has changed
et.nbRowsPerPage = paging.pageLength;
let validIndexes = tf.validRowsIndex,
validIdxLen = validIndexes.length,
pagingEndRow = parseInt(paging.startPagingRow, 10) +
parseInt(paging.pageLength, 10);
let 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) {
let advGrid = paging.tf.extension('advancedGrid');
let et = advGrid._ezEditTable;
let slc = et.Selection;
/* eslint-disable */
let row = slc.GetActiveRow();
/* eslint-enable */
if (row) {
row.scrollIntoView(false);
}
/* eslint-disable */
let cell = slc.GetActiveCell();
/* eslint-enable */
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') {
let fnB = cfg.on_before_selected_row;
cfg.on_before_selected_row = function () {
var args = arguments;
onBeforeSelection(args[0], args[1], args[2]);
if (fnB) {
fnB.call(null, args[0], args[1], args[2]);
}
};
let fnA = cfg.on_after_selected_row;
cfg.on_after_selected_row = function () {
var args = arguments;
onAfterSelection(args[0], args[1], args[2]);
if (fnA) {
fnA.call(null, args[0], args[1], args[2]);
}
};
} else {
let fnD = cfg.on_before_selected_cell;
cfg.on_before_selected_cell = function () {
var args = arguments;
onBeforeSelection(args[0], args[1], args[2]);
if (fnD) {
fnD.call(null, args[0], args[1], args[2]);
}
};
let fnC = cfg.on_after_selected_cell;
cfg.on_after_selected_cell = function () {
var args = arguments;
onAfterSelection(args[0], args[1], args[2]);
if (fnC) {
fnC.call(null, args[0], args[1], args[2]);
}
};
}
}
if (editable) {
//Added or removed rows, TF rows number needs to be re-calculated
let fnE = cfg.on_added_dom_row;
cfg.on_added_dom_row = function () {
var args = arguments;
tf.nbFilterableRows++;
if (!tf.paging) {
tf.emitter.emit('rows-changed', tf, this);
} else {
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, args[0], args[1], args[2]);
}
};
if (cfg.actions && cfg.actions['delete']) {
let fnF = cfg.actions['delete'].on_after_submit;
cfg.actions['delete'].on_after_submit = function () {
var args = arguments;
tf.nbFilterableRows--;
if (!tf.paging) {
tf.emitter.emit('rows-changed', tf, this);
} else {
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, args[0], args[1]);
}
};
}
}
try {
/* eslint-disable */
this._ezEditTable = new EditTable(tf.id, cfg, startRow);
this._ezEditTable.Init();
/* eslint-enable */
} catch (e) { throw new Error(INSTANTIATION_ERROR); }
this.initialized = true;
}
/**
* Reset advanced grid when previously removed
*/
reset() {
let ezEditTable = this._ezEditTable;
if (ezEditTable) {
if (this.cfg.selection) {
/* eslint-disable */
ezEditTable.Selection.Set();
/* eslint-enable */
}
if (this.cfg.editable) {
/* eslint-disable */
ezEditTable.Editable.Set();
/* eslint-enable */
}
}
}
/**
* Toggle behaviour
*/
toggle() {
let ezEditTable = this._ezEditTable;
if (ezEditTable.editable) {
/* eslint-disable */
ezEditTable.Editable.Remove();
/* eslint-enable */
} else {
/* eslint-disable */
ezEditTable.Editable.Set();
/* eslint-enable */
}
if (ezEditTable.selection) {
/* eslint-disable */
ezEditTable.Selection.Remove();
/* eslint-enable */
} else {
/* eslint-disable */
ezEditTable.Selection.Set();
/* eslint-enable */
}
}
_toggleForInputFilter() {
let tf = this.tf;
if (!tf.getActiveFilterId()) {
return;
}
let colIndex = tf.getColumnIndexFromFilterId(tf.getActiveFilterId());
let filterType = tf.getFilterType(colIndex);
if (filterType === INPUT) {
this.toggle();
}
}
/**
* Remove advanced grid
*/
destroy() {
if (!this.initialized) {
return;
}
let ezEditTable = this._ezEditTable;
if (ezEditTable) {
if (this.cfg.selection) {
/* eslint-disable */
ezEditTable.Selection.ClearSelections();
ezEditTable.Selection.Remove();
/* eslint-enable */
}
if (this.cfg.editable) {
/* eslint-disable */
ezEditTable.Editable.Remove();
/* eslint-enable */
}
}
this.emitter.off(['filter-focus', 'filter-blur'],
() => this._toggleForInputFilter());
this.initialized = false;
}
}
@@ -1,3 +0,0 @@
import AdapterEzEditTable from './adapterEzEditTable';
export default AdapterEzEditTable;
@@ -1,479 +0,0 @@
import {Feature} from '../../feature';
import {createText, elm} from '../../dom';
import {isArray, isEmpty, EMPTY_FN} from '../../types';
import {numSortAsc} from '../../sort';
import {FORMATTED_NUMBER} from '../../const';
import formatNumber from 'format-number';
import {defaultsFn, defaultsArr} from '../../settings';
const EVENTS = [
'after-filtering',
'after-page-change',
'after-page-length-change'
];
const SUM = 'sum';
const MEAN = 'mean';
const MIN = 'min';
const MAX = 'max';
const MEDIAN = 'median';
const Q1 = 'q1';
const Q3 = 'q3';
/**
* Column calculations extension
*/
export default class ColOps extends Feature {
/**
* Creates an instance of ColOps
*
* @param {TableFilter} tf TableFilter instance
* @param {Object} opts Configuration object
*/
constructor(tf, opts) {
super(tf, opts.name);
/**
* Callback fired before columns operations start
* @type {Function}
*/
this.onBeforeOperation = defaultsFn(opts.on_before_operation, EMPTY_FN);
/**
* Callback fired after columns operations are completed
* @type {Function}
*/
this.onAfterOperation = defaultsFn(opts.on_after_operation, EMPTY_FN);
/**
* Configuration options
* @type {Object}
*/
this.opts = opts;
/**
* List of DOM element IDs containing column's calculation result
* @type {Array}
*/
this.labelIds = defaultsArr(opts.id, []);
/**
* List of columns' indexes for calculations
* @type {Array}
*/
this.colIndexes = defaultsArr(opts.col, []);
/**
* List of operations - possible values: 'sum', 'mean', 'min', 'max',
* 'median', 'q1', 'q3'
* @type {Array}
*/
this.operations = defaultsArr(opts.operation, []);
/**
* List of write methods used to write the result - possible values:
* 'innerHTML', 'setValue', 'createTextNode'
* @type {Array}
*/
this.outputTypes = defaultsArr(opts.write_method, []);
/**
* List of format objects used for formatting the result -
* refer to https://github.com/componitable/format-number to check
* configuration options
* @type {Array}
*/
this.formatResults = defaultsArr(opts.format_result, []);
/**
* List of row indexes displaying the results
* @type {Array}
*/
this.totRowIndexes = defaultsArr(opts.tot_row_index, []);
/**
* List of row indexes excluded from calculations
* @type {Array}
*/
this.excludeRows = defaultsArr(opts.exclude_row, []);
/**
* List of decimal precision for calculation results
* @type {Array}
*/
this.decimalPrecisions = defaultsArr(opts.decimal_precision, 2);
this.enable();
}
/**
* Initializes ColOps instance
*/
init() {
if (this.initialized) {
return;
}
// subscribe to events
this.emitter.on(EVENTS, () => this.calcAll());
this.calcAll();
/** @inherited */
this.initialized = true;
}
/**
* 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.
*/
calcAll() {
let tf = this.tf;
if (!tf.isInitialized()) {
return;
}
this.onBeforeOperation(tf, this);
this.emitter.emit('before-column-operation', tf, this);
let { colIndexes, operations: colOperations, outputTypes,
totRowIndexes, excludeRows, formatResults,
decimalPrecisions } = this;
//nuovella: determine unique list of columns to operate on
let uIndexes = [];
colIndexes.forEach((val) => {
if (uIndexes.indexOf(val) === -1) {
uIndexes.push(val);
}
});
let nbCols = uIndexes.length,
rows = tf.dom().rows,
colValues = [];
for (let u = 0; u < nbCols; u++) {
//this retrieves col values
//use uIndexes because we only want to pass through this loop
//once for each column get the values in this unique column
colValues.push(
tf.getVisibleColumnData(uIndexes[u], false, excludeRows)
);
let curValues = colValues[u];
//next: calculate all operations for this column
let result = 0,
operations = [],
precisions = [],
labels = [],
writeType,
formatResult = [],
idx = 0;
for (let k = 0; k < colIndexes.length; k++) {
if (colIndexes[k] !== uIndexes[u]) {
continue;
}
operations[idx] = (colOperations[k] || 'sum').toLowerCase();
precisions[idx] = decimalPrecisions[k];
labels[idx] = this.labelIds[k];
writeType = isArray(outputTypes) ? outputTypes[k] : null;
formatResult[idx] =
this.configureFormat(uIndexes[u], formatResults[k]);
idx++;
}
for (let i = 0; i < idx; i++) {
// emit values before column calculation
this.emitter.emit(
'before-column-calc',
tf,
this,
uIndexes[u],
curValues,
operations[i],
precisions[i]
);
result = Number(this.calc(curValues, operations[i], null));
// emit column calculation result
this.emitter.emit(
'column-calc',
tf,
this,
uIndexes[u],
result,
operations[i],
precisions[i]
);
// write result in expected DOM element
this.writeResult(
result,
labels[i],
writeType,
precisions[i],
formatResult[i]
);
}//for i
// row(s) with result are always visible
let totRow = totRowIndexes && totRowIndexes[u] ?
rows[totRowIndexes[u]] : null;
if (totRow) {
totRow.style.display = '';
}
}//for u
this.onAfterOperation(tf, this);
this.emitter.emit('after-column-operation', tf, this);
}
/**
* Make desired calculation on specified column.
* @param {Number} colIndex Column index
* @param {String} [operation=SUM] Operation type
* @param {Number} precision Decimal precision
* @returns {Number}
*/
columnCalc(colIndex, operation = SUM, precision) {
let excludeRows = this.excludeRows || [];
let colValues = tf.getVisibleColumnData(colIndex, false, excludeRows);
return Number(this.calc(colValues, operation, precision));
}
/**
* Make calculation on passed values.
* @param {Array} values List of values
* @param {String} [operation=SUM] Optional operation type
* @param {Number} precision Optional result precision
* @returns {Number}
* @private
*/
calc(colValues, operation = SUM, precision) {
let result = 0;
if (operation === Q1 || operation === Q3 || operation === MEDIAN) {
colValues = this.sortColumnValues(colValues, numSortAsc);
}
switch (operation) {
case MEAN:
result = this.calcMean(colValues);
break;
case SUM:
result = this.calcSum(colValues);
break;
case MIN:
result = this.calcMin(colValues);
break;
case MAX:
result = this.calcMax(colValues);
break;
case MEDIAN:
result = this.calcMedian(colValues);
break;
case Q1:
result = this.calcQ1(colValues);
break;
case Q3:
result = this.calcQ3(colValues);
break;
}
return isEmpty(precision) ? result : result.toFixed(precision);
}
/**
* Calculate the sum of passed values.
* @param {Array} [values=[]] List of values
* @returns {Number}
*/
calcSum(values = []) {
if (isEmpty(values)) {
return 0;
}
let result = values.reduce((x, y) => Number(x) + Number(y));
return result;
}
/**
* Calculate the mean of passed values.
* @param {Array} [values=[]] List of values
* @returns {Number}
*/
calcMean(values = []) {
let result = this.calcSum(values) / values.length;
return Number(result);
}
/**
* Calculate the max value of passed values.
* @param {Array} [values=[]] List of values
* @returns {Number}
*/
calcMax(values = []) {
return Math.max.apply(null, values);
}
/**
* Calculate the min value of passed values.
* @param {Array} [values=[]] List of values
* @returns {Number}
*/
calcMin(values = []) {
return Math.min.apply(null, values);
}
/**
* Calculate the median of passed values.
* @param {Array} [values=[]] List of values
* @returns {Number}
*/
calcMedian(values = []) {
let nbValues = values.length;
let aux = 0;
if (nbValues % 2 === 1) {
aux = Math.floor(nbValues / 2);
return Number(values[aux]);
}
return (Number(values[nbValues / 2]) +
Number(values[((nbValues / 2) - 1)])) / 2;
}
/**
* Calculate the lower quartile of passed values.
* @param {Array} [values=[]] List of values
* @returns {Number}
*/
calcQ1(values = []) {
let nbValues = values.length;
let posa = 0.0;
posa = Math.floor(nbValues / 4);
if (4 * posa === nbValues) {
return (Number(values[posa - 1]) +
Number(values[posa])) / 2;
}
return Number(values[posa]);
}
/**
* Calculate the upper quartile of passed values.
* @param {Array} [values=[]] List of values
* @returns {Number}
*/
calcQ3(values = []) {
let nbValues = values.length;
let posa = 0.0;
let posb = 0.0;
posa = Math.floor(nbValues / 4);
if (4 * posa === nbValues) {
posb = 3 * posa;
return (Number(values[posb]) +
Number(values[posb - 1])) / 2;
}
return Number(values[nbValues - posa - 1]);
}
/**
* Sort passed values with supplied sorter function.
* @param {Array} [values=[]] List of values to be sorted
* @param {Function} sorter Sorter function
* @returns {Array}
*/
sortColumnValues(values = [], sorter) {
return values.sort(sorter);
}
/**
* Write calculation result in passed DOM element with supplied write method
* and decimal precision.
* @param {Number} [result=0] Calculation result
* @param {DOMElement} label DOM element
* @param {String} [writeType='innerhtml'] Write method
* @param {Number} [precision=2] Applied decimal precision
* @private
*/
writeResult(result = 0, label, writeType = 'innerhtml',
precision = 2, format = {}) {
let labelElm = elm(label);
if (!labelElm) {
return;
}
result = result.toFixed(precision);
if (isNaN(result) || !isFinite(result)) {
result = '';
} else {
result = formatNumber(format)(result);
}
switch (writeType.toLowerCase()) {
case 'innerhtml':
labelElm.innerHTML = result;
break;
case 'setvalue':
labelElm.value = result;
break;
case 'createtextnode':
let oldNode = labelElm.firstChild;
let txtNode = createText(result);
labelElm.replaceChild(txtNode, oldNode);
break;
}
}
/**
* Configure the format options used to format the operation result based
* on column type.
* @param {Number} colIndex Column index
* @param {Object} [format={}] Format object
* @returns {Object}
* @private
*/
configureFormat(colIndex, format = {}) {
let tf = this.tf;
if (tf.hasType(colIndex, [FORMATTED_NUMBER])) {
let colType = tf.colTypes[colIndex];
if (colType.decimal && !format.decimal) {
format.decimal = colType.decimal;
}
if (colType.thousands && !format.integerSeparator) {
format.integerSeparator = colType.thousands;
}
} else {
format.decimal = format.decimal || '';
format.integerSeparator = format.integerSeparator || '';
}
return format;
}
/** Remove extension */
destroy() {
if (!this.initialized) {
return;
}
// unsubscribe to events
this.emitter.off(EVENTS, () => this.calcAll());
this.initialized = false;
}
}
@@ -1,706 +0,0 @@
import {Feature} from '../../feature';
import {
addClass, removeClass, createCheckItem, createElm, elm, removeElm,
getText
} from '../../dom';
import {isUndef, EMPTY_FN} from '../../types';
import {addEvt, targetEvt, removeEvt} from '../../event';
import {root} from '../../root';
import {NONE} from '../../const';
import {
defaultsBool, defaultsStr, defaultsFn,
defaultsNb, defaultsArr
} from '../../settings';
/**
* Columns Visibility extension
*/
export default class ColsVisibility extends Feature {
/**
* Creates an instance of ColsVisibility
* @param {TableFilter} tf TableFilter instance
* @param {Object} Configuration object
*/
constructor(tf, f) {
super(tf, f.name);
// Configuration object
let cfg = this.config;
/**
* Module name
* @type {String}
*/
this.name = f.name;
/**
* Module description
* @type {String}
*/
this.desc = defaultsStr(f.description, 'Columns visibility manager');
/**
* show/hide columns container element
* @private
*/
this.spanEl = null;
/**
* show/hide columns button element
* @private
*/
this.btnEl = null;
/**
* show/hide columns main container element
* @private
*/
this.contEl = null;
/**
* Enable tick to hide a column, defaults to true
* @type {Boolean}
*/
this.tickToHide = defaultsBool(f.tick_to_hide, true);
/**
* Enable columns manager UI, defaults to true
* @type {Boolean}
*/
this.manager = defaultsBool(f.manager, true);
/**
* Headers HTML table reference only if headers are external
* @type {DOMElement}
*/
this.headersTbl = f.headers_table || null;
/**
* Headers row index only if headers are external
* @type {Number}
*/
this.headersIndex = defaultsNb(f.headers_index, 1);
/**
* ID of main container element
* @type {String}
*/
this.contElTgtId = defaultsStr(f.container_target_id, null);
/**
* Alternative text for column headers in column manager UI
* @type {Array}
*/
this.headersText = defaultsArr(f.headers_text, []);
/**
* ID of button's container element
* @type {String}
*/
this.btnTgtId = defaultsStr(f.btn_target_id, null);
/**
* Button's text, defaults to Columns&#9660;
* @type {String}
*/
this.btnText = defaultsStr(f.btn_text, 'Columns&#9660;');
/**
* Button's inner HTML
* @type {String}
*/
this.btnHtml = defaultsStr(f.btn_html, null);
/**
* Css class for button
* @type {String}
*/
this.btnCssClass = defaultsStr(f.btn_css_class, 'colVis');
/**
* Columns manager UI close link text, defaults to 'Close'
* @type {String}
*/
this.btnCloseText = defaultsStr(f.btn_close_text, 'Close');
/**
* Columns manager UI close link HTML
* @type {String}
*/
this.btnCloseHtml = defaultsStr(f.btn_close_html, null);
/**
* Css for columns manager UI close link
* @type {String}
*/
this.btnCloseCssClass = defaultsStr(f.btn_close_css_class,
this.btnCssClass);
/**
* Extension's stylesheet filename
* @type {String}
*/
this.stylesheet = defaultsStr(f.stylesheet, 'colsVisibility.css');
/**
* Css for columns manager UI span
* @type {String}
*/
this.spanCssClass = defaultsStr(f.span_css_class, 'colVisSpan');
/**
* Css for columns manager UI main container
* @type {String}
*/
this.contCssClass = defaultsStr(f.cont_css_class, 'colVisCont');
/**
* Css for columns manager UI checklist (ul)
* @type {String}
*/
this.listCssClass = defaultsStr(cfg.list_css_class, 'cols_checklist');
/**
* Css for columns manager UI checklist item (li)
* @type {String}
*/
this.listItemCssClass = defaultsStr(cfg.checklist_item_css_class,
'cols_checklist_item');
/**
* Css for columns manager UI checklist item selected state (li)
* @type {String}
*/
this.listSlcItemCssClass = defaultsStr(
cfg.checklist_selected_item_css_class,
'cols_checklist_slc_item'
);
/**
* Text preceding the columns list, defaults to 'Hide' or 'Show'
* depending on tick mode (tick_to_hide option)
* @type {String}
*/
this.text = defaultsStr(f.text, this.tickToHide ? 'Hide: ' : 'Show: ');
/**
* List of columns indexes to be hidden at initialization
* @type {Array}
*/
this.atStart = defaultsArr(f.at_start, []);
/**
* Enable hover behaviour on columns manager button/link
* @type {Boolean}
*/
this.enableHover = Boolean(f.enable_hover);
/**
* Enable select all option, disabled by default
* @type {Boolean}
*/
this.enableTickAll = Boolean(f.enable_tick_all);
/**
* Text for select all option, defaults to 'Select all:'
* @type {String}
*/
this.tickAllText = defaultsStr(f.tick_all_text, 'Select all:');
/**
* List of indexes of hidden columns
* @private
*/
this.hiddenCols = [];
/**
* Bound mouseup wrapper
* @private
*/
this.boundMouseup = null;
/**
* Callback fired when the extension is initialized
* @type {Function}
*/
this.onLoaded = defaultsFn(f.on_loaded, EMPTY_FN);
/**
* Callback fired before the columns manager is opened
* @type {Function}
*/
this.onBeforeOpen = defaultsFn(f.on_before_open, EMPTY_FN);
/**
* Callback fired after the columns manager is opened
* @type {Function}
*/
this.onAfterOpen = defaultsFn(f.on_after_open, EMPTY_FN);
/**
* Callback fired before the columns manager is closed
* @type {Function}
*/
this.onBeforeClose = defaultsFn(f.on_before_close, EMPTY_FN);
/**
* Callback fired after the columns manager is closed
* @type {Function}
*/
this.onAfterClose = defaultsFn(f.on_after_close, EMPTY_FN);
/**
* Callback fired before a column is hidden
* @type {Function}
*/
this.onBeforeColHidden = defaultsFn(f.on_before_col_hidden, EMPTY_FN);
/**
* Callback fired after a column is hidden
* @type {Function}
*/
this.onAfterColHidden = defaultsFn(f.on_after_col_hidden, EMPTY_FN);
/**
* Callback fired before a column is displayed
* @type {Function}
*/
this.onBeforeColDisplayed = defaultsFn(f.on_before_col_displayed,
EMPTY_FN);
/**
* Callback fired after a column is displayed
* @type {Function}
*/
this.onAfterColDisplayed = defaultsFn(f.on_after_col_displayed,
EMPTY_FN);
//Grid layout support
if (tf.gridLayout) {
this.headersTbl = tf.feature('gridLayout').headTbl; //headers table
this.headersIndex = 0; //headers index
}
//Loads extension stylesheet
tf.import(f.name + 'Style', tf.getStylePath() + this.stylesheet, null,
'link');
this.enable();
}
/**
* Mouse-up event handler handling popup auto-close behaviour
* @private
*/
onMouseup(evt) {
let targetElm = targetEvt(evt);
while (targetElm && targetElm !== this.contEl
&& targetElm !== this.btnEl) {
targetElm = targetElm.parentNode;
}
if (targetElm !== this.contEl && targetElm !== this.btnEl) {
this.toggle();
}
return;
}
/**
* Toggle columns manager UI
*/
toggle() {
// ensure mouseup event handler is removed
removeEvt(root, 'mouseup', this.boundMouseup);
let contDisplay = this.contEl.style.display;
if (contDisplay !== 'inline') {
this.onBeforeOpen(this);
}
if (contDisplay === 'inline') {
this.onBeforeClose(this);
}
this.contEl.style.display = contDisplay === 'inline' ?
NONE : 'inline';
if (contDisplay !== 'inline') {
this.onAfterOpen(this);
addEvt(root, 'mouseup', this.boundMouseup);
}
if (contDisplay === 'inline') {
this.onAfterClose(this);
}
}
/**
* Check an item in columns manager UI
* @private
*/
checkItem(lbl) {
let li = lbl.parentNode;
if (!li || !lbl) {
return;
}
let isChecked = lbl.firstChild.checked;
let colIndex = lbl.firstChild.getAttribute('id').split('_')[1];
colIndex = parseInt(colIndex, 10);
if (isChecked) {
addClass(li, this.listSlcItemCssClass);
} else {
removeClass(li, this.listSlcItemCssClass);
}
let hide = false;
if ((this.tickToHide && isChecked) ||
(!this.tickToHide && !isChecked)) {
hide = true;
}
this.setHidden(colIndex, hide);
}
/**
* Initializes ColsVisibility instance
*/
init() {
if (this.initialized || !this.manager) {
return;
}
this.emitter.on(['hide-column'],
(tf, colIndex) => this.hideCol(colIndex));
this.buildBtn();
this.buildManager();
/** @inherited */
this.initialized = true;
this.boundMouseup = this.onMouseup.bind(this);
this.emitter.emit('columns-visibility-initialized', this.tf, this);
// Hide columns at start at very end of initialization, do not move
// as order is important
this._hideAtStart();
}
/**
* Build main button UI
*/
buildBtn() {
if (this.btnEl) {
return;
}
let tf = this.tf;
let span = createElm('span');
span.className = this.spanCssClass;
//Container element (rdiv or custom element)
if (!this.btnTgtId) {
tf.setToolbar();
}
let targetEl = !this.btnTgtId ? tf.rDiv : elm(this.btnTgtId);
if (!this.btnTgtId) {
let firstChild = targetEl.firstChild;
firstChild.parentNode.insertBefore(span, firstChild);
} else {
targetEl.appendChild(span);
}
if (!this.btnHtml) {
let btn = createElm('a', ['href', 'javascript:;']);
btn.className = this.btnCssClass;
btn.title = this.desc;
btn.innerHTML = this.btnText;
span.appendChild(btn);
if (!this.enableHover) {
addEvt(btn, 'click', (evt) => this.toggle(evt));
} else {
addEvt(btn, 'mouseover', (evt) => this.toggle(evt));
}
} else { //Custom html
span.innerHTML = this.btnHtml;
let colVisEl = span.firstChild;
if (!this.enableHover) {
addEvt(colVisEl, 'click', (evt) => this.toggle(evt));
} else {
addEvt(colVisEl, 'mouseover', (evt) => this.toggle(evt));
}
}
this.spanEl = span;
this.btnEl = this.spanEl.firstChild;
this.onLoaded(this);
}
/**
* Build columns manager UI
*/
buildManager() {
let tf = this.tf;
let container = !this.contElTgtId ?
createElm('div') :
elm(this.contElTgtId);
container.className = this.contCssClass;
//Extension description
let extNameLabel = createElm('p');
extNameLabel.innerHTML = this.text;
container.appendChild(extNameLabel);
//Headers list
let ul = createElm('ul');
ul.className = this.listCssClass;
let tbl = this.headersTbl || tf.dom();
let headerIndex = this.headersTbl ?
this.headersIndex : tf.getHeadersRowIndex();
let headerRow = tbl.rows[headerIndex];
//Tick all option
if (this.enableTickAll) {
let li = createCheckItem('col__' + tf.id, this.tickAllText,
this.tickAllText);
addClass(li, this.listItemCssClass);
ul.appendChild(li);
li.check.checked = !this.tickToHide;
addEvt(li.check, 'click', () => {
for (let h = 0; h < headerRow.cells.length; h++) {
let itm = elm('col_' + h + '_' + tf.id);
if (itm && li.check.checked !== itm.checked) {
itm.click();
itm.checked = li.check.checked;
}
}
});
}
for (let i = 0; i < headerRow.cells.length; i++) {
let cell = headerRow.cells[i];
let cellText = this.headersText[i] || this._getHeaderText(cell);
let liElm = createCheckItem('col_' + i + '_' + tf.id, cellText,
cellText);
addClass(liElm, this.listItemCssClass);
if (!this.tickToHide) {
addClass(liElm, this.listSlcItemCssClass);
}
ul.appendChild(liElm);
if (!this.tickToHide) {
liElm.check.checked = true;
}
addEvt(liElm.check, 'click', (evt) => {
let elm = targetEvt(evt);
let lbl = elm.parentNode;
this.checkItem(lbl);
});
}
//separator
let p = createElm('p', ['align', 'center']);
let btn;
//Close link
if (!this.btnCloseHtml) {
btn = createElm('a', ['href', 'javascript:;']);
btn.className = this.btnCloseCssClass;
btn.innerHTML = this.btnCloseText;
addEvt(btn, 'click', (evt) => this.toggle(evt));
p.appendChild(btn);
} else {
p.innerHTML = this.btnCloseHtml;
btn = p.firstChild;
addEvt(btn, 'click', (evt) => this.toggle(evt));
}
container.appendChild(ul);
container.appendChild(p);
this.btnEl.parentNode.insertBefore(container, this.btnEl);
this.contEl = container;
}
/**
* Hide or show specified columns
* @param {Number} colIndex Column index
* @param {Boolean} hide Hide column if true or show if false
*/
setHidden(colIndex, hide) {
let tf = this.tf;
let tbl = tf.dom();
if (hide) {
this.onBeforeColHidden(this, colIndex);
}
if (!hide) {
this.onBeforeColDisplayed(this, colIndex);
}
this._hideCells(tbl, colIndex, hide);
if (this.headersTbl) {
this._hideCells(this.headersTbl, colIndex, hide);
}
let hiddenCols = this.hiddenCols;
let itemIndex = hiddenCols.indexOf(colIndex);
if (hide) {
if (itemIndex === -1) {
this.hiddenCols.push(colIndex);
}
} else {
if (itemIndex !== -1) {
this.hiddenCols.splice(itemIndex, 1);
}
}
if (hide) {
this.onAfterColHidden(this, colIndex);
this.emitter.emit('column-hidden', tf, this, colIndex,
this.hiddenCols);
}
if (!hide) {
this.onAfterColDisplayed(this, colIndex);
this.emitter.emit('column-shown', tf, this, colIndex,
this.hiddenCols);
}
}
/**
* Show specified column
* @param {Number} colIndex Column index
*/
showCol(colIndex) {
if (isUndef(colIndex) || !this.isColHidden(colIndex)) {
return;
}
if (this.manager && this.contEl) {
let itm = elm('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 (isUndef(colIndex) || this.isColHidden(colIndex)) {
return;
}
if (this.manager && this.contEl) {
let itm = elm('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 (isUndef(colIndex) || this.isColHidden(colIndex)) {
this.showCol(colIndex);
} else {
this.hideCol(colIndex);
}
}
/**
* Return the indexes of the columns currently hidden
* @return {Array} column indexes
*/
getHiddenCols() {
return this.hiddenCols;
}
/**
* Remove the columns manager
*/
destroy() {
if (!this.initialized) {
return;
}
if (elm(this.contElTgtId)) {
elm(this.contElTgtId).innerHTML = '';
} else {
this.contEl.innerHTML = '';
removeElm(this.contEl);
this.contEl = null;
}
this.btnEl.innerHTML = '';
removeElm(this.btnEl);
this.btnEl = null;
this.emitter.off(['hide-column'],
(tf, colIndex) => this.hideCol(colIndex));
this.boundMouseup = null;
this.initialized = false;
}
_getHeaderText(cell) {
if (!cell.hasChildNodes) {
return '';
}
for (let i = 0; i < cell.childNodes.length; i++) {
let 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 getText(n);
}
}
continue;
}
return '';
}
_hideCells(tbl, colIndex, hide) {
for (let i = 0; i < tbl.rows.length; i++) {
let row = tbl.rows[i];
let cell = row.cells[colIndex];
if (cell) {
cell.style.display = hide ? NONE : '';
}
}
}
_hideAtStart() {
this.atStart.forEach((colIdx) => {
this.hideCol(colIdx);
});
}
}
@@ -1,308 +0,0 @@
import {Feature} from '../../feature';
import {createElm, removeElm, elm} from '../../dom';
import {EMPTY_FN} from '../../types';
import {addEvt} from '../../event';
import {
defaultsBool, defaultsStr, defaultsFn, defaultsNb,
} from '../../settings';
/**
* Filters Visibility extension
*/
export default class FiltersVisibility extends Feature {
/**
* Creates an instance of FiltersVisibility
* @param {TableFilter} tf TableFilter instance
* @param {Object} Configuration object
*/
constructor(tf, f) {
super(tf, f.name);
/**
* Module name
* @type {String}
*/
this.name = f.name;
/**
* Module description
* @type {String}
*/
this.desc = defaultsStr(f.description,
'Filters row visibility manager');
/**
* Extension's stylesheet filename
* @type {String}
*/
this.stylesheet = defaultsStr(f.stylesheet , 'filtersVisibility.css');
/**
* Expand icon filename
* @type {String}
*/
this.icnExpand = defaultsStr(f.expand_icon_name, 'icn_exp.png');
/**
* Collapse icon filename
* @type {String}
*/
this.icnCollapse = defaultsStr(f.collapse_icon_name, 'icn_clp.png');
/**
* Main container element
* @private
*/
this.contEl = null;
/**
* Button element
* @private
*/
this.btnEl = null;
/**
* Expand icon HTML
* @private
*/
this.icnExpandHtml = '<img src="' + tf.themesPath + this.icnExpand +
'" alt="Expand filters" >';
/**
* Collapse icon HTML
* @private
*/
this.icnCollapseHtml = '<img src="' + tf.themesPath + this.icnCollapse +
'" alt="Collapse filters" >';
/**
* Default text
* @private
*/
this.defaultText = 'Toggle filters';
/**
* ID of main container element
* @type {String}
*/
this.targetId = f.target_id || null;
/**
* Enable expand/collapse icon, defaults to true
* @type {Boolean}
*/
this.enableIcon = defaultsBool(f.enable_icon, true);
/**
* Custom text for button
* @type {String}
*/
this.btnText = defaultsStr(f.btn_text, '');
/**
* Collapse button HTML
* @private
*/
this.collapseBtnHtml = this.enableIcon ?
this.icnCollapseHtml + this.btnText :
this.btnText || this.defaultText;
/**
* Expand button HTML
* @private
*/
this.expandBtnHtml = this.enableIcon ?
this.icnExpandHtml + this.btnText :
this.btnText || this.defaultText;
/**
* Button's custom HTML
* @type {String}
*/
this.btnHtml = defaultsStr(f.btn_html, null);
/**
* Css class for expand/collapse filters button
* @type {String}
*/
this.btnCssClass = defaultsStr(f.btn_css_class, 'btnExpClpFlt');
/**
* Css class for main container
* @type {String}
*/
this.contCssClass = defaultsStr(f.cont_css_class, 'expClpFlt');
/**
* Filters row index
* @type {Number}
*/
this.filtersRowIndex = defaultsNb(f.filters_row_index,
tf.getFiltersRowIndex());
/**
* Make filters visible at initialization, defaults to true
* @type {Boolean}
*/
this.visibleAtStart = defaultsNb(f.visible_at_start, true);
/**
* Callback fired before filters row is shown
* @type {Function}
*/
this.onBeforeShow = defaultsFn(f.on_before_show, EMPTY_FN);
/**
* Callback fired after filters row is shown
* @type {Function}
*/
this.onAfterShow = defaultsFn(f.on_after_show, EMPTY_FN);
/**
* Callback fired before filters row is hidden
* @type {Function}
*/
this.onBeforeHide = defaultsFn(f.on_before_hide, EMPTY_FN);
/**
* Callback fired after filters row is hidden
* @type {Function}
*/
this.onAfterHide = defaultsFn(f.on_after_hide, EMPTY_FN);
//Import extension's stylesheet
tf.import(f.name + 'Style', tf.getStylePath() + this.stylesheet, null,
'link');
this.enable();
}
/**
* Initialise extension
*/
init() {
if (this.initialized) {
return;
}
this.buildUI();
/**
* @inherited
*/
this.initialized = true;
this.emitter.on(['show-filters'], (tf, visible) => this.show(visible));
this.emitter.emit('filters-visibility-initialized', this.tf, this);
}
/**
* Build UI elements
*/
buildUI() {
let tf = this.tf;
let span = createElm('span');
span.className = this.contCssClass;
//Container element (rdiv or custom element)
if (!this.targetId) {
tf.setToolbar();
}
let targetEl = !this.targetId ? tf.rDiv : elm(this.targetId);
if (!this.targetId) {
let firstChild = targetEl.firstChild;
firstChild.parentNode.insertBefore(span, firstChild);
} else {
targetEl.appendChild(span);
}
let btn;
if (!this.btnHtml) {
btn = createElm('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;
}
addEvt(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.dom();
let fltRow = tbl.rows[this.filtersRowIndex];
let isDisplayed = fltRow.style.display === '';
this.show(!isDisplayed);
}
/**
* Show or hide filters
*
* @param {boolean} [visible=true] Visibility flag
*/
show(visible = true) {
let tf = this.tf;
let tbl = tf.gridLayout ? tf.feature('gridLayout').headTbl : tf.dom();
let fltRow = tbl.rows[this.filtersRowIndex];
if (visible) {
this.onBeforeShow(this);
}
if (!visible) {
this.onBeforeHide(this);
}
fltRow.style.display = visible ? '' : 'none';
if (this.enableIcon && !this.btnHtml) {
this.btnEl.innerHTML = visible ?
this.collapseBtnHtml : this.expandBtnHtml;
}
if (visible) {
this.onAfterShow(this);
}
if (!visible) {
this.onAfterHide(this);
}
this.emitter.emit('filters-toggled', tf, this, visible);
}
/**
* Destroy the UI
*/
destroy() {
if (!this.initialized) {
return;
}
this.emitter.off(['show-filters'], (tf, visible) => this.show(visible));
this.btnEl.innerHTML = '';
removeElm(this.btnEl);
this.btnEl = null;
this.contEl.innerHTML = '';
removeElm(this.contEl);
this.contEl = null;
this.initialized = false;
}
}
@@ -1,531 +0,0 @@
import {Feature} from '../../feature';
import {isUndef, isObj, EMPTY_FN} from '../../types';
import {createElm, elm, getText, tag} from '../../dom';
import {addEvt} from '../../event';
import {parse as parseNb} from '../../number';
import {
NONE, CELL_TAG, HEADER_TAG, STRING, NUMBER, DATE, FORMATTED_NUMBER,
IP_ADDRESS
} from '../../const';
import {defaultsStr, defaultsFn, defaultsArr} from '../../settings';
/**
* SortableTable Adapter module
*/
export default class AdapterSortableTable extends Feature {
/**
* Creates an instance of AdapterSortableTable
* @param {TableFilter} tf TableFilter instance
* @param {Object} opts Configuration object
*/
constructor(tf, opts) {
super(tf, opts.name);
/**
* Module name
* @type {String}
*/
this.name = opts.name;
/**
* Module description
* @type {String}
*/
this.desc = defaultsStr(opts.description, 'Sortable table');
/**
* Indicate whether table previously sorted
* @type {Boolean}
* @private
*/
this.sorted = false;
/**
* List of sort type per column basis
* @type {Array}
*/
this.sortTypes = defaultsArr(opts.types, tf.colTypes);
/**
* Column to be sorted at initialization, ie:
* sort_col_at_start: [1, true]
* @type {Array}
*/
this.sortColAtStart = defaultsArr(opts.sort_col_at_start, null);
/**
* Enable asynchronous sort, if triggers are external
* @type {Boolean}
*/
this.asyncSort = Boolean(opts.async_sort);
/**
* List of element IDs triggering sort on a per column basis
* @type {Array}
*/
this.triggerIds = defaultsArr(opts.trigger_ids, []);
// edit .sort-arrow.descending / .sort-arrow.ascending in
// tablefilter.css to reflect any path change
/**
* Path to images
* @type {String}
*/
this.imgPath = defaultsStr(opts.images_path, tf.themesPath);
/**
* Blank image file name
* @type {String}
*/
this.imgBlank = defaultsStr(opts.image_blank, 'blank.png');
/**
* Css class for sort indicator image
* @type {String}
*/
this.imgClassName = defaultsStr(opts.image_class_name, 'sort-arrow');
/**
* Css class for ascending sort indicator image
* @type {String}
*/
this.imgAscClassName = defaultsStr(opts.image_asc_class_name,
'ascending');
/**
* Css class for descending sort indicator image
* @type {String}
*/
this.imgDescClassName = defaultsStr(opts.image_desc_class_name,
'descending');
/**
* Cell attribute key storing custom value used for sorting
* @type {String}
*/
this.customKey = defaultsStr(opts.custom_key, 'data-tf-sortKey');
/**
* Callback fired when sort extension is instanciated
* @type {Function}
*/
this.onSortLoaded = defaultsFn(opts.on_sort_loaded, EMPTY_FN);
/**
* Callback fired before a table column is sorted
* @type {Function}
*/
this.onBeforeSort = defaultsFn(opts.on_before_sort, EMPTY_FN);
/**
* Callback fired after a table column is sorted
* @type {Function}
*/
this.onAfterSort = defaultsFn(opts.on_after_sort, EMPTY_FN);
/**
* SortableTable instance
* @private
*/
this.stt = null;
this.enable();
}
/**
* Initializes AdapterSortableTable instance
*/
init() {
if (this.initialized) {
return;
}
let tf = this.tf;
let adpt = this;
// SortableTable class sanity check (sortabletable.js)
if (isUndef(SortableTable)) {
throw new Error('SortableTable class not found.');
}
// Add any date format if needed
this.emitter.emit('add-date-type-formats', this.tf, this.sortTypes);
this.overrideSortableTable();
this.setSortTypes();
this.onSortLoaded(tf, this);
/*** SortableTable callbacks ***/
this.stt.onbeforesort = function () {
adpt.onBeforeSort(tf, adpt.stt.sortColumn);
/*** sort behaviour for paging ***/
if (tf.paging) {
tf.feature('paging').disable();
}
};
this.stt.onsort = function () {
adpt.sorted = true;
//sort behaviour for paging
if (tf.paging) {
let paginator = tf.feature('paging');
// recalculate valid rows index as sorting may have change it
tf.getValidRows(true);
paginator.enable();
paginator.setPage(paginator.getPage());
}
adpt.onAfterSort(tf, adpt.stt.sortColumn, adpt.stt.descending);
adpt.emitter.emit('column-sorted', tf, adpt.stt.sortColumn,
adpt.stt.descending);
};
// Column sort at start
let sortColAtStart = adpt.sortColAtStart;
if (sortColAtStart) {
this.stt.sort(sortColAtStart[0], sortColAtStart[1]);
}
this.emitter.on(['sort'],
(tf, colIdx, desc) => this.sortByColumnIndex(colIdx, desc));
/** @inherited */
this.initialized = true;
this.emitter.emit('sort-initialized', tf, this);
}
/**
* Sort specified column
* @param {Number} colIdx Column index
* @param {Boolean} desc Optional: descending manner
*/
sortByColumnIndex(colIdx, desc) {
this.stt.sort(colIdx, desc);
}
/**
* Set SortableTable overrides for TableFilter integration
*/
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 !== CELL_TAG && el.tagName !== HEADER_TAG) {
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 = createElm('img',
['src', adpt.imgPath + adpt.imgBlank]);
c.appendChild(img);
if (stt.sortTypes[i] !== null) {
c.setAttribute('_sortType', stt.sortTypes[i]);
}
addEvt(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 < l; j++) {
cells.push(elm(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 cell = cells[i];
if (!cell) {
continue;
}
let cellAttr = cell.getAttribute('_sortType');
if (cellAttr !== null && cellAttr !== 'None') {
img = cell.lastChild || cell;
if (img.nodeName.toLowerCase() !== 'img') {
img = createElm('img',
['src', adpt.imgPath + adpt.imgBlank]);
cell.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 getText(oNode);
}
};
}
/**
* Adds a sort type
*/
addSortType(...args) {
// Extract the arguments
let [id, caster, sorter] = args;
SortableTable.prototype.addSortType(id, caster, sorter);
}
/**
* Sets the sort types on a column basis
* @private
*/
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];
if (isObj(colType)) {
if (colType.type === DATE) {
colType = this._addDateType(i, sortTypes);
}
else if (colType.type === FORMATTED_NUMBER) {
let decimal = colType.decimal || tf.decimalSeparator;
colType = this._addNumberType(i, decimal);
}
} else {
colType = colType.toLowerCase();
if (colType === DATE) {
colType = this._addDateType(i, sortTypes);
}
else if (colType === FORMATTED_NUMBER ||
colType === NUMBER) {
colType = this._addNumberType(i, tf.decimalSeparator);
}
else if (colType === NONE) {
// TODO: normalise 'none' vs 'None'
colType = 'None';
}
}
} else {
colType = STRING;
}
_sortTypes.push(colType);
}
//Public TF method to add sort type
//Custom sort types
this.addSortType('caseinsensitivestring', SortableTable.toUpperCase);
this.addSortType(STRING);
this.addSortType(IP_ADDRESS, ipAddress, sortIP);
this.stt = new SortableTable(tf.dom(), _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 = elm(triggers[j]);
if (trigger) {
trigger.style.cursor = 'pointer';
addEvt(trigger, 'click', (evt) => {
let elm = evt.target;
if (!this.tf.sort) {
return;
}
this.stt.asyncSort(triggers.indexOf(elm.id));
});
trigger.setAttribute('_sortType', _sortTypes[j]);
}
}
}
}
_addDateType(colIndex, types) {
let tf = this.tf;
let dateType = tf.feature('dateType');
let locale = dateType.getOptions(colIndex, types).locale || tf.locale;
let colType = `${DATE}-${locale}`;
this.addSortType(colType, (value) => {
let parsedDate = dateType.parse(value, locale);
// Invalid date defaults to Wed Feb 04 -768 11:00:00
return isNaN(+parsedDate) ? new Date(-86400000000000) : parsedDate;
});
return colType;
}
_addNumberType(colIndex, decimal) {
let colType = `${FORMATTED_NUMBER}${decimal === '.' ? '' : '-custom'}`;
this.addSortType(colType, (value) => {
return parseNb(value, decimal);
});
return colType;
}
/**
* Remove extension
*/
destroy() {
if (!this.initialized) {
return;
}
let tf = this.tf;
this.emitter.off(['sort'],
(tf, colIdx, desc) => this.sortByColumnIndex(colIdx, desc));
this.sorted = false;
this.stt.destroy();
let ids = tf.getFiltersId();
for (let idx = 0; idx < ids.length; idx++) {
let header = tf.getHeaderElement(idx);
let img = tag(header, 'img');
if (img.length === 1) {
header.removeChild(img[0]);
}
}
this.initialized = false;
}
}
//Converters
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;
}
}
@@ -1,8 +0,0 @@
import AdapterSortableTable from './adapterSortabletable';
import {root} from '../../root';
if (!root.SortableTable) {
require('script-loader!sortabletable');
}
export default AdapterSortableTable;
-97
View File
@@ -1,97 +0,0 @@
const NOT_IMPLEMENTED = 'Not implemented.';
/**
* Base class defining the interface of a TableFilter feature
*/
export class Feature {
/**
* Creates an instance of Feature
* @param {Object} tf TableFilter instance
* @param {String} feature Feature name known by TableFilter
*/
constructor(tf, feature) {
/**
* TableFilter instance
* @type {TableFilter}
*/
this.tf = tf;
/**
* Feature name
* @type {String}
*/
this.feature = feature;
/**
* TableFilter feature setting
* @type {Boolean}
*/
this.enabled = tf[feature];
/**
* TableFilter configuration
* @type {Object}
*/
this.config = tf.config();
/**
* TableFilter emitter instance
* @type {Emitter}
*/
this.emitter = tf.emitter;
/**
* Field indicating whether Feature is initialized
* @type {Boolean}
*/
this.initialized = false;
/** Subscribe to destroy event */
this.emitter.on(['destroy'], () => this.destroy());
}
/**
* Initialize the feature
*/
init() {
throw new Error(NOT_IMPLEMENTED);
}
/**
* Reset the feature after being disabled
*/
reset() {
this.enable();
this.init();
}
/**
* Destroy the feature
*/
destroy() {
throw new Error(NOT_IMPLEMENTED);
}
/**
* Enable the feature
*/
enable() {
this.enabled = true;
}
/**
* Disable the feature
*/
disable() {
this.enabled = false;
}
/**
* Indicate whether the feature is enabled or not
* @returns {Boolean}
*/
isEnabled() {
return this.enabled === true;
}
}
@@ -1,141 +0,0 @@
import {Feature} from '../feature';
import {addClass, removeClass} from '../dom';
import {defaultsStr} from '../settings';
/**
* Rows with alternating background color for improved readability
*/
export class AlternateRows extends Feature {
/**
* Creates an instance of AlternateRows.
*
* @param {Object} tf TableFilter instance
*/
constructor(tf) {
super(tf, 'alternateRows');
let config = this.config;
/**
* Css class for even rows (default: 'even')
* @type {String}
*/
this.evenCss = defaultsStr(config.even_row_css_class, 'even');
/**
* Css class for odd rows (default: 'odd')
* @type {String}
*/
this.oddCss = defaultsStr(config.odd_row_css_class, 'odd');
}
/**
* Sets alternating rows color
*/
init() {
if (this.initialized) {
return;
}
this.processAll();
// Subscribe to events
this.emitter.on(['row-processed', 'row-paged'],
(tf, rowIndex, arrIndex, isValid) =>
this.processRow(rowIndex, arrIndex, isValid));
this.emitter.on(['column-sorted', 'rows-changed'],
() => this.processAll());
/** @inherited */
this.initialized = true;
}
/**
* Apply background to all valid rows
*/
processAll() {
if (!this.isEnabled()) {
return;
}
let tf = this.tf;
let validRowsIndex = tf.getValidRows(true);
let indexLen = validRowsIndex.length;
let idx = 0;
//alternates bg color
for (let j = 0; j < indexLen; j++) {
let rowIdx = validRowsIndex[j];
this.setRowBg(rowIdx, idx);
idx++;
}
}
/**
* Set/remove row background based on row validation
* @param {Number} rowIdx Row index
* @param {Number} arrIdx Array index
* @param {Boolean} isValid Valid row flag
*/
processRow(rowIdx, arrIdx, isValid) {
if (isValid) {
this.setRowBg(rowIdx, arrIdx);
} else {
this.removeRowBg(rowIdx);
}
}
/**
* Sets row background color
* @param {Number} rowIdx Row index
* @param {Number} idx Valid rows collection index needed to calculate bg
* color
* @private
*/
setRowBg(rowIdx, idx) {
if (!this.isEnabled() || isNaN(rowIdx)) {
return;
}
let rows = this.tf.dom().rows;
let i = isNaN(idx) ? rowIdx : idx;
this.removeRowBg(rowIdx);
addClass(rows[rowIdx], (i % 2) ? this.evenCss : this.oddCss);
}
/**
* Removes row background color
* @param {Number} idx Row index
* @private
*/
removeRowBg(idx) {
if (isNaN(idx)) {
return;
}
let rows = this.tf.dom().rows;
removeClass(rows[idx], this.oddCss);
removeClass(rows[idx], this.evenCss);
}
/**
* Removes all alternating backgrounds
*/
destroy() {
if (!this.initialized) {
return;
}
let nbRows = this.tf.getRowsNb(true);
for (let i = 0; i < nbRows; i++) {
this.removeRowBg(i);
}
// Unsubscribe to events
this.emitter.off(['row-processed', 'row-paged'],
(tf, rowIndex, arrIndex, isValid) =>
this.processRow(rowIndex, arrIndex, isValid));
this.emitter.off(['column-sorted', 'rows-changed'],
() => this.processAll());
this.initialized = false;
}
}
@@ -1,147 +0,0 @@
import {Feature} from '../feature';
import {
ignoreCase, numSortAsc, numSortDesc,
dateSortAsc, sortNumberStr, sortDateStr
} from '../sort';
import {isArray, isObj, isEmpty} from '../types';
import {NUMBER, FORMATTED_NUMBER, DATE} from '../const';
/**
* Base class for Dropdown and CheckList UI components
* @export
* @class BaseDropdown
* @extends {Feature}
*/
export class BaseDropdown extends Feature {
/**
* Creates an instance of BaseDropdown
* @param {TableFilter} tf
*/
constructor(tf) {
super(tf, 'baseDropdown');
let f = this.config;
/**
* Filter options custom sorter on a column basis
* @type {Object}
*/
this.customSorter = isObj(f.filter_options_sorter) &&
isArray(f.filter_options_sorter.col) &&
isArray(f.filter_options_sorter.comparer) ?
f.filter_options_sorter :
null;
// TODO: move here all properties shared by Dropdown CheckList
/**
* Has custom options
* @type {Boolean}
* @private
*/
this.isCustom = false;
/**
* List of options values
* @type {Array}
* @private
*/
this.opts = [];
/**
* List of options texts for custom values
* @type {Array}
* @private
*/
this.optsTxt = [];
/**
* List of options to be excluded from the checklist filter
* @type {Array}
* @private
*/
this.excludedOpts = [];
}
/**
* Sort passed options based on the type of the specified column
* @param {Number} colIndex Column index
* @param {Array} [options=[]] Collection of values
* @return {Array} Sorted values
* @private
*/
sortOptions(colIndex, options = []) {
let tf = this.tf;
if (tf.isCustomOptions(colIndex) || !tf.sortSlc ||
(isArray(tf.sortSlc) && tf.sortSlc.indexOf(colIndex) === -1)) {
return options;
}
let { caseSensitive, sortNumDesc } = tf;
let compareFn;
if (this.customSorter &&
this.customSorter.col.indexOf(colIndex) !== -1) {
var idx = this.customSorter.col.indexOf(colIndex);
compareFn = this.customSorter.comparer[idx];
}
else if (tf.hasType(colIndex, [NUMBER, FORMATTED_NUMBER])) {
let decimal = tf.getDecimal(colIndex);
let comparer = numSortAsc;
if (sortNumDesc === true || sortNumDesc.indexOf(colIndex) !== -1) {
comparer = numSortDesc;
}
compareFn = sortNumberStr(comparer, decimal);
}
else if (tf.hasType(colIndex, [DATE])) {
let locale = tf.feature('dateType').getLocale(colIndex);
let comparer = dateSortAsc;
compareFn = sortDateStr(comparer, locale);
} else { // string
compareFn = caseSensitive ? undefined : ignoreCase;
}
return options.sort(compareFn);
}
/**
* Regenerate filters of specified columns and maintain selection if any
* @param {Array} colIndexes Collection of column indexes
* @private
*/
refreshFilters(colIndexes) {
colIndexes.forEach((colIdx) => {
let values = this.getValues(colIdx);
this.build(colIdx, this.tf.linkedFilters);
this.selectOptions(colIdx, values);
});
}
/**
* Check passed row contains a valid linked value
* @param {Number} rowIdx Row index
* @param {Number} activeFilterIdx Current active filter index
* @returns {Boolean}
*/
isValidLinkedValue(rowIdx, activeFilterIdx) {
let tf = this.tf;
if (tf.disableExcludedOptions) {
return true;
}
if (tf.paging) {
if (!isEmpty(activeFilterIdx) && tf.isRowValid(rowIdx)) {
return true;
}
} else {
if (tf.isRowDisplayed(rowIdx)) {
return true;
}
}
return false;
}
}
@@ -1,531 +0,0 @@
import {BaseDropdown} from './baseDropdown';
import {
addClass, createCheckItem, createText, createElm, elm, getText,
removeClass, tag
} from '../dom';
import {has} from '../array';
import {matchCase, trim, rgxEsc} from '../string';
import {addEvt, removeEvt, targetEvt} from '../event';
import {isEmpty} from '../types';
import {CHECKLIST, NONE} from '../const';
import {defaultsStr, defaultsBool} from '../settings';
/**
* Checklist filter UI component
* @export
* @class CheckList
* @extends {BaseDropdown}
*/
export class CheckList extends BaseDropdown {
/**
* Creates an instance of CheckList
* @param {TableFilter} tf TableFilter instance
*/
constructor(tf) {
super(tf, 'checkList');
let f = this.config;
/**
* List of container DOM elements
* @type {Array}
*/
this.containers = [];
/**
* Css class for the container of the checklist filter (div)
* @type {String}
*/
this.containerCssClass = defaultsStr(f.div_checklist_css_class,
'div_checklist');
/**
* Css class for the checklist filter element (ul)
* @type {String}
*/
this.filterCssClass = defaultsStr(f.checklist_css_class,
'flt_checklist');
/**
* Css class for the item of a checklist (li)
* @type {String}
*/
this.itemCssClass = defaultsStr(f.checklist_item_css_class,
'flt_checklist_item');
/**
* Css class for a selected item of a checklist (li)
* @type {String}
*/
this.selectedItemCssClass = defaultsStr(
f.checklist_selected_item_css_class,
'flt_checklist_slc_item'
);
/**
* Text placed in the filter's container when load filter on demand
* feature is enabled
* @type {String}
*/
this.activateText = defaultsStr(
f.activate_checklist_text,
'Click to load filter data'
);
/**
* Css class for a disabled item of a checklist (li)
* @type {String}
*/
this.disabledItemCssClass = defaultsStr(
f.checklist_item_disabled_css_class,
'flt_checklist_item_disabled'
);
/**
* Enable the reset filter option as first item
* @type {Boolean}
*/
this.enableResetOption = defaultsBool(f.enable_checklist_reset_filter,
true);
/**
* Prefix for container element ID
* @type {String}
* @private
*/
this.prfx = 'chkdiv_';
}
/**
* Checklist option click event handler
* @param {Event} evt
* @private
*/
optionClick(evt) {
let elm = targetEvt(evt);
let tf = this.tf;
this.emitter.emit('filter-focus', tf, elm);
this.setCheckListValues(elm);
tf.filter();
}
/**
* Checklist container click event handler for load-on-demand feature
* @param {Event} evt
* @private
*/
onCheckListClick(evt) {
let elm = targetEvt(evt);
if (this.tf.loadFltOnDemand && elm.getAttribute('filled') === '0') {
let ct = elm.getAttribute('ct');
let div = this.containers[ct];
this.build(ct);
removeEvt(div, 'click', (evt) => this.onCheckListClick(evt));
}
}
/**
* Refresh all checklist filters
*/
refreshAll() {
let colIdxs = this.tf.getFiltersByType(CHECKLIST, true);
this.refreshFilters(colIdxs);
}
/**
* Initialize checklist filter
* @param {Number} colIndex Column index
* @param {Boolean} isExternal External filter flag
* @param {DOMElement} container Dom element containing the filter
*/
init(colIndex, isExternal, container) {
let tf = this.tf;
let externalFltTgtId = isExternal ?
tf.externalFltTgtIds[colIndex] : null;
let divCont = createElm('div',
['id', `${this.prfx}${colIndex}_${tf.id}`],
['ct', colIndex], ['filled', '0']);
divCont.className = this.containerCssClass;
//filter is appended in desired element
if (externalFltTgtId) {
elm(externalFltTgtId).appendChild(divCont);
} else {
container.appendChild(divCont);
}
this.containers[colIndex] = divCont;
tf.fltIds.push(tf.buildFilterId(colIndex));
if (!tf.loadFltOnDemand) {
this.build(colIndex);
} else {
addEvt(divCont, 'click', (evt) => this.onCheckListClick(evt));
divCont.appendChild(createText(this.activateText));
}
this.emitter.on(
['build-checklist-filter'],
(tf, colIndex, isLinked) => this.build(colIndex, isLinked)
);
this.emitter.on(
['select-checklist-options'],
(tf, colIndex, values) => this.selectOptions(colIndex, values)
);
this.emitter.on(['rows-changed'], () => this.refreshAll());
/** @inherited */
this.initialized = true;
}
/**
* Build checklist UI
* @param {Number} colIndex Column index
* @param {Boolean} isLinked Enable linked filters behaviour
*/
build(colIndex, isLinked = false) {
let tf = this.tf;
colIndex = parseInt(colIndex, 10);
this.emitter.emit('before-populating-filter', tf, colIndex);
/** @inherited */
this.opts = [];
/** @inherited */
this.optsTxt = [];
let flt = this.containers[colIndex];
let ul = createElm('ul',
['id', tf.fltIds[colIndex]],
['colIndex', colIndex]);
ul.className = this.filterCssClass;
let rows = tf.dom().rows;
let nbRows = tf.getRowsNb(true);
let caseSensitive = tf.caseSensitive;
/** @inherited */
this.isCustom = tf.isCustomOptions(colIndex);
//Retrieves custom values
if (this.isCustom) {
let customValues = tf.getCustomOptions(colIndex);
this.opts = customValues[0];
this.optsTxt = customValues[1];
}
let activeIdx;
let activeFilterId = tf.getActiveFilterId();
if (isLinked && activeFilterId) {
activeIdx = tf.getColumnIndexFromFilterId(activeFilterId);
}
let filteredDataCol = [];
if (isLinked && tf.disableExcludedOptions) {
/** @inherited */
this.excludedOpts = [];
}
flt.innerHTML = '';
for (let k = tf.refRow; k < nbRows; k++) {
// always visible rows don't need to appear on selects as always
// valid
if (tf.hasVisibleRows && tf.visibleRows.indexOf(k) !== -1) {
continue;
}
let cells = rows[k].cells;
let ncells = cells.length;
// checks if row has exact cell #
if (ncells !== tf.nbCells || this.isCustom) {
continue;
}
if (isLinked && !this.isValidLinkedValue(k, activeIdx)) {
continue;
}
// this loop retrieves cell data
for (let j = 0; j < ncells; j++) {
if (colIndex !== j) {
continue;
}
let cellValue = tf.getCellValue(cells[j]);
//Vary Peter's patch
let cellString = matchCase(cellValue, caseSensitive);
// checks if celldata is already in array
if (!has(this.opts, cellString, caseSensitive)) {
this.opts.push(cellValue);
}
let filteredCol = filteredDataCol[j];
if (isLinked && tf.disableExcludedOptions) {
if (!filteredCol) {
filteredCol = tf.getVisibleColumnValues(j);
}
if (!has(filteredCol, cellString, caseSensitive) &&
!has(this.excludedOpts, cellString,
caseSensitive)) {
this.excludedOpts.push(cellValue);
}
}
}
}
//sort options
this.opts = this.sortOptions(colIndex, this.opts);
if (this.excludedOpts) {
this.excludedOpts = this.sortOptions(colIndex, this.excludedOpts);
}
this.addChecks(colIndex, ul);
if (tf.loadFltOnDemand) {
flt.innerHTML = '';
}
flt.appendChild(ul);
flt.setAttribute('filled', '1');
this.emitter.emit('after-populating-filter', tf, colIndex, flt);
}
/**
* Add checklist options
* @param {Number} colIndex Column index
* @param {Object} ul Ul element
* @private
*/
addChecks(colIndex, ul) {
let tf = this.tf;
let chkCt = this.addTChecks(colIndex, ul);
for (let y = 0; y < this.opts.length; y++) {
let val = this.opts[y]; //item value
let lbl = this.isCustom ? this.optsTxt[y] : val; //item text
let fltId = tf.fltIds[colIndex];
let li = createCheckItem(`${fltId}_${(y + chkCt)}`, val, lbl);
li.className = this.itemCssClass;
if (tf.linkedFilters && tf.disableExcludedOptions &&
has(this.excludedOpts, matchCase(val, tf.caseSensitive),
tf.caseSensitive)) {
addClass(li, this.disabledItemCssClass);
li.check.disabled = true;
li.disabled = true;
} else {
addEvt(li.check, 'click', evt => this.optionClick(evt));
}
ul.appendChild(li);
if (val === '') {
//item is hidden
li.style.display = NONE;
}
}
}
/**
* Add checklist header option
* @param {Number} colIndex Column index
* @param {Object} ul Ul element
* @private
*/
addTChecks(colIndex, ul) {
let tf = this.tf;
let chkCt = 1;
let fltId = tf.fltIds[colIndex];
let li0 = createCheckItem(`${fltId}_0`, '',
tf.getClearFilterText(colIndex));
li0.className = this.itemCssClass;
ul.appendChild(li0);
addEvt(li0.check, 'click', evt => this.optionClick(evt));
if (!this.enableResetOption) {
li0.style.display = NONE;
}
if (tf.enableEmptyOption) {
let li1 = createCheckItem(`${fltId}_1`, tf.emOperator,
tf.emptyText);
li1.className = this.itemCssClass;
ul.appendChild(li1);
addEvt(li1.check, 'click', evt => this.optionClick(evt));
chkCt++;
}
if (tf.enableNonEmptyOption) {
let li2 = createCheckItem(`${fltId}_2`, tf.nmOperator,
tf.nonEmptyText);
li2.className = this.itemCssClass;
ul.appendChild(li2);
addEvt(li2.check, 'click', evt => this.optionClick(evt));
chkCt++;
}
return chkCt;
}
/**
* Store checked options in DOM element attribute
* @param {Object} o checklist option DOM element
* @private
*/
setCheckListValues(o) {
if (!o) {
return;
}
let tf = this.tf;
let chkValue = o.value; //checked item value
// TODO: provide helper to extract column index, ugly!
let chkIndex = parseInt(o.id.split('_')[2], 10);
let colIdx = tf.getColumnIndexFromFilterId(o.id);
let itemTag = 'LI';
let n = tf.getFilterElement(parseInt(colIdx, 10));
let li = n.childNodes[chkIndex];
let colIndex = n.getAttribute('colIndex');
let fltValue = n.getAttribute('value'); //filter value (ul tag)
let fltIndexes = n.getAttribute('indexes'); //selected items (ul tag)
if (o.checked) {
//show all item
if (chkValue === '') {
if ((fltIndexes && fltIndexes !== '')) {
//items indexes
let indSplit = fltIndexes.split(tf.separator);
//checked items loop
for (let u = 0; u < indSplit.length; u++) {
//checked item
let cChk = elm(tf.fltIds[colIndex] + '_' +
indSplit[u]);
if (cChk) {
cChk.checked = false;
removeClass(n.childNodes[indSplit[u]],
this.selectedItemCssClass);
}
}
}
n.setAttribute('value', '');
n.setAttribute('indexes', '');
} else {
fltValue = (fltValue) ? fltValue : '';
chkValue = trim(fltValue + ' ' + chkValue + ' ' +
tf.orOperator);
chkIndex = fltIndexes + chkIndex + tf.separator;
n.setAttribute('value', chkValue);
n.setAttribute('indexes', chkIndex);
//1st option unchecked
if (elm(tf.fltIds[colIndex] + '_0')) {
elm(tf.fltIds[colIndex] + '_0').checked = false;
}
}
if (li.nodeName === itemTag) {
removeClass(n.childNodes[0], this.selectedItemCssClass);
addClass(li, this.selectedItemCssClass);
}
} else { //removes values and indexes
if (chkValue !== '') {
let replaceValue = new RegExp(
rgxEsc(chkValue + ' ' + tf.orOperator));
fltValue = fltValue.replace(replaceValue, '');
n.setAttribute('value', trim(fltValue));
let replaceIndex = new RegExp(
rgxEsc(chkIndex + tf.separator));
fltIndexes = fltIndexes.replace(replaceIndex, '');
n.setAttribute('indexes', fltIndexes);
}
if (li.nodeName === itemTag) {
removeClass(li, this.selectedItemCssClass);
}
}
}
/**
* Select filter options programmatically
* @param {Number} colIndex Column index
* @param {Array} values Array of option values to select
*/
selectOptions(colIndex, values = []) {
let tf = this.tf;
let flt = tf.getFilterElement(colIndex);
if (tf.getFilterType(colIndex) !== CHECKLIST || !flt ||
values.length === 0) {
return;
}
let lisNb = tag(flt, 'li').length;
flt.setAttribute('value', '');
flt.setAttribute('indexes', '');
for (let k = 0; k < lisNb; k++) {
let li = tag(flt, 'li')[k];
let lbl = tag(li, 'label')[0];
let chk = tag(li, 'input')[0];
let lblTxt = matchCase(getText(lbl), tf.caseSensitive);
if (lblTxt !== '' && has(values, lblTxt, tf.caseSensitive)) {
chk.checked = true;
} else {
// Check non-empty-text or empty-text option
if (values.indexOf(tf.nmOperator) !== -1 &&
lblTxt === matchCase(tf.nonEmptyText, tf.caseSensitive)) {
chk.checked = true;
}
else if (values.indexOf(tf.emOperator) !== -1 &&
lblTxt === matchCase(tf.emptyText, tf.caseSensitive)) {
chk.checked = true;
} else {
chk.checked = false;
}
}
this.setCheckListValues(chk);
}
}
/**
* Get filter values for a given column index
* @param {Number} colIndex Column index
* @returns {Array} values Collection of selected values
*/
getValues(colIndex) {
let tf = this.tf;
let flt = tf.getFilterElement(colIndex);
let fltAttr = flt.getAttribute('value');
let values = isEmpty(fltAttr) ? '' : fltAttr;
//removes last operator ||
values = values.substr(0, values.length - 3);
//turn || separated values into array
values = values.split(' ' + tf.orOperator + ' ');
return values;
}
/**
* Destroy CheckList instance
*/
destroy() {
this.emitter.off(
['build-checklist-filter'],
(tf, colIndex, isLinked) => this.build(colIndex, isLinked)
);
this.emitter.off(
['select-checklist-options'],
(tf, colIndex, values) => this.selectOptions(colIndex, values)
);
this.emitter.off(['rows-changed'], () => this.refreshAll());
this.initialized = false;
}
}
@@ -1,129 +0,0 @@
import {Feature} from '../feature';
import {createElm, createText, elm, removeElm} from '../dom';
import {addEvt} from '../event';
import {defaultsStr} from '../settings';
/**
* Clear button UI component
*/
export class ClearButton extends Feature {
/**
* Creates an instance of ClearButton
* @param {TableFilter} tf TableFilter instance
*/
constructor(tf) {
super(tf, 'btnReset');
let f = this.config;
/**
* Container element ID
* @type {String}
*/
this.targetId = defaultsStr(f.btn_reset_target_id, null);
/**
* Clear button container element
* @type {DOMElement}
* @private
*/
this.container = null;
/**
* Clear button element
* @type {DOMElement}
* @private
*/
this.element = null;
/**
* Text for the clear button
* @type {String}
*/
this.text = defaultsStr(f.btn_reset_text, 'Reset');
/**
* Css class for reset button
* @type {String}
*/
this.cssClass = defaultsStr(f.btn_reset_css_class, 'reset');
/**
* Tooltip text for the clear button
* @type {String}
*/
this.tooltip = f.btn_reset_tooltip || 'Clear filters';
/**
* Custom Html string for the clear button
* @type {String}
*/
this.html = defaultsStr(f.btn_reset_html,
(!tf.enableIcons ? null :
'<input type="button" value="" class="' + this.cssClass +
'" ' + 'title="' + this.tooltip + '" />'));
}
/**
* Click event handler for clear button
* @private
*/
onClick() {
if (!this.isEnabled()) {
return;
}
this.tf.clearFilters();
}
/**
* Initialize clear button component
*/
init() {
let tf = this.tf;
if (this.initialized) {
return;
}
let cont = createElm('span');
// reset button is added to defined element
if (!this.targetId) {
tf.setToolbar();
}
let targetEl = !this.targetId ? tf.rDiv : elm(this.targetId);
targetEl.appendChild(cont);
if (!this.html) {
let fltReset = createElm('a', ['href', 'javascript:void(0);']);
fltReset.className = this.cssClass;
fltReset.appendChild(createText(this.text));
cont.appendChild(fltReset);
addEvt(fltReset, 'click', () => this.onClick());
} else {
cont.innerHTML = this.html;
let resetEl = cont.firstChild;
addEvt(resetEl, 'click', () => this.onClick());
}
this.element = cont.firstChild;
this.container = cont;
/** @inherited */
this.initialized = true;
}
/**
* Destroy ClearButton instance
*/
destroy() {
if (!this.initialized) {
return;
}
removeElm(this.element);
removeElm(this.container);
this.element = null;
this.container = null;
this.initialized = false;
}
}
@@ -1,157 +0,0 @@
import {Date as SugarDate} from 'sugar-date';
import 'sugar-date/locales';
import {Feature} from '../feature';
import {isObj, isArray} from '../types';
import {DATE} from '../const';
import {root} from '../root';
/**
* Wrapper for Sugar Date module providing datetime helpers and locales
* @export
* @class DateType
*/
export class DateType extends Feature {
/**
* Creates an instance of DateType
* @param {TableFilter} tf TableFilter instance
*/
constructor(tf) {
super(tf, 'dateType');
/**
* Global locale
* @type {String}
*/
this.locale = tf.locale;
/**
* Sugar Date instance
* @type {Object}
*/
this.datetime = SugarDate;
this.enable();
}
/**
* Initialize DateType instance
*/
init() {
if (this.initialized) {
return;
}
// Set global locale
this.datetime.setLocale(this.locale);
// Add formats from column types configuration if any
this.addConfigFormats(this.tf.colTypes);
this.emitter.on(
['add-date-type-formats'],
(tf, types) => this.addConfigFormats(types)
);
// Broadcast date-type initialization
this.emitter.emit('date-type-initialized', this.tf, this);
/** @inherited */
this.initialized = true;
}
/**
* Parse a string representation of a date for a specified locale and return
* a date object
* @param {String} dateStr String representation of a date
* @param {String} localeCode Locale code (ie 'en-us')
* @returns {Date}
*/
parse(dateStr, localeCode) {
return this.datetime.create(dateStr, localeCode);
}
/**
* Check string representation of a date for a specified locale is valid
* @param {any} dateStr String representation of a date
* @param {any} localeCode Locale code (ie 'en-us')
* @returns {Boolean}
*/
isValid(dateStr, localeCode) {
return this.datetime.isValid(this.parse(dateStr, localeCode));
}
/**
* Return the type object of a specified column as per configuration or
* passed collection
* @param {Number} colIndex Column index
* @param {Array} types Collection of column types, optional
* @returns {Object}
*/
getOptions(colIndex, types) {
types = types || this.tf.colTypes;
let colType = types[colIndex];
return isObj(colType) ? colType : {};
}
/**
* Return the locale code for supplied column index as per configuration
* or global setting
* @param {Number} colIndex Column index
* @returns {String} Locale code (ie: 'en-us')
*/
getLocale(colIndex) {
return this.getOptions(colIndex).locale || this.locale;
}
/**
* Add date time format(s) to a locale as specified by the passed
* collection of column types, ie:
* [
* 'string',
* 'number',
* { type: 'date', locale: 'en', format: ['{dd}/{MM}/{yyyy}']}
* ]
*
* @param {Array} [types=[]] Collection of column types
*/
addConfigFormats(types=[]) {
types.forEach((type, idx) => {
let options = this.getOptions(idx, types);
if (options.type === DATE && options.hasOwnProperty('format')) {
let locale = this.datetime.getLocale(
options.locale || this.locale
);
let formats = isArray(options.format) ?
options.format : [options.format];
// Sugar date module throws exceptions with locale.addFormat
try {
formats.forEach((format) => {
locale.addFormat(format);
});
} catch (ex) {
root.console.error(ex);
}
}
});
}
/**
* Remove DateType instance
*/
destroy() {
if (!this.initialized) {
return;
}
// TODO: remove added formats
this.emitter.off(
['add-date-type-formats'],
(tf, types) => this.addConfigFormats(types)
);
this.initialized = false;
}
}
@@ -1,382 +0,0 @@
import {BaseDropdown} from './baseDropdown';
import {createElm, createOpt, elm} from '../dom';
import {has} from '../array';
import {matchCase} from '../string';
import {addEvt, targetEvt} from '../event';
import {SELECT, MULTIPLE, NONE} from '../const';
import {defaultsStr, defaultsBool} from '../settings';
/**
* Dropdown filter UI component
* @export
* @class Dropdown
* @extends {BaseDropdown}
*/
export class Dropdown extends BaseDropdown {
/**
* Creates an instance of Dropdown
* @param {TableFilter} tf TableFilter instance
*/
constructor(tf) {
super(tf, 'dropdown');
// Configuration object
let f = this.config;
/**
* Enable the reset filter option as first item
* @type {Boolean}
*/
this.enableSlcResetFilter =
defaultsBool(f.enable_slc_reset_filter, true);
/**
* Non empty option text
* @type {String}
*/
this.nonEmptyText = defaultsStr(f.non_empty_text, '(Non empty)');
/**
* Tooltip text appearing on multiple select
* @type {String}
*/
this.multipleSlcTooltip = defaultsStr(f.multiple_slc_tooltip,
'Use Ctrl/Cmd key for multiple selections');
}
/**
* Drop-down filter focus event handler
* @param {Event} e DOM Event
* @private
*/
onSlcFocus(e) {
let elm = targetEvt(e);
let tf = this.tf;
// select is populated when element has focus
if (tf.loadFltOnDemand && elm.getAttribute('filled') === '0') {
let ct = elm.getAttribute('ct');
this.build(ct);
}
this.emitter.emit('filter-focus', tf, elm);
}
/**
* Drop-down filter change event handler
* @private
*/
onSlcChange() {
if (this.tf.onSlcChange) {
this.tf.filter();
}
}
/**
* Refresh all drop-down filters
*/
refreshAll() {
let selectFlts = this.tf.getFiltersByType(SELECT, true);
let multipleFlts = this.tf.getFiltersByType(MULTIPLE, true);
let colIdxs = selectFlts.concat(multipleFlts);
this.refreshFilters(colIdxs);
}
/**
* Initialize drop-down filter
* @param {Number} colIndex Column index
* @param {Boolean} isExternal External filter flag
* @param {DOMElement} container Dom element containing the filter
*/
init(colIndex, isExternal, container) {
let tf = this.tf;
let col = tf.getFilterType(colIndex);
let externalFltTgtId = isExternal ?
tf.externalFltTgtIds[colIndex] : null;
let slc = createElm(SELECT,
['id', tf.buildFilterId(colIndex)],
['ct', colIndex], ['filled', '0']
);
if (col === MULTIPLE) {
slc.multiple = MULTIPLE;
slc.title = this.multipleSlcTooltip;
}
slc.className = col.toLowerCase() === SELECT ?
tf.fltCssClass : tf.fltMultiCssClass;
//filter is appended in container element
if (externalFltTgtId) {
elm(externalFltTgtId).appendChild(slc);
} else {
container.appendChild(slc);
}
tf.fltIds.push(slc.id);
if (!tf.loadFltOnDemand) {
this.build(colIndex);
} else {
//1st option is created here since build isn't invoked
let opt0 = createOpt(tf.getClearFilterText(colIndex), '');
slc.appendChild(opt0);
}
addEvt(slc, 'change', () => this.onSlcChange());
addEvt(slc, 'focus', (e) => this.onSlcFocus(e));
this.emitter.on(
['build-select-filter'],
(tf, colIndex, isLinked, isExternal) =>
this.build(colIndex, isLinked, isExternal)
);
this.emitter.on(
['select-options'],
(tf, colIndex, values) => this.selectOptions(colIndex, values)
);
this.emitter.on(['rows-changed'], () => this.refreshAll());
/** @inherited */
this.initialized = true;
}
/**
* Build drop-down filter UI
* @param {Number} colIndex Column index
* @param {Boolean} isLinked Enable linked filters behaviour
*/
build(colIndex, isLinked = false) {
let tf = this.tf;
colIndex = parseInt(colIndex, 10);
this.emitter.emit('before-populating-filter', tf, colIndex);
/** @inherited */
this.opts = [];
/** @inherited */
this.optsTxt = [];
let slcId = tf.fltIds[colIndex];
let slc = elm(slcId);
let rows = tf.dom().rows;
let nbRows = tf.getRowsNb(true);
//custom select test
/** @inherited */
this.isCustom = tf.isCustomOptions(colIndex);
//Retrieves custom values
if (this.isCustom) {
let customValues = tf.getCustomOptions(colIndex);
this.opts = customValues[0];
this.optsTxt = customValues[1];
}
//custom selects text
let activeIdx;
let activeFilterId = tf.getActiveFilterId();
if (isLinked && activeFilterId) {
activeIdx = tf.getColumnIndexFromFilterId(activeFilterId);
}
let excludedOpts = null,
filteredDataCol = null;
if (isLinked && tf.disableExcludedOptions) {
excludedOpts = [];
filteredDataCol = [];
}
for (let k = tf.refRow; k < nbRows; k++) {
// always visible rows don't need to appear on selects as always
// valid
if (tf.hasVisibleRows && tf.visibleRows.indexOf(k) !== -1) {
continue;
}
let cell = rows[k].cells,
nchilds = cell.length;
// checks if row has exact cell #
if (nchilds !== tf.nbCells || this.isCustom) {
continue;
}
if (isLinked && !this.isValidLinkedValue(k, activeIdx)) {
continue;
}
// this loop retrieves cell data
for (let j = 0; j < nchilds; j++) {
if (colIndex !== j) {
continue;
}
let cellValue = tf.getCellValue(cell[j]),
//Vary Peter's patch
cellString = matchCase(cellValue, tf.caseSensitive);
// checks if celldata is already in array
if (!has(this.opts, cellString, tf.caseSensitive)) {
this.opts.push(cellValue);
}
if (isLinked && tf.disableExcludedOptions) {
let filteredCol = filteredDataCol[j];
if (!filteredCol) {
filteredCol = tf.getVisibleColumnValues(j);
}
if (!has(filteredCol, cellString, tf.caseSensitive) &&
!has(excludedOpts, cellString, tf.caseSensitive)) {
excludedOpts.push(cellValue);
}
}
}//for j
}//for k
//sort options
this.opts = this.sortOptions(colIndex, this.opts);
if (excludedOpts) {
excludedOpts = this.sortOptions(colIndex, excludedOpts);
}
//populates drop-down
this.addOptions(colIndex, slc, isLinked, excludedOpts);
this.emitter.emit('after-populating-filter', tf, colIndex, slc);
}
/**
* 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
*/
addOptions(colIndex, slc, isLinked, excludedOpts) {
let tf = this.tf,
slcValue = slc.value;
slc.innerHTML = '';
slc = this.addFirstOption(slc);
for (let y = 0; y < this.opts.length; y++) {
if (this.opts[y] === '') {
continue;
}
let val = this.opts[y]; //option value
let lbl = this.isCustom ? this.optsTxt[y] : val; //option text
let isDisabled = false;
if (isLinked && tf.disableExcludedOptions &&
has(excludedOpts, matchCase(val, tf.caseSensitive),
tf.caseSensitive)) {
isDisabled = true;
}
let opt;
//fill select on demand
if (tf.loadFltOnDemand && slcValue === this.opts[y] &&
tf.getFilterType(colIndex) === SELECT) {
opt = createOpt(lbl, val, true);
} else {
opt = createOpt(lbl, val, false);
}
if (isDisabled) {
opt.disabled = true;
}
slc.appendChild(opt);
}// for y
slc.setAttribute('filled', '1');
}
/**
* Add drop-down header option
* @param {Object} slc Select DOM element
*/
addFirstOption(slc) {
let tf = this.tf;
let colIdx = tf.getColumnIndexFromFilterId(slc.id);
let opt0 = createOpt((!this.enableSlcResetFilter ?
'' : tf.getClearFilterText(colIdx)), '');
if (!this.enableSlcResetFilter) {
opt0.style.display = NONE;
}
slc.appendChild(opt0);
if (tf.enableEmptyOption) {
let opt1 = createOpt(tf.emptyText, tf.emOperator);
slc.appendChild(opt1);
}
if (tf.enableNonEmptyOption) {
let opt2 = createOpt(tf.nonEmptyText, tf.nmOperator);
slc.appendChild(opt2);
}
return slc;
}
/**
* Select filter options programmatically
* @param {Number} colIndex Column index
* @param {Array} values Array of option values to select
*/
selectOptions(colIndex, values = []) {
let tf = this.tf;
if (values.length === 0) {
return;
}
let slc = tf.getFilterElement(colIndex);
[].forEach.call(slc.options, (option) => {
// Empty value means clear all selections and first option is the
// clear all option
if (values[0] === '' || option.value === '') {
option.selected = false;
}
if (option.value !== '' && has(values, option.value, true)) {
option.selected = true;
}//if
});
}
/**
* Get filter values for a given column index
* @param {Number} colIndex Column index
* @returns {Array} values Array of selected values
*/
getValues(colIndex) {
let tf = this.tf;
let slc = tf.getFilterElement(colIndex);
let values = [];
// IE >= 9 does not support the selectedOptions property :(
if (slc.selectedOptions) {
[].forEach.call(slc.selectedOptions,
option => values.push(option.value));
} else {
[].forEach.call(slc.options, (option) => {
if (option.selected) {
values.push(option.value);
}
});
}
return values;
}
/**
* Destroy Dropdown instance
*/
destroy() {
this.emitter.off(
['build-select-filter'],
(colIndex, isLinked, isExternal) =>
this.build(colIndex, isLinked, isExternal)
);
this.emitter.off(
['select-options'],
(tf, colIndex, values) => this.selectOptions(colIndex, values)
);
this.emitter.off(['rows-changed'], () => this.refreshAll());
this.initialized = false;
}
}
@@ -1,483 +0,0 @@
import {Feature} from '../feature';
import {createElm, removeElm, elm, tag} from '../dom';
import {addEvt, targetEvt} from '../event';
import {contains} from '../string';
import {NONE} from '../const';
import {
defaultsBool, defaultsStr, defaultsNb, defaultsArr
} from '../settings';
/**
* Grid layout, table with fixed headers
*/
export class GridLayout extends Feature {
/**
* Creates an instance of GridLayout
* @param {TableFilter} tf TableFilter instance
*/
constructor(tf) {
super(tf, 'gridLayout');
let f = this.config.grid_layout || {};
/**
* Grid-layout container width as CSS string
* @type {String}
*/
this.width = defaultsStr(f.width, null);
/**
* Grid-layout container height as CSS string
* @type {String}
*/
this.height = defaultsStr(f.height, null);
/**
* Css class for main container element
* @type {String}
*/
this.mainContCssClass = defaultsStr(f.cont_css_class, 'grd_Cont');
/**
* Css class for body table container element
* @type {String}
*/
this.contCssClass = defaultsStr(f.tbl_cont_css_class, 'grd_tblCont');
/**
* Css class for headers table container element
* @type {String}
*/
this.headContCssClass = defaultsStr(f.tbl_head_css_class,
'grd_headTblCont');
/**
* Css class for toolbar container element (rows counter, paging etc.)
* @type {String}
*/
this.infDivCssClass = defaultsStr(f.inf_grid_css_class, 'grd_inf');
/**
* Index of the headers row, default: 0
* @type {Number}
*/
this.headRowIndex = defaultsNb(f.headers_row_index, 0);
/**
* Collection of the header row indexes to be moved into headers table
* @type {Array}
*/
this.headRows = defaultsArr(f.headers_rows, [0]);
/**
* Enable or disable column filters generation, default: true
* @type {Boolean}
*/
this.filters = defaultsBool(f.filters, true);
/**
* Enable or disable column headers, default: false
* @type {Boolean}
*/
this.noHeaders = Boolean(f.no_headers);
/**
* Grid-layout default column widht as CSS string
* @type {String}
*/
this.defaultColWidth = defaultsStr(f.default_col_width, '100px');
/**
* List of column elements
* @type {Array}
* @private
*/
this.colElms = [];
/**
* Prefix for grid-layout filter's cell ID
* @type {String}
* @private
*/
this.prfxGridFltTd = '_td_';
/**
* Prefix for grid-layout header's cell ID
* @type {String}
* @private
*/
this.prfxGridTh = 'tblHeadTh_';
/**
* Mark-up of original HTML table
* @type {String}
* @private
*/
this.sourceTblHtml = tf.dom().outerHTML;
/**
* Indicates if working table has column elements
* @type {Boolean}
* @private
*/
this.tblHasColTag = tag(tf.dom(), 'col').length > 0 ? true : false;
/**
* Main container element
* @private
*/
this.tblMainCont = null;
/**
* Table container element
* @private
*/
this.tblCont = null;
/**
* Headers' table container element
* @private
*/
this.headTblCont = null;
/**
* Headers' table element
* @private
*/
this.headTbl = null;
// filters flag at TF level
tf.fltGrid = this.filters;
}
/**
* Generates a grid with fixed headers
* TODO: reduce size of init by extracting single purposed methods
*/
init() {
let tf = this.tf;
let tbl = tf.dom();
if (this.initialized) {
return;
}
// Override relevant TableFilter properties
this.setOverrides();
// Assign default column widths
this.setDefaultColWidths();
// Initial table width
let tblW = this.initialTableWidth();
//Main container: it will contain all the elements
this.tblMainCont = this.createContainer(
'div', this.mainContCssClass);
if (this.width) {
this.tblMainCont.style.width = this.width;
}
tbl.parentNode.insertBefore(this.tblMainCont, tbl);
//Table container: div wrapping content table
this.tblCont = this.createContainer('div', this.contCssClass);
this.setConfigWidth(this.tblCont);
if (this.height) {
this.tblCont.style.height = this.height;
}
tbl.parentNode.insertBefore(this.tblCont, tbl);
let t = removeElm(tbl);
this.tblCont.appendChild(t);
//In case table width is expressed in %
if (tbl.style.width === '') {
tbl.style.width = (contains('%', tblW) ?
tbl.clientWidth : tblW) + 'px';
}
let d = removeElm(this.tblCont);
this.tblMainCont.appendChild(d);
//Headers table container: div wrapping headers table
this.headTblCont = this.createContainer(
'div', this.headContCssClass);
//Headers table
this.headTbl = createElm('table');
let tH = createElm('tHead');
//1st row should be headers row, ids are added if not set
//Those ids are used by the sort feature
let hRow = tbl.rows[this.headRowIndex];
let sortTriggers = this.getSortTriggerIds(hRow);
//Filters row is created
let filtersRow = this.createFiltersRow();
//Headers row are moved from content table to headers table
this.setHeadersRow(tH);
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
let thead = tag(tbl, 'thead');
if (thead.length > 0) {
tbl.removeChild(thead[0]);
}
// ensure table layout is always set even if already set in css
// definitions, potentially with custom css class this could be lost
this.headTbl.style.tableLayout = 'fixed';
tbl.style.tableLayout = 'fixed';
//content table without headers needs col widths to be reset
tf.setColWidths(this.headTbl);
//Headers container width
this.headTbl.style.width = tbl.style.width;
//
//scroll synchronisation
addEvt(this.tblCont, 'scroll', (evt) => {
let elm = targetEvt(evt);
let scrollLeft = elm.scrollLeft;
this.headTblCont.scrollLeft = scrollLeft;
//New pointerX calc taking into account scrollLeft
// if(!o.isPointerXOverwritten){
// try{
// o.Evt.pointerX = function(evt){
// let e = evt || global.event;
// let bdScrollLeft = tf_StandardBody().scrollLeft +
// scrollLeft;
// return (e.pageX + scrollLeft) ||
// (e.clientX + bdScrollLeft);
// };
// o.isPointerXOverwritten = true;
// } catch(err) {
// o.isPointerXOverwritten = false;
// }
// }
});
// TODO: Trigger a custom event handled by sort extension
let sort = tf.extension('sort');
if (sort) {
sort.asyncSort = true;
sort.triggerIds = sortTriggers;
}
//Col elements are enough to keep column widths after sorting and
//filtering
this.setColumnElements();
if (tf.popupFilters) {
filtersRow.style.display = NONE;
}
/** @inherited */
this.initialized = true;
}
/**
* Overrides TableFilter instance properties to adjust to grid layout mode
* @private
*/
setOverrides() {
let tf = this.tf;
tf.refRow = 0;
tf.headersRow = 0;
tf.filtersRowIndex = 1;
}
/**
* Set grid-layout default column widths if column widths are not defined
* @private
*/
setDefaultColWidths() {
let tf = this.tf;
if (tf.colWidths.length > 0) {
return;
}
for (let k = 0, len = tf.getCellsNb(); k < len; k++) {
let colW;
let cell = tf.dom().rows[tf.getHeadersRowIndex()].cells[k];
if (cell.width !== '') {
colW = cell.width;
} else if (cell.style.width !== '') {
colW = parseInt(cell.style.width, 10);
} else {
colW = this.defaultColWidth;
}
tf.colWidths[k] = colW;
}
tf.setColWidths();
}
/**
* Initial table width
* @returns {Number}
* @private
*/
initialTableWidth() {
let tbl = this.tf.dom();
let width; //initial table width
if (tbl.width !== '') {
width = tbl.width;
}
else if (tbl.style.width !== '') {
width = tbl.style.width;
} else {
width = tbl.clientWidth;
}
return parseInt(width, 10);
}
/**
* Creates container element
* @param {String} tag Tag name
* @param {String} className Css class to assign to element
* @returns {DOMElement}
* @private
*/
createContainer(tag, className) {
let element = createElm(tag);
element.className = className;
return element;
}
/**
* Creates filters row with cells
* @returns {HTMLTableRowElement}
* @private
*/
createFiltersRow() {
let tf = this.tf;
let filtersRow = createElm('tr');
if (this.filters && tf.fltGrid) {
tf.externalFltTgtIds = [];
for (let j = 0; j < tf.getCellsNb(); j++) {
let fltTdId = `${tf.prfxFlt + j + this.prfxGridFltTd + tf.id}`;
let cl = createElm(tf.fltCellTag, ['id', fltTdId]);
filtersRow.appendChild(cl);
tf.externalFltTgtIds[j] = fltTdId;
}
}
return filtersRow;
}
/**
* Generates column elements if necessary and assigns their widths
* @private
*/
setColumnElements() {
let tf = this.tf;
let cols = tag(tf.dom(), 'col');
this.tblHasColTag = cols.length > 0;
for (let k = (tf.nbCells - 1); k >= 0; k--) {
let col;
if (!this.tblHasColTag) {
col = createElm('col');
tf.dom().insertBefore(col, tf.dom().firstChild);
} else {
col = cols[k];
}
col.style.width = tf.colWidths[k];
this.colElms[k] = col;
}
this.tblHasColTag = true;
}
/**
* Sets headers row in headers table
* @param {HTMLHeadElement} tableHead Table head element
* @private
*/
setHeadersRow(tableHead) {
if (this.noHeaders) {
// Handle table with no headers, assuming here headers do not
// exist
tableHead.appendChild(createElm('tr'));
} else {
// Headers row are moved from content table to headers table
for (let i = 0; i < this.headRows.length; i++) {
let row = this.tf.dom().rows[this.headRows[i]];
tableHead.appendChild(row);
}
}
}
/**
* Sets width defined in configuration to passed element
* @param {DOMElement} element DOM element
* @private
*/
setConfigWidth(element) {
if (!this.width) {
return;
}
if (this.width.indexOf('%') !== -1) {
element.style.width = '100%';
} else {
element.style.width = this.width;
}
}
/**
* Returns a list of header IDs used for specifing external sort triggers
* @param {HTMLTableRowElement} row DOM row element
* @returns {Array} List of IDs
* @private
*/
getSortTriggerIds(row) {
let tf = this.tf;
let sortTriggers = [];
for (let n = 0; n < tf.getCellsNb(); n++) {
let c = row.cells[n];
let thId = c.getAttribute('id');
if (!thId || thId === '') {
thId = `${this.prfxGridTh + n}_${tf.id}`;
c.setAttribute('id', thId);
}
sortTriggers.push(thId);
}
return sortTriggers;
}
/**
* Removes the grid layout
*/
destroy() {
let tf = this.tf;
let tbl = tf.dom();
if (!this.initialized) {
return;
}
let t = removeElm(tbl);
this.tblMainCont.parentNode.insertBefore(t, this.tblMainCont);
removeElm(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 for future usage
this.tf.tbl = elm(tf.id);
this.initialized = false;
}
}
-126
View File
@@ -1,126 +0,0 @@
import {addEvt, removeEvt} from '../event';
import {root} from '../root';
const JSON = root.JSON;
const location = root.location;
const decodeURIComponent = root.decodeURIComponent;
const encodeURIComponent = root.encodeURIComponent;
/**
* Checks if browser has onhashchange event
*/
export const hasHashChange = () => {
let docMode = root.documentMode;
return ('onhashchange' in root) && (docMode === undefined || docMode > 7);
};
/**
* Manages state via URL hash changes
*
* @export
* @class Hash
*/
export class Hash {
/**
* Creates an instance of Hash
*
* @param {State} state Instance of State
*/
constructor(state) {
/**
* State object
* @type {State}
*/
this.state = state;
/**
* Cached URL hash
* @type {String} Hash string
* @private
*/
this.lastHash = null;
/**
* Application event emitter instance
* @type {Emitter}
*/
this.emitter = state.emitter;
/**
* Bound sync wrapper for future use
* @private
*/
this.boundSync = null;
}
/**
* Initializes the Hash object
*/
init() {
if (!hasHashChange()) {
return;
}
this.lastHash = location.hash;
//Store a bound sync wrapper
this.boundSync = this.sync.bind(this);
this.emitter.on(['state-changed'], (tf, state) => this.update(state));
this.emitter.on(['initialized'], this.boundSync);
addEvt(root, 'hashchange', this.boundSync);
}
/**
* Updates the URL hash based on a state change
*
* @param {State} state Instance of State
*/
update(state) {
let hash = `#${encodeURIComponent(JSON.stringify(state))}`;
if (this.lastHash === hash) {
return;
}
location.hash = hash;
this.lastHash = hash;
}
/**
* Converts a URL hash into a state JSON object
*
* @param {String} hash URL hash fragment
* @returns {Object} JSON object
*/
parse(hash) {
if (hash.indexOf('#') === -1) {
return null;
}
hash = hash.substr(1);
return JSON.parse(decodeURIComponent(hash));
}
/**
* Applies current hash state to features
*/
sync() {
let state = this.parse(location.hash);
if (!state) {
return;
}
// override current state with persisted one and sync features
this.state.overrideAndSync(state);
}
/**
* Release Hash event subscriptions and clear fields
*/
destroy() {
this.emitter.off(['state-changed'], (tf, state) => this.update(state));
this.emitter.off(['initialized'], this.boundSync);
removeEvt(root, 'hashchange', this.boundSync);
this.state = null;
this.lastHash = null;
this.emitter = null;
}
}
-230
View File
@@ -1,230 +0,0 @@
import {Feature} from '../feature';
import {createElm, createText, elm, removeElm} from '../dom';
import {addEvt, targetEvt, removeEvt} from '../event';
import {NONE} from '../const';
import {root} from '../root';
import {isEmpty} from '../types';
import {defaultsStr} from '../settings';
const WIKI_URL = 'https://github.com/koalyptus/TableFilter/wiki/' +
'4.-Filter-operators';
const WEBSITE_URL = 'http://koalyptus.github.io/TableFilter/';
/**
* Help UI component
*/
export class Help extends Feature {
/**
* Creates an instance of Help
* @param {TableFilter} tf TableFilter instance
*/
constructor(tf) {
super(tf, 'help');
let f = this.config.help_instructions || {};
/**
* ID of main custom container element
* @type {String}
*/
this.tgtId = defaultsStr(f.target_id, null);
/**
* ID of custom container element for instructions
* @type {String}
*/
this.contTgtId = defaultsStr(f.container_target_id, null);
/**
* Instructions text (accepts HTML)
* @type {String}
*/
this.instrText = !isEmpty(f.text) ? f.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/>';
/**
* Instructions HTML
* @type {String}
*/
this.instrHtml = defaultsStr(f.html, null);
/**
* Help button text ('?')
* @type {String}
*/
this.btnText = defaultsStr(f.btn_text, '?');
/**
* Custom help button HTML
* @type {String}
*/
this.btnHtml = defaultsStr(f.btn_html, null);
/**
* Css class for help button
* @type {String}
*/
this.btnCssClass = defaultsStr(f.btn_css_class, 'helpBtn');
/**
* Css class for help container element
* @type {String}
*/
this.contCssClass = defaultsStr(f.container_css_class, 'helpCont');
/**
* Button DOM element
* @type {DOMElement}
*/
this.btn = null;
/**
* Help container DOM element
* @type {DOMElement}
*/
this.cont = null;
/**
* Bound mouseup wrapper
* @private
*/
this.boundMouseup = null;
/**
* Default HTML appended to instructions text
* @type {String}
*/
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>';
this.emitter.on(['init-help'], () => this.init());
}
/**
* Mouse-up event handler handling popup auto-close behaviour
* @private
*/
onMouseup(evt) {
let targetElm = targetEvt(evt);
while (targetElm && targetElm !== this.cont && targetElm !== this.btn) {
targetElm = targetElm.parentNode;
}
if (targetElm !== this.cont && targetElm !== this.btn) {
this.toggle();
}
return;
}
/**
* Initialise Help instance
*/
init() {
if (this.initialized) {
return;
}
let tf = this.tf;
let btn = createElm('span');
let cont = createElm('div');
this.boundMouseup = this.onMouseup.bind(this);
//help button is added to defined element
if (!this.tgtId) {
tf.setToolbar();
}
let targetEl = !this.tgtId ? tf.rDiv : elm(this.tgtId);
targetEl.appendChild(btn);
let divContainer = !this.contTgtId ? btn : elm(this.contTgtId);
if (!this.btnHtml) {
divContainer.appendChild(cont);
let helplink = createElm('a', ['href', 'javascript:void(0);']);
helplink.className = this.btnCssClass;
helplink.appendChild(createText(this.btnText));
btn.appendChild(helplink);
addEvt(helplink, 'click', () => this.toggle());
} else {
btn.innerHTML = this.btnHtml;
let helpEl = btn.firstChild;
addEvt(helpEl, 'click', () => this.toggle());
divContainer.appendChild(cont);
}
if (!this.instrHtml) {
cont.innerHTML = this.instrText;
cont.className = this.contCssClass;
} else {
if (this.contTgtId) {
divContainer.appendChild(cont);
}
cont.innerHTML = this.instrHtml;
if (!this.contTgtId) {
cont.className = this.contCssClass;
}
}
cont.innerHTML += this.defaultHtml;
addEvt(cont, 'click', () => this.toggle());
this.cont = cont;
this.btn = btn;
/** @inherited */
this.initialized = true;
}
/**
* Toggle help pop-up
*/
toggle() {
// check only if explicitily disabled as in this case undefined
// signifies the help feature is enabled by default
if (!this.isEnabled()) {
return;
}
// ensure mouseup event handler is removed
removeEvt(root, 'mouseup', this.boundMouseup);
let divDisplay = this.cont.style.display;
if (divDisplay === '' || divDisplay === NONE) {
this.cont.style.display = 'inline';
addEvt(root, 'mouseup', this.boundMouseup);
} else {
this.cont.style.display = NONE;
}
}
/**
* Remove help UI
*/
destroy() {
if (!this.initialized) {
return;
}
removeElm(this.btn);
this.btn = null;
removeElm(this.cont);
this.cont = null;
this.boundMouseup = null;
this.initialized = false;
}
}
@@ -1,180 +0,0 @@
import {createText, createElm, getText} from '../dom';
import {isArray} from '../types';
import {rgxEsc} from '../string';
import {defaultsStr} from '../settings';
/**
* Highlight matched keywords upon filtering
*
* @export
* @class HighlightKeyword
*/
export class HighlightKeyword {
/**
* Creates an instance of HighlightKeyword
* @param {TableFilter} tf TableFilter instance
*/
constructor(tf) {
let f = tf.config();
/**
* Css class for highlighted term
* @type {String}
*/
this.highlightCssClass = defaultsStr(f.highlight_css_class, 'keyword');
/**
* TableFilter instance
* @type {TableFilter}
*/
this.tf = tf;
/**
* TableFilter's emitter instance
* @type {Emitter}
*/
this.emitter = tf.emitter;
}
/**
* Initializes HighlightKeyword instance
*/
init() {
this.emitter.on(
['before-filtering', 'destroy'],
() => this.unhighlightAll()
);
this.emitter.on(
['highlight-keyword'],
(tf, cell, term) => this._processTerm(cell, term)
);
}
/**
* Highlight occurences of searched term in passed node
* @param {Node} node
* @param {String} term Searched term
* @param {String} cssClass Css class name
*
* TODO: refactor this method
*/
highlight(node, term, cssClass) {
// Iterate into this nodes childNodes
if (node.hasChildNodes) {
let children = node.childNodes;
for (let i = 0; i < children.length; i++) {
this.highlight(children[i], term, cssClass);
}
}
if (node.nodeType === 3) {
let nodeVal = node.nodeValue.toLowerCase();
let termIdx = nodeVal.indexOf(term.toLowerCase());
if (termIdx !== -1) {
let pn = node.parentNode;
if (pn && pn.className !== cssClass) {
// term not highlighted yet
let nv = node.nodeValue,
// Create a load of replacement nodes
before = createText(nv.substr(0, termIdx)),
value = nv.substr(termIdx, term.length),
after = createText(nv.substr(termIdx + term.length)),
text = createText(value),
container = createElm('span');
container.className = cssClass;
container.appendChild(text);
pn.insertBefore(before, node);
pn.insertBefore(container, node);
pn.insertBefore(after, node);
pn.removeChild(node);
}
}
}
}
/**
* Removes highlight to nodes matching passed string
* @param {String} term
* @param {String} cssClass Css class to remove
*/
unhighlight(term, cssClass) {
let highlightedNodes = this.tf.dom().querySelectorAll(`.${cssClass}`);
for (let i = 0; i < highlightedNodes.length; i++) {
let n = highlightedNodes[i];
let nodeVal = getText(n);
if (nodeVal.toLowerCase().indexOf(term.toLowerCase()) !== -1) {
let parentNode = n.parentNode;
parentNode.replaceChild(createText(nodeVal), n);
parentNode.normalize();
}
}
}
/**
* Clear all occurrences of highlighted nodes
*/
unhighlightAll() {
if (!this.tf.highlightKeywords) {
return;
}
// iterate filters values to unhighlight all values
this.tf.getFiltersValue().forEach((val) => {
if (isArray(val)) {
val.forEach((item) =>
this.unhighlight(item, this.highlightCssClass));
} else {
this.unhighlight(val, this.highlightCssClass);
}
});
}
/** Remove feature */
destroy() {
this.emitter.off(
['before-filtering', 'destroy'],
() => this.unhighlightAll()
);
this.emitter.off(
['highlight-keyword'],
(tf, cell, term) => this._processTerm(cell, term)
);
}
/**
* Ensure filtering operators are handled before highlighting any match
* @param {any} Table cell to look searched term into
* @param {any} Searched termIdx
*/
_processTerm(cell, term) {
let tf = this.tf;
let reLk = new RegExp(rgxEsc(tf.lkOperator));
let reEq = new RegExp(tf.eqOperator);
let reSt = new RegExp(tf.stOperator);
let reEn = new RegExp(tf.enOperator);
let reLe = new RegExp(tf.leOperator);
let reGe = new RegExp(tf.geOperator);
let reL = new RegExp(tf.lwOperator);
let reG = new RegExp(tf.grOperator);
let reD = new RegExp(tf.dfOperator);
term = term
.replace(reLk, '')
.replace(reEq, '')
.replace(reSt, '')
.replace(reEn, '');
if (reLe.test(term) || reGe.test(term) || reL.test(term) ||
reG.test(term) || reD.test(term)) {
term = getText(cell);
}
if (term === '') {
return;
}
this.highlight(cell, term, this.highlightCssClass);
}
}
-171
View File
@@ -1,171 +0,0 @@
import {Feature} from '../feature';
import {createElm, createText, elm, removeElm} from '../dom';
import {EMPTY_FN} from '../types';
import {root} from '../root';
import {NONE} from '../const';
import {defaultsStr, defaultsFn} from '../settings';
const EVENTS = [
'before-filtering',
'before-populating-filter',
'before-page-change',
'before-clearing-filters',
'before-page-length-change',
'before-reset-page',
'before-reset-page-length',
'before-loading-extensions',
'before-loading-themes'
];
/**
* Activity indicator
*
* @export
* @class Loader
* @extends {Feature}
*/
export class Loader extends Feature {
/**
* Creates an instance of Loader.
*
* @param {TableFilter} tf TableFilter instance
*/
constructor(tf) {
super(tf, 'loader');
let f = this.config.loader || {};
/**
* ID of custom container element
* @type {String}
*/
this.targetId = defaultsStr(f.target_id, null);
/**
* Loader container DOM element
* @type {DOMElement}
*/
this.cont = null;
/**
* Text displayed when indicator is visible
* @type {String}
*/
this.text = defaultsStr(f.text, 'Loading...');
/**
* Custom HTML injected in Loader's container element
* @type {String}
*/
this.html = defaultsStr(f.html, null);
/**
* Css class for Loader's container element
* @type {String}
*/
this.cssClass = defaultsStr(f.css_class, 'loader');
/**
* Close delay in milliseconds
* @type {Number}
*/
this.closeDelay = 250;
/**
* Callback fired when loader is displayed
* @type {Function}
*/
this.onShow = defaultsFn(f.on_show_loader, EMPTY_FN);
/**
* Callback fired when loader is closed
* @type {Function}
*/
this.onHide = defaultsFn(f.on_hide_loader, EMPTY_FN);
}
/**
* Initializes Loader instance
*/
init() {
if (this.initialized) {
return;
}
let tf = this.tf;
let emitter = this.emitter;
let containerDiv = createElm('div');
containerDiv.className = this.cssClass;
let targetEl = !this.targetId ?
tf.dom().parentNode : elm(this.targetId);
if (!this.targetId) {
targetEl.insertBefore(containerDiv, tf.dom());
} else {
targetEl.appendChild(containerDiv);
}
this.cont = containerDiv;
if (!this.html) {
this.cont.appendChild(createText(this.text));
} else {
this.cont.innerHTML = this.html;
}
this.show(NONE);
// Subscribe to events
emitter.on(EVENTS, () => this.show(''));
emitter.on(EVENTS, () => this.show(NONE));
/** @inherited */
this.initialized = true;
}
/**
* Shows or hides activity indicator
* @param {String} Two possible values: '' or 'none'
*/
show(p) {
if (!this.isEnabled()) {
return;
}
let displayLoader = () => {
if (!this.cont) {
return;
}
if (p !== NONE) {
this.onShow(this);
}
this.cont.style.display = p;
if (p === NONE) {
this.onHide(this);
}
};
let t = p === NONE ? this.closeDelay : 1;
root.setTimeout(displayLoader, t);
}
/**
* Removes feature
*/
destroy() {
if (!this.initialized) {
return;
}
let emitter = this.emitter;
removeElm(this.cont);
this.cont = null;
// Unsubscribe to events
emitter.off(EVENTS, () => this.show(''));
emitter.off(EVENTS, () => this.show(NONE));
this.initialized = false;
}
}
@@ -1,108 +0,0 @@
import {Feature} from '../feature';
import {addClass, removeClass, hasClass} from '../dom';
import {EMPTY_FN} from '../types';
import {defaultsStr, defaultsFn} from '../settings';
/**
* Visual indicator for filtered columns
* @export
* @class MarkActiveColumns
* @extends {Feature}
*/
export class MarkActiveColumns extends Feature {
/**
* Create an instance of MarkActiveColumns
* @param {TableFilter} tf TableFilter instance
*/
constructor(tf) {
super(tf, 'markActiveColumns');
let config = this.config.mark_active_columns || {};
/**
* Css class for filtered (active) columns
* @type {String}
*/
this.headerCssClass = defaultsStr(config.header_css_class,
'activeHeader');
/**
* Callback fired before a column is marked as filtered
* @type {Function}
*/
this.onBeforeActiveColumn = defaultsFn(config.on_before_active_column,
EMPTY_FN);
/**
* Callback fired after a column is marked as filtered
* @type {Function}
*/
this.onAfterActiveColumn = defaultsFn(config.on_after_active_column,
EMPTY_FN);
}
/**
* Initialise MarkActiveColumns instance
*/
init() {
if (this.initialized) {
return;
}
this.emitter.on(['before-filtering'], () => this.clearActiveColumns());
this.emitter.on(
['cell-processed'],
(tf, colIndex) => this.markActiveColumn(colIndex)
);
/** @inherited */
this.initialized = true;
}
/**
* Clear filtered columns visual indicator (background color)
*/
clearActiveColumns() {
let tf = this.tf;
let len = tf.getCellsNb();
for (let i = 0; i < len; i++) {
removeClass(tf.getHeaderElement(i), this.headerCssClass);
}
}
/**
* Mark currently filtered column
* @param {Number} colIndex Column index
*/
markActiveColumn(colIndex) {
let header = this.tf.getHeaderElement(colIndex);
if (hasClass(header, this.headerCssClass)) {
return;
}
this.onBeforeActiveColumn(this, colIndex);
addClass(header, this.headerCssClass);
this.onAfterActiveColumn(this, colIndex);
}
/**
* Remove feature
*/
destroy() {
if (!this.initialized) {
return;
}
this.clearActiveColumns();
this.emitter.off(['before-filtering'], () => this.clearActiveColumns());
this.emitter.off(
['cell-processed'],
(tf, colIndex) => this.markActiveColumn(colIndex)
);
/** @inherited */
this.initialized = false;
}
}
@@ -1,194 +0,0 @@
import {Feature} from '../feature';
import {createElm, elm, removeElm} from '../dom';
import {isEmpty, EMPTY_FN} from '../types';
import {NONE} from '../const';
import {defaultsStr, defaultsFn} from '../settings';
/**
* UI when filtering yields no matches
* @export
* @class NoResults
* @extends {Feature}
*/
export class NoResults extends Feature {
/**
* Creates an instance of NoResults
* @param {TableFilter} tf TableFilter instance
*/
constructor(tf) {
super(tf, 'noResults');
//configuration object
let f = this.config.no_results_message || {};
/**
* Text (accepts HTML)
* @type {String}
*/
this.content = defaultsStr(f.content, 'No results');
/**
* Custom container DOM element
* @type {DOMElement}
*/
this.customContainer = defaultsStr(f.custom_container, null);
/**
* ID of custom container element
* @type {String}
*/
this.customContainerId = defaultsStr(f.custom_container_id, null);
/**
* Indicates if UI is contained in a external element
* @type {Boolean}
* @private
*/
this.isExternal = !isEmpty(this.customContainer) ||
!isEmpty(this.customContainerId);
/**
* Css class assigned to container element
* @type {String}
*/
this.cssClass = defaultsStr(f.css_class, 'no-results');
/**
* Stores container DOM element
* @type {DOMElement}
*/
this.cont = null;
/**
* Callback fired before the message is displayed
* @type {Function}
*/
this.onBeforeShow = defaultsFn(f.on_before_show_msg, EMPTY_FN);
/**
* Callback fired after the message is displayed
* @type {Function}
*/
this.onAfterShow = defaultsFn(f.on_after_show_msg, EMPTY_FN);
/**
* Callback fired before the message is hidden
* @type {Function}
*/
this.onBeforeHide = defaultsFn(f.on_before_hide_msg, EMPTY_FN);
/**
* Callback fired after the message is hidden
* @type {Function}
*/
this.onAfterHide = defaultsFn(f.on_after_hide_msg, EMPTY_FN);
}
/**
* Initializes NoResults instance
*/
init() {
if (this.initialized) {
return;
}
let tf = this.tf;
let target = this.customContainer || elm(this.customContainerId) ||
tf.dom();
//container
let cont = createElm('div');
cont.className = this.cssClass;
cont.innerHTML = this.content;
if (this.isExternal) {
target.appendChild(cont);
} else {
target.parentNode.insertBefore(cont, target.nextSibling);
}
this.cont = cont;
// subscribe to after-filtering event
this.emitter.on(['after-filtering'], () => this.toggle());
/** @inherited */
this.initialized = true;
this.hide();
}
/**
* Toggle no results message
*/
toggle() {
if (this.tf.getValidRowsNb() > 0) {
this.hide();
} else {
this.show();
}
}
/**
* Show no results message
*/
show() {
if (!this.initialized || !this.isEnabled()) {
return;
}
this.onBeforeShow(this.tf, this);
this.setWidth();
this.cont.style.display = 'block';
this.onAfterShow(this.tf, this);
}
/**
* Hide no results message
*/
hide() {
if (!this.initialized || !this.isEnabled()) {
return;
}
this.onBeforeHide(this.tf, this);
this.cont.style.display = NONE;
this.onAfterHide(this.tf, this);
}
/**
* Sets no results container width
* @private
*/
setWidth() {
if (!this.initialized || this.isExternal || !this.isEnabled()) {
return;
}
let tf = this.tf;
if (tf.gridLayout) {
let gridLayout = tf.feature('gridLayout');
this.cont.style.width = gridLayout.headTbl.clientWidth + 'px';
} else {
this.cont.style.width = (tf.dom().tHead ?
tf.dom().tHead.clientWidth :
tf.dom().tBodies[0].clientWidth) + 'px';
}
}
/**
* Remove feature
*/
destroy() {
if (!this.initialized) {
return;
}
removeElm(this.cont);
this.cont = null;
// unsubscribe to after-filtering event
this.emitter.off(['after-filtering'], () => this.toggle());
this.initialized = false;
}
}
-898
View File
@@ -1,898 +0,0 @@
import {Feature} from '../feature';
import {createElm, createOpt, createText, elm, removeElm} from '../dom';
import {isArray, isNull, EMPTY_FN} from '../types';
import {addEvt, keyCode, removeEvt} from '../event';
import {INPUT, SELECT, NONE, ENTER_KEY} from '../const';
import {
defaultsStr, defaultsNb, defaultsBool, defaultsArr, defaultsFn
} from '../settings';
/**
* Paging UI component
* @export
* @class Paging
* @extends {Feature}
*/
export class Paging extends Feature {
/**
* Creates an instance of Paging
* @param {TableFilter} tf TableFilter instance
*/
constructor(tf) {
super(tf, 'paging');
// Configuration object
let f = this.config.paging || {};
/**
* Css class for the paging buttons (previous, next, etc.)
* @type {String}
*/
this.btnCssClass = defaultsStr(f.btn_css_class, 'pgInp');
/**
* Main select DOM element
* @type {DOMElement}
*/
this.pageSlc = null;
/**
* Results per page select DOM element
* @type {DOMElement}
*/
this.pageLengthSlc = null;
/**
* ID of custom container element
* @type {String}
*/
this.tgtId = defaultsStr(f.target_id, null);
/**
* Number of rows contained in a page
* @type {Number}
*/
this.pageLength = defaultsNb(f.length, 10);
/**
* ID of custom container element for the results per page selector
* @type {String}
*/
this.pageLengthTgtId = defaultsStr(f.results_per_page_target_id, null);
/**
* Css class for the paging select element
* @type {String}
*/
this.pgSlcCssClass = defaultsStr(f.slc_css_class, 'pgSlc');
/**
* Css class for the paging input element
* @type {String}
*/
this.pgInpCssClass = defaultsStr(f.inp_css_class, 'pgNbInp');
/**
* Label and values for the results per page select, example of usage:
* ['Records: ', [10,25,50,100]]
* @type {Array}
*/
this.resultsPerPage = defaultsArr(f.results_per_page, null);
/**
* Determines if results per page is configured
* @type {Boolean}
*/
this.hasResultsPerPage = isArray(this.resultsPerPage);
/**
* Css class for the results per page select
* @type {String}
*/
this.resultsSlcCssClass = defaultsStr(f.results_slc_css_class, 'rspg');
/**
* Css class for the label preceding results per page select
* @type {String}
*/
this.resultsSpanCssClass = defaultsStr(f.results_span_css_class,
'rspgSpan');
/**
* Index of the first row of current page
* @type {Number}
* @private
*/
this.startPagingRow = 0;
/**
* Total number of pages
* @type {Number}
* @private
*/
this.nbPages = 0;
/**
* Current page number
* @type {Number}
* @private
*/
this.currentPageNb = 1;
/**
* Next page button text
* @type {String}
*/
this.btnNextPageText = defaultsStr(f.btn_next_page_text, '>');
/**
* Previous page button text
* @type {String}
*/
this.btnPrevPageText = defaultsStr(f.btn_prev_page_text, '<');
/**
* Last page button text
* @type {String}
*/
this.btnLastPageText = defaultsStr(f.btn_last_page_text, '>|');
/**
* First page button text
* @type {String}
*/
this.btnFirstPageText = defaultsStr(f.btn_first_page_text, '|<');
/**
* Next page button HTML
* @type {String}
*/
this.btnNextPageHtml = defaultsStr(f.btn_next_page_html,
(!tf.enableIcons ? null :
'<input type="button" value="" class="' + this.btnCssClass +
' nextPage" title="Next page" />'));
/**
* Previous page button HTML
* @type {String}
*/
this.btnPrevPageHtml = defaultsStr(f.btn_prev_page_html,
(!tf.enableIcons ? null :
'<input type="button" value="" class="' + this.btnCssClass +
' previousPage" title="Previous page" />'));
/**
* First page button HTML
* @type {String}
*/
this.btnFirstPageHtml = defaultsStr(f.btn_first_page_html,
(!tf.enableIcons ? null :
'<input type="button" value="" class="' + this.btnCssClass +
' firstPage" title="First page" />'));
/**
* Last page button HTML
* @type {String}
*/
this.btnLastPageHtml = defaultsStr(f.btn_last_page_html,
(!tf.enableIcons ? null :
'<input type="button" value="" class="' + this.btnCssClass +
' lastPage" title="Last page" />'));
/**
* Text preceeding page selector drop-down
* @type {String}
*/
this.pageText = defaultsStr(f.page_text, ' Page ');
/**
* Text after page selector drop-down
* @type {String}
*/
this.ofText = defaultsStr(f.of_text, ' of ');
/**
* Css class for the span containing total number of pages
* @type {String}
*/
this.nbPgSpanCssClass = defaultsStr(f.nb_pages_css_class, 'nbpg');
/**
* Determines if paging buttons are enabled (default: true)
* @type {Boolean}
*/
this.hasBtns = defaultsBool(f.btns, true);
/**
* Defines page selector type, two possible values: 'select', 'input'
* @type {String}
*/
this.pageSelectorType = defaultsStr(f.page_selector_type, SELECT);
/**
* Callback fired before the page is changed
* @type {Function}
*/
this.onBeforeChangePage = defaultsFn(f.on_before_change_page, EMPTY_FN);
/**
* Callback fired after the page is changed
* @type {Function}
*/
this.onAfterChangePage = defaultsFn(f.on_after_change_page, EMPTY_FN);
/**
* Label preciding results per page select
* @type {DOMElement}
* @private
*/
this.slcResultsTxt = null;
/**
* Span containing next page button
* @type {DOMElement}
* @private
*/
this.btnNextCont = null;
/**
* Span containing previous page button
* @type {DOMElement}
* @private
*/
this.btnPrevCont = null;
/**
* Span containing last page button
* @type {DOMElement}
* @private
*/
this.btnLastCont = null;
/**
* Span containing first page button
* @type {DOMElement}
* @private
*/
this.btnFirstCont = null;
/**
* Span for tot nb pages
* @type {DOMElement}
* @private
*/
this.pgCont = null;
/**
* Span preceding pages select (contains 'Page')
* @type {DOMElement}
* @private
*/
this.pgBefore = null;
/**
* Span following pages select (contains ' of ')
* @type {DOMElement}
* @private
*/
this.pgAfter = null;
let startRow = tf.refRow;
let nrows = tf.getRowsNb(true);
//calculates page nb
this.nbPages = Math.ceil((nrows - startRow) / this.pageLength);
let o = this;
/**
* Paging DOM events handlers
* @type {String}
* @private
*/
this.evt = {
slcIndex() {
return (o.pageSelectorType === SELECT) ?
o.pageSlc.options.selectedIndex :
parseInt(o.pageSlc.value, 10) - 1;
},
nbOpts() {
return (o.pageSelectorType === SELECT) ?
parseInt(o.pageSlc.options.length, 10) - 1 :
(o.nbPages - 1);
},
next() {
let nextIndex = o.evt.slcIndex() < o.evt.nbOpts() ?
o.evt.slcIndex() + 1 : 0;
o.changePage(nextIndex);
},
prev() {
let 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) {
let key = keyCode(e);
if (key === ENTER_KEY) {
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() {
let slcPages;
let tf = this.tf;
let 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.pageLength = this.resultsPerPage[1][0];
this.setResultsPerPage();
}
}
evt.slcPagesChange = (event) => {
let slc = event.target;
this.changePage(slc.selectedIndex);
};
// Paging drop-down list selector
if (this.pageSelectorType === SELECT) {
slcPages = createElm(SELECT);
slcPages.className = this.pgSlcCssClass;
addEvt(slcPages, 'change', evt.slcPagesChange);
}
// Paging input selector
if (this.pageSelectorType === INPUT) {
slcPages = createElm(INPUT, ['value', this.currentPageNb]);
slcPages.className = this.pgInpCssClass;
addEvt(slcPages, 'keypress', evt._detectKey);
}
// btns containers
let btnNextSpan = createElm('span');
let btnPrevSpan = createElm('span');
let btnLastSpan = createElm('span');
let btnFirstSpan = createElm('span');
if (this.hasBtns) {
// Next button
if (!this.btnNextPageHtml) {
let btnNext = createElm(INPUT,
['type', 'button'],
['value', this.btnNextPageText],
['title', 'Next']
);
btnNext.className = this.btnCssClass;
addEvt(btnNext, 'click', evt.next);
btnNextSpan.appendChild(btnNext);
} else {
btnNextSpan.innerHTML = this.btnNextPageHtml;
addEvt(btnNextSpan, 'click', evt.next);
}
// Previous button
if (!this.btnPrevPageHtml) {
let btnPrev = createElm(INPUT,
['type', 'button'],
['value', this.btnPrevPageText],
['title', 'Previous']
);
btnPrev.className = this.btnCssClass;
addEvt(btnPrev, 'click', evt.prev);
btnPrevSpan.appendChild(btnPrev);
} else {
btnPrevSpan.innerHTML = this.btnPrevPageHtml;
addEvt(btnPrevSpan, 'click', evt.prev);
}
// Last button
if (!this.btnLastPageHtml) {
let btnLast = createElm(INPUT,
['type', 'button'],
['value', this.btnLastPageText],
['title', 'Last']
);
btnLast.className = this.btnCssClass;
addEvt(btnLast, 'click', evt.last);
btnLastSpan.appendChild(btnLast);
} else {
btnLastSpan.innerHTML = this.btnLastPageHtml;
addEvt(btnLastSpan, 'click', evt.last);
}
// First button
if (!this.btnFirstPageHtml) {
let btnFirst = createElm(INPUT,
['type', 'button'],
['value', this.btnFirstPageText],
['title', 'First']
);
btnFirst.className = this.btnCssClass;
addEvt(btnFirst, 'click', evt.first);
btnFirstSpan.appendChild(btnFirst);
} else {
btnFirstSpan.innerHTML = this.btnFirstPageHtml;
addEvt(btnFirstSpan, 'click', evt.first);
}
}
// paging elements (buttons+drop-down list) are added to defined element
if (!this.tgtId) {
tf.setToolbar();
}
let targetEl = !this.tgtId ? tf.mDiv : elm(this.tgtId);
targetEl.appendChild(btnFirstSpan);
targetEl.appendChild(btnPrevSpan);
let pgBeforeSpan = createElm('span');
pgBeforeSpan.appendChild(createText(this.pageText));
pgBeforeSpan.className = this.nbPgSpanCssClass;
targetEl.appendChild(pgBeforeSpan);
targetEl.appendChild(slcPages);
let pgAfterSpan = createElm('span');
pgAfterSpan.appendChild(createText(this.ofText));
pgAfterSpan.className = this.nbPgSpanCssClass;
targetEl.appendChild(pgAfterSpan);
let pgSpan = createElm('span');
pgSpan.className = this.nbPgSpanCssClass;
pgSpan.appendChild(createText(' ' + this.nbPages + ' '));
targetEl.appendChild(pgSpan);
targetEl.appendChild(btnNextSpan);
targetEl.appendChild(btnLastSpan);
this.btnNextCont = btnNextSpan;
this.btnPrevCont = btnPrevSpan;
this.btnLastCont = btnLastSpan;
this.btnFirstCont = btnFirstSpan;
this.pgCont = pgSpan;
this.pgBefore = pgBeforeSpan;
this.pgAfter = pgAfterSpan;
this.pageSlc = slcPages;
this.setPagingInfo();
if (!tf.fltGrid) {
tf.validateAllRows();
this.setPagingInfo(tf.validRowsIndex);
}
this.emitter.on(['after-filtering'], () => this.resetPagingInfo());
this.emitter.on(['change-page'],
(tf, pageNumber) => this.setPage(pageNumber));
this.emitter.on(['change-page-results'],
(tf, pageLength) => this.changeResultsPerPage(pageLength));
/** @inherited */
this.initialized = true;
}
/**
* Reset paging when filters are already instantiated
* @param {Boolean} filterTable Execute filtering once paging instanciated
*/
reset(filterTable = false) {
this.enable();
this.init();
if (filterTable) {
this.tf.filter();
}
}
/**
* Reset paging info from scratch after a filtering process
*/
resetPagingInfo() {
this.startPagingRow = 0;
this.currentPageNb = 1;
this.setPagingInfo(this.tf.validRowsIndex);
}
/**
* 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) {
let tf = this.tf;
let mdiv = !this.tgtId ? tf.mDiv : elm(this.tgtId);
//store valid rows indexes
tf.validRowsIndex = validRows || tf.getValidRows(true);
//calculate nb of pages
this.nbPages = Math.ceil(tf.validRowsIndex.length / this.pageLength);
//refresh page nb span
this.pgCont.innerHTML = this.nbPages;
//select clearing shortcut
if (this.pageSelectorType === SELECT) {
this.pageSlc.innerHTML = '';
}
if (this.nbPages > 0) {
mdiv.style.visibility = 'visible';
if (this.pageSelectorType === SELECT) {
for (let z = 0; z < this.nbPages; z++) {
let opt = createOpt(z + 1, z * this.pageLength, false);
this.pageSlc.options[z] = opt;
}
} else {
//input type
this.pageSlc.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) {
let tf = this.tf;
let rows = tf.dom().rows;
let startPagingRow = parseInt(this.startPagingRow, 10);
let endPagingRow = startPagingRow + parseInt(this.pageLength, 10);
//store valid rows indexes
if (validRows) {
tf.validRowsIndex = validRows;
}
//this loop shows valid rows of current page
for (let h = 0, len = tf.getValidRowsNb(true); h < len; h++) {
let validRowIdx = tf.validRowsIndex[h];
let r = rows[validRowIdx];
let isRowValid = r.getAttribute('validRow');
let rowDisplayed = false;
if (h >= startPagingRow && h < endPagingRow) {
if (isNull(isRowValid) || Boolean(isRowValid === 'true')) {
r.style.display = '';
rowDisplayed = true;
}
} else {
r.style.display = NONE;
}
this.emitter.emit('row-paged', tf, validRowIdx, h, rowDisplayed);
}
// broadcast grouping by page
this.emitter.emit('grouped-by-page', tf, this);
}
/**
* Return the current page number
* @return {Number} Page number
*/
getPage() {
return this.currentPageNb;
}
/**
* Show page defined by passed argument (string or number):
* @param {String}/{Number} cmd possible string values: 'next',
* 'previous', 'last', 'first' or page number as per param
*/
setPage(cmd) {
let tf = this.tf;
if (!tf.isInitialized() || !this.isEnabled()) {
return;
}
let btnEvt = this.evt,
cmdtype = typeof cmd;
if (cmdtype === 'string') {
switch (cmd.toLowerCase()) {
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() {
let tf = this.tf;
let evt = this.evt;
if (this.pageLengthSlc || !this.resultsPerPage) {
return;
}
evt.slcResultsChange = (ev) => {
this.onChangeResultsPerPage();
ev.target.blur();
};
let slcR = createElm(SELECT);
slcR.className = this.resultsSlcCssClass;
let slcRText = this.resultsPerPage[0],
slcROpts = this.resultsPerPage[1];
let slcRSpan = createElm('span');
slcRSpan.className = this.resultsSpanCssClass;
// results per page select is added to external element
if (!this.pageLengthTgtId) {
tf.setToolbar();
}
let targetEl = !this.pageLengthTgtId ?
tf.rDiv : elm(this.pageLengthTgtId);
slcRSpan.appendChild(createText(slcRText));
let 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 (let r = 0; r < slcROpts.length; r++) {
let currOpt = new Option(slcROpts[r], slcROpts[r], false, false);
slcR.options[r] = currOpt;
}
addEvt(slcR, 'change', evt.slcResultsChange);
this.slcResultsTxt = slcRSpan;
this.pageLengthSlc = slcR;
}
/**
* Remove number of results per page UI elements
*/
removeResultsPerPage() {
let tf = this.tf;
if (!tf.isInitialized() || !this.pageLengthSlc ||
!this.resultsPerPage) {
return;
}
if (this.pageLengthSlc) {
removeElm(this.pageLengthSlc);
}
if (this.slcResultsTxt) {
removeElm(this.slcResultsTxt);
}
this.pageLengthSlc = null;
this.slcResultsTxt = null;
}
/**
* Change the page based on passed index
* @param {Number} index Index of the page (0-n)
*/
changePage(index) {
let tf = this.tf;
if (!this.isEnabled()) {
return;
}
this.emitter.emit('before-page-change', tf, (index + 1));
if (index === null) {
index = this.pageSelectorType === SELECT ?
this.pageSlc.options.selectedIndex : this.pageSlc.value - 1;
}
if (index >= 0 && index <= (this.nbPages - 1)) {
this.onBeforeChangePage(this, (index + 1));
this.currentPageNb = parseInt(index, 10) + 1;
if (this.pageSelectorType === SELECT) {
this.pageSlc.options[index].selected = true;
} else {
this.pageSlc.value = this.currentPageNb;
}
this.startPagingRow = (this.pageSelectorType === SELECT) ?
this.pageSlc.value : (index * this.pageLength);
this.groupByPage();
this.onAfterChangePage(this, (index + 1));
}
this.emitter.emit('after-page-change', tf, (index + 1));
}
/**
* Change the number of results per page based on passed value
* @param {String} val The number of results per page
*/
changeResultsPerPage(val) {
if (!this.isEnabled() || isNaN(val)) {
return;
}
this.pageLengthSlc.value = val;
this.onChangeResultsPerPage();
}
/**
* Change rows according to page results drop-down
*/
onChangeResultsPerPage() {
let tf = this.tf;
if (!this.isEnabled() || tf.getValidRowsNb() === 0) {
return;
}
let {
pageLengthSlc: slcR, pageSelectorType, pageSlc, emitter
} = this;
emitter.emit('before-page-length-change', tf);
let slcIndex = slcR.selectedIndex;
let slcPagesSelIndex = (pageSelectorType === SELECT) ?
pageSlc.selectedIndex : parseInt(pageSlc.value - 1, 10);
this.pageLength = parseInt(slcR.options[slcIndex].value, 10);
this.startPagingRow = this.pageLength * slcPagesSelIndex;
if (!isNaN(this.pageLength)) {
if (this.startPagingRow >= tf.nbFilterableRows) {
this.startPagingRow = (tf.nbFilterableRows - this.pageLength);
}
this.setPagingInfo();
if (pageSelectorType === SELECT) {
let slcIdx = (pageSlc.options.length - 1 <= slcPagesSelIndex) ?
(pageSlc.options.length - 1) :
slcPagesSelIndex;
pageSlc.options[slcIdx].selected = true;
}
}
emitter.emit('after-page-length-change', tf, this.pageLength);
}
/**
* Re-set page nb at page re-load
*/
resetPage() {
let tf = this.tf;
if (!this.isEnabled()) {
return;
}
this.emitter.emit('before-reset-page', tf);
let pgNb = tf.feature('store').getPageNb();
if (pgNb !== '') {
this.changePage((pgNb - 1));
}
this.emitter.emit('after-reset-page', tf, pgNb);
}
/**
* Re-set page length value at page re-load
*/
resetPageLength() {
let tf = this.tf;
if (!this.isEnabled()) {
return;
}
this.emitter.emit('before-reset-page-length', tf);
let pglenIndex = tf.feature('store').getPageLength();
if (pglenIndex !== '') {
this.pageLengthSlc.options[pglenIndex].selected = true;
this.changeResultsPerPage();
}
this.emitter.emit('after-reset-page-length', tf, pglenIndex);
}
/**
* Remove paging feature
*/
destroy() {
if (!this.initialized) {
return;
}
let evt = this.evt;
if (this.pageSlc) {
if (this.pageSelectorType === SELECT) {
removeEvt(this.pageSlc, 'change', evt.slcPagesChange);
}
else if (this.pageSelectorType === INPUT) {
removeEvt(this.pageSlc, 'keypress', evt._detectKey);
}
removeElm(this.pageSlc);
}
if (this.btnNextCont) {
removeEvt(this.btnNextCont, 'click', evt.next);
removeElm(this.btnNextCont);
this.btnNextCont = null;
}
if (this.btnPrevCont) {
removeEvt(this.btnPrevCont, 'click', evt.prev);
removeElm(this.btnPrevCont);
this.btnPrevCont = null;
}
if (this.btnLastCont) {
removeEvt(this.btnLastCont, 'click', evt.last);
removeElm(this.btnLastCont);
this.btnLastCont = null;
}
if (this.btnFirstCont) {
removeEvt(this.btnFirstCont, 'click', evt.first);
removeElm(this.btnFirstCont);
this.btnFirstCont = null;
}
if (this.pgBefore) {
removeElm(this.pgBefore);
this.pgBefore = null;
}
if (this.pgAfter) {
removeElm(this.pgAfter);
this.pgAfter = null;
}
if (this.pgCont) {
removeElm(this.pgCont);
this.pgCont = null;
}
if (this.hasResultsPerPage) {
this.removeResultsPerPage();
}
this.emitter.off(['after-filtering'], () => this.resetPagingInfo());
this.emitter.off(['change-page'],
(tf, pageNumber) => this.setPage(pageNumber));
this.emitter.off(['change-page-results'],
(tf, pageLength) => this.changeResultsPerPage(pageLength));
this.pageSlc = null;
this.nbPages = 0;
this.initialized = false;
}
}
@@ -1,457 +0,0 @@
import {Feature} from '../feature';
import {isUndef, EMPTY_FN} from '../types';
import {createElm, removeElm} from '../dom';
import {addEvt, cancelEvt, stopEvt, targetEvt, removeEvt} from '../event';
import {INPUT, NONE, CHECKLIST, MULTIPLE} from '../const';
import {root} from '../root';
import {defaultsStr, defaultsBool, defaultsArr, defaultsFn} from '../settings';
/**
* Pop-up filter component
* @export
* @class PopupFilter
* @extends {Feature}
*/
export class PopupFilter extends Feature {
/**
* Creates an instance of PopupFilter
* @param {TableFilter} tf TableFilter instance
*/
constructor(tf) {
super(tf, 'popupFilters');
// Configuration object
let f = this.config.popup_filters || {};
/**
* Close active popup filter upon filtering, enabled by default
* @type {Boolean}
*/
this.closeOnFiltering = defaultsBool(f.close_on_filtering, true);
/**
* Filter icon path
* @type {String}
*/
this.iconPath = defaultsStr(f.image, tf.themesPath + 'icn_filter.gif');
/**
* Active filter icon path
* @type {string}
*/
this.activeIconPath = defaultsStr(f.image_active,
tf.themesPath + 'icn_filterActive.gif');
/**
* HTML for the filter icon
* @type {string}
*/
this.iconHtml = defaultsStr(f.image_html,
'<img src="' + this.iconPath + '" alt="Column filter" />');
/**
* Css class assigned to the popup container element
* @type {String}
*/
this.placeholderCssClass = defaultsStr(f.placeholder_css_class,
'popUpPlaceholder');
/**
* Css class assigned to filter container element
* @type {String}
*/
this.containerCssClass = defaultsStr(f.div_css_class, 'popUpFilter');
/**
* Ensure filter's container element width matches column width, enabled
* by default
* @type {Boolean}
*/
this.adjustToContainer = defaultsBool(f.adjust_to_container, true);
/**
* Callback fired before a popup filter is opened
* @type {Function}
*/
this.onBeforeOpen = defaultsFn(f.on_before_popup_filter_open, EMPTY_FN);
/**
* Callback fired after a popup filter is opened
* @type {Function}
*/
this.onAfterOpen = defaultsFn(f.on_after_popup_filter_open, EMPTY_FN);
/**
* Callback fired before a popup filter is closed
* @type {Function}
*/
this.onBeforeClose = defaultsFn(f.on_before_popup_filter_close,
EMPTY_FN);
/**
* Callback fired after a popup filter is closed
* @type {Function}
*/
this.onAfterClose = defaultsFn(f.on_after_popup_filter_close, EMPTY_FN);
/**
* Collection of filters spans
* @type {Array}
* @private
*/
this.fltSpans = [];
/**
* Collection of filters icons
* @type {Array}
* @private
*/
this.fltIcons = [];
/**
* Collection of filters icons cached after pop-up filters are removed
* @type {Array}
* @private
*/
this.filtersCache = null;
/**
* Collection of filters containers
* @type {Array}
* @private
*/
this.fltElms = defaultsArr(this.filtersCache, []);
/**
* Prefix for pop-up filter container ID
* @type {String}
* @private
*/
this.prfxDiv = 'popup_';
/**
* Column index of popup filter currently active
* @type {Number}
* @private
*/
this.activeFilterIdx = -1;
}
/**
* Click event handler for pop-up filter icon
* @private
*/
onClick(evt) {
let elm = targetEvt(evt).parentNode;
let colIndex = parseInt(elm.getAttribute('ci'), 10);
this.closeAll(colIndex);
this.toggle(colIndex);
if (this.adjustToContainer) {
let cont = this.fltElms[colIndex],
header = this.tf.getHeaderElement(colIndex),
headerWidth = header.clientWidth * 0.95;
cont.style.width = parseInt(headerWidth, 10) + 'px';
}
cancelEvt(evt);
stopEvt(evt);
}
/**
* Mouse-up event handler handling popup filter auto-close behaviour
* @private
*/
onMouseup(evt) {
if (this.activeFilterIdx === -1) {
return;
}
let targetElm = targetEvt(evt);
let activeFlt = this.fltElms[this.activeFilterIdx];
let icon = this.fltIcons[this.activeFilterIdx];
if (icon === targetElm) {
return;
}
while (targetElm && targetElm !== activeFlt) {
targetElm = targetElm.parentNode;
}
if (targetElm !== activeFlt) {
this.close(this.activeFilterIdx);
}
return;
}
/**
* Initialize DOM elements
*/
init() {
if (this.initialized) {
return;
}
let tf = this.tf;
// Enable external filters
tf.externalFltTgtIds = [''];
// Override filters row index supplied by configuration
tf.filtersRowIndex = 0;
// Override headers row index if no grouped headers
// TODO: Because of the filters row generation, headers row index needs
// adjusting: prevent useless row generation
if (tf.headersRow <= 1 && isNaN(tf.config().headers_row_index)) {
tf.headersRow = 0;
}
// Adjust headers row index for grid-layout mode
// TODO: Because of the filters row generation, headers row index needs
// adjusting: prevent useless row generation
if (tf.gridLayout) {
tf.headersRow--;
this.buildIcons();
}
// subscribe to events
this.emitter.on(['before-filtering'], () => this.setIconsState());
this.emitter.on(['after-filtering'], () => this.closeAll());
this.emitter.on(['cell-processed'],
(tf, cellIndex) => this.changeState(cellIndex, true));
this.emitter.on(['filters-row-inserted'], () => this.buildIcons());
this.emitter.on(['before-filter-init'],
(tf, colIndex) => this.build(colIndex));
/** @inherited */
this.initialized = true;
}
/**
* Reset previously destroyed feature
*/
reset() {
this.enable();
this.init();
this.buildIcons();
this.buildAll();
}
/**
* Build all filters icons
*/
buildIcons() {
let tf = this.tf;
// TODO: Because of the filters row generation, headers row index needs
// adjusting: prevent useless row generation
tf.headersRow++;
for (let i = 0; i < tf.nbCells; i++) {
if (tf.getFilterType(i) === NONE) {
continue;
}
let icon = createElm('span', ['ci', i]);
icon.innerHTML = this.iconHtml;
let header = tf.getHeaderElement(i);
header.appendChild(icon);
addEvt(icon, 'click', (evt) => this.onClick(evt));
this.fltSpans[i] = icon;
this.fltIcons[i] = icon.firstChild;
}
}
/**
* Build all pop-up filters elements
*/
buildAll() {
for (let i = 0; i < this.filtersCache.length; i++) {
this.build(i, this.filtersCache[i]);
}
}
/**
* Build a specified pop-up filter elements
* @param {Number} colIndex Column index
* @param {Object} div Optional container DOM element
*/
build(colIndex, div) {
let tf = this.tf;
let contId = `${this.prfxDiv}${tf.id}_${colIndex}`;
let placeholder = createElm('div', ['class', this.placeholderCssClass]);
let cont = div ||
createElm('div', ['id', contId], ['class', this.containerCssClass]);
tf.externalFltTgtIds[colIndex] = cont.id;
placeholder.appendChild(cont);
let header = tf.getHeaderElement(colIndex);
header.insertBefore(placeholder, header.firstChild);
addEvt(cont, 'click', (evt) => stopEvt(evt));
this.fltElms[colIndex] = cont;
}
/**
* Toggle visibility of specified filter
* @param {Number} colIndex Column index
*/
toggle(colIndex) {
if (!this.isOpen(colIndex)) {
this.open(colIndex);
} else {
this.close(colIndex);
}
}
/**
* Open popup filter of specified column
* @param {Number} colIndex Column index
*/
open(colIndex) {
let tf = this.tf,
container = this.fltElms[colIndex];
this.onBeforeOpen(this, container, colIndex);
container.style.display = 'block';
this.activeFilterIdx = colIndex;
addEvt(root, 'mouseup', (evt) => this.onMouseup(evt));
if (tf.getFilterType(colIndex) === INPUT) {
let flt = tf.getFilterElement(colIndex);
if (flt) {
flt.focus();
}
}
this.onAfterOpen(this, container, colIndex);
}
/**
* Close popup filter of specified column
* @param {Number} colIndex Column index
*/
close(colIndex) {
let container = this.fltElms[colIndex];
this.onBeforeClose(this, container, colIndex);
container.style.display = NONE;
if (this.activeFilterIdx === colIndex) {
this.activeFilterIdx = -1;
}
removeEvt(root, 'mouseup', (evt) => this.onMouseup(evt));
this.onAfterClose(this, container, colIndex);
}
/**
* Check if popup filter for specified column is open
* @param {Number} colIndex Column index
* @returns {Boolean}
*/
isOpen(colIndex) {
return this.fltElms[colIndex].style.display === 'block';
}
/**
* Close all filters excepted for the specified one if any
* @param {Number} exceptIdx Column index of the filter to not close
*/
closeAll(exceptIdx) {
// Do not close filters only if argument is undefined and close on
// filtering option is disabled
if (isUndef(exceptIdx) && !this.closeOnFiltering) {
return;
}
for (let i = 0; i < this.fltElms.length; i++) {
if (i === exceptIdx) {
continue;
}
let fltType = this.tf.getFilterType(i);
let isMultipleFilter =
(fltType === CHECKLIST || fltType === MULTIPLE);
// Always hide all single selection filter types but hide multiple
// selection filter types only if index set
if (!isMultipleFilter || !isUndef(exceptIdx)) {
this.close(i);
}
}
}
/**
* Build all the icons representing the pop-up filters
*/
setIconsState() {
for (let i = 0; i < this.fltIcons.length; i++) {
this.changeState(i, false);
}
}
/**
* Apply specified icon state
* @param {Number} colIndex Column index
* @param {Boolean} active Apply active state
*/
changeState(colIndex, active) {
let icon = this.fltIcons[colIndex];
if (icon) {
icon.src = active ? this.activeIconPath : this.iconPath;
}
}
/**
* Remove pop-up filters
*/
destroy() {
if (!this.initialized) {
return;
}
this.filtersCache = [];
for (let i = 0; i < this.fltElms.length; i++) {
let container = this.fltElms[i],
placeholder = container.parentNode,
icon = this.fltSpans[i],
iconImg = this.fltIcons[i];
if (container) {
removeElm(container);
this.filtersCache[i] = container;
}
container = null;
if (placeholder) {
removeElm(placeholder);
}
placeholder = null;
if (icon) {
removeElm(icon);
}
icon = null;
if (iconImg) {
removeElm(iconImg);
}
iconImg = null;
}
this.fltElms = [];
this.fltSpans = [];
this.fltIcons = [];
// TODO: expose an API to handle external filter IDs
this.tf.externalFltTgtIds = [];
// unsubscribe to events
this.emitter.off(['before-filtering'], () => this.setIconsState());
this.emitter.off(['after-filtering'], () => this.closeAll());
this.emitter.off(['cell-processed'],
(tf, cellIndex) => this.changeState(cellIndex, true));
this.emitter.off(['filters-row-inserted'], () => this.buildIcons());
this.emitter.off(['before-filter-init'],
(tf, colIndex) => this.build(colIndex));
this.initialized = false;
}
}

Some files were not shown because too many files have changed in this diff Show More