Skip to content

Commit

Permalink
Refine error message format
Browse files Browse the repository at this point in the history
  • Loading branch information
cheton committed Jul 31, 2017
1 parent 6e15078 commit 26d90bc
Showing 1 changed file with 118 additions and 107 deletions.
225 changes: 118 additions & 107 deletions src/infinite-tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,21 @@ import {
removeEventListener
} from './dom-events';

const error = (...args) => {
const error = (format, ...args) => {
let argIndex = 0;
const message = 'Error: ' + format.replace(/%s/g, () => {
return args[argIndex++];
});

if (console && console.error) {
const prefix = '[InfiniteTree]';
console.error.apply(console, [prefix].concat(args));
console.error(message);
}
try {
// This error was thrown as a convenience so that you can use this stack
// to find the callsite that caused this error to fire.
throw new Error(message);
} catch (e) {
// Ignore
}
};

Expand Down Expand Up @@ -294,7 +305,7 @@ class InfiniteTree extends events.EventEmitter {
};

if (!this.options.el) {
console.error('Failed to initialize infinite-tree: el is not specified.', options);
error('Failed to initialize infinite-tree: el is not specified.', options);
return;
}

Expand Down Expand Up @@ -597,6 +608,109 @@ class InfiniteTree extends events.EventEmitter {

return true;
}
// Filters nodes. Use a string or a function to test each node of the tree. Otherwise, it will render nothing after filtering (e.g. tree.filter(), tree.filter(null), tree.flter(0), tree.filter({}), etc.).
// @param {string|function} predicate A keyword string, or a function to test each node of the tree. If the predicate is an empty string, all nodes will be filtered. If the predicate is a function, returns true to keep the node, false otherwise.
// @param {object} [options] The options object.
// @param {boolean} [options.caseSensitive] Case sensitive string comparison. Defaults to false. This option is only available for string comparison.
// @param {boolean} [options.exactMatch] Exact string matching. Defaults to false. This option is only available for string comparison.
// @param {string} [options.filterPath] Gets the value at path of Node object. Defaults to 'name'. This option is only available for string comparison.
// @param {boolean} [options.includeAncestors] Whether to include ancestor nodes. Defaults to true.
// @param {boolean} [options.includeDescendants] Whether to include descendant nodes. Defaults to true.
// @example
//
// const filterOptions = {
// caseSensitive: false,
// exactMatch: false,
// filterPath: 'props.some.other.key',
// includeAncestors: true,
// includeDescendants: true
// };
// tree.filter('keyword', filterOptions);
//
// @example
//
// const filterOptions = {
// includeAncestors: true,
// includeDescendants: true
// };
// tree.filter(function(node) {
// const keyword = 'keyword';
// const filterText = node.name || '';
// return filterText.toLowerCase().indexOf(keyword) >= 0;
// }, filterOptions);
filter(predicate, options) {
options = {
caseSensitive: false,
exactMatch: false,
filterPath: 'name',
includeAncestors: true,
includeDescendants: true,
...options
};

this.filtered = true;

const rootNode = this.state.rootNode;
const traverse = (node, filterNode = false) => {
if (!node || !node.children) {
return false;
}

if (node === rootNode) {
node.state.filtered = false;
} else if (filterNode) {
node.state.filtered = true;
} else if (typeof predicate === 'string') {
// string
let filterText = get(node, options.filterPath, '');
let keyword = predicate;
if (!options.caseSensitive) {
filterText = filterText.toLowerCase();
keyword = keyword.toLowerCase();
}
node.state.filtered = options.exactMatch
? (filterText === keyword)
: (filterText.indexOf(keyword) >= 0);
} else if (typeof predicate === 'function') {
// function
const callback = predicate;
node.state.filtered = !!callback(node);
} else {
node.state.filtered = false;
}

if (options.includeDescendants) {
filterNode = filterNode || node.state.filtered;
}

let filtered = false;
for (let i = 0; i < node.children.length; ++i) {
const childNode = node.children[i];
if (!childNode) {
continue;
}
if (traverse(childNode, filterNode)) {
filtered = true;
}
}
if (options.includeAncestors && filtered) {
node.state.filtered = true;
}

return node.state.filtered;
};

traverse(rootNode);

// Update rows
this.rows.length = this.nodes.length;
for (let i = 0; i < this.nodes.length; ++i) {
const node = this.nodes[i];
this.rows[i] = this.options.rowRenderer(node, this.options);
}

this.update();
}
// Flattens all child nodes of a parent node by performing full tree traversal using child-parent link.
// No recursion or stack is involved.
// @param {Node} parentNode The Node object that defines the parent node.
Expand Down Expand Up @@ -1333,109 +1447,6 @@ class InfiniteTree extends events.EventEmitter {

return traverse(node);
}
// Filters nodes. Use a string or a function to test each node of the tree. Otherwise, it will render nothing after filtering (e.g. tree.filter(), tree.filter(null), tree.flter(0), tree.filter({}), etc.).
// @param {string|function} predicate A keyword string, or a function to test each node of the tree. If the predicate is an empty string, all nodes will be filtered. If the predicate is a function, returns true to keep the node, false otherwise.
// @param {object} [options] The options object.
// @param {boolean} [options.caseSensitive] Case sensitive string comparison. Defaults to false. This option is only available for string comparison.
// @param {boolean} [options.exactMatch] Exact string matching. Defaults to false. This option is only available for string comparison.
// @param {string} [options.filterPath] Gets the value at path of Node object. Defaults to 'name'. This option is only available for string comparison.
// @param {boolean} [options.includeAncestors] Whether to include ancestor nodes. Defaults to true.
// @param {boolean} [options.includeDescendants] Whether to include descendant nodes. Defaults to true.
// @example
//
// const filterOptions = {
// caseSensitive: false,
// exactMatch: false,
// filterPath: 'props.some.other.key',
// includeAncestors: true,
// includeDescendants: true
// };
// tree.filter('keyword', filterOptions);
//
// @example
//
// const filterOptions = {
// includeAncestors: true,
// includeDescendants: true
// };
// tree.filter(function(node) {
// const keyword = 'keyword';
// const filterText = node.name || '';
// return filterText.toLowerCase().indexOf(keyword) >= 0;
// }, filterOptions);
filter(predicate, options) {
options = {
caseSensitive: false,
exactMatch: false,
filterPath: 'name',
includeAncestors: true,
includeDescendants: true,
...options
};

this.filtered = true;

const rootNode = this.state.rootNode;
const traverse = (node, filterNode = false) => {
if (!node || !node.children) {
return false;
}

if (node === rootNode) {
node.state.filtered = false;
} else if (filterNode) {
node.state.filtered = true;
} else if (typeof predicate === 'string') {
// string
let filterText = get(node, options.filterPath, '');
let keyword = predicate;
if (!options.caseSensitive) {
filterText = filterText.toLowerCase();
keyword = keyword.toLowerCase();
}
node.state.filtered = options.exactMatch
? (filterText === keyword)
: (filterText.indexOf(keyword) >= 0);
} else if (typeof predicate === 'function') {
// function
const callback = predicate;
node.state.filtered = !!callback(node);
} else {
node.state.filtered = false;
}

if (options.includeDescendants) {
filterNode = filterNode || node.state.filtered;
}

let filtered = false;
for (let i = 0; i < node.children.length; ++i) {
const childNode = node.children[i];
if (!childNode) {
continue;
}
if (traverse(childNode, filterNode)) {
filtered = true;
}
}
if (options.includeAncestors && filtered) {
node.state.filtered = true;
}

return node.state.filtered;
};

traverse(rootNode);

// Update rows
this.rows.length = this.nodes.length;
for (let i = 0; i < this.nodes.length; ++i) {
const node = this.nodes[i];
this.rows[i] = this.options.rowRenderer(node, this.options);
}

this.update();
}
// Unfilters nodes.
unfilter() {
this.filtered = false;
Expand Down

0 comments on commit 26d90bc

Please sign in to comment.