/************************************************************************************************************
DG Filter
Copyright (C) 2009  DTHMLGoodies.com, Alf Magne Kalleland
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Dhtmlgoodies.com., hereby disclaims all copyright interest in this script
written by Alf Magne Kalleland.
Alf Magne Kalleland, 2009
Owner of DHTMLgoodies.com
************************************************************************************************************/
if(!window.DG){
window.DG = {};
}
DG.Filter = new Class({
Extends : Events,
xPathFilterElements : false,	/** Css class for text elements to be indexed */
html :  {
filterField : null,	/** Reference to text field */
filterEl : null		/** Reference to element to be filtered */
},
filterElType : null,
searchIndex : null,
onMatchShowChildren : false,
idCounter : 0,
matchedNodes : null,			/** Temporary array of matched nodes */
txtNoMatchFound : '',			/** No matches found text */
/**
 * Constructor
 * @param {Object} config
 */
initialize : function(config) {
this.html.filterField = $(config.filterField);
this.html.filterEl = $(config.filterEl);
this.xPathFilterElements = config.xPathFilterElements;
this.filterElType = this.html.filterEl.tagName.toLowerCase() == 'table' ? 'table' : 'list';
this.onMatchShowChildren = config.onMatchShowChildren;
this.txtNoMatchFound = config.txtNoMatchFound || 'No matches found';
this.listeners = config.listeners || null;
this.addFilterEvents();
this.createSearchIndex();
this._createNoMatchFoundElement();
},
_setNewNodeId : function(node) {
node.setProperty('id', 'dgfilter' + this.idCounter);
this.idCounter++;
},
/**
 * Create search index - makes the search work faster
 */
createSearchIndex : function() {
if(this.filterElType == 'list') {
var elements = this.html.filterEl.getElements('li');
}else{
var elements = this.html.filterEl.getElements('tbody')[0].getElements('tr');
}
var textProperty = document.body.innerText ? 'innerText' : 'textContent';
this.searchIndex = [];
for(var i=0; i< elements.length; i++) {
if(!elements[i].get('id')) {
this._setNewNodeId(elements[i]);
}
var textContent = '';
var firstParent = null;
if(this.xPathFilterElements) {
var textElements = this.filterElType == 'list' ? [elements[i].getElements(this.xPathFilterElements)[0]] : elements[i].getElements(this.xPathFilterElements);
for(var j=0;j<textElements.length;j++) {
textContent = textContent + textElements[j][textProperty];
}
}
this.searchIndex.push({
itemText : textContent.toLowerCase(),
branchText : elements[i][textProperty].toLowerCase(),
el : elements[i],
parentEl : (this.filterElType == 'list' && i>0) ? elements[i].getParent('li') : null
});
}
},
addFilterEvents : function() {
this.html.filterField.addEvent('keyup', this.filter.bind(this));
this.html.filterField.addEvent('paste', this.filter.bind(this));
this.addEvents(this.listeners);
},
_showNode : function(node) {
node.el.setStyle('display', '');
},
_hideNode : function(node) {
node.el.setStyle('display', 'none');
},
filter : function() {
this.fireEvent('beforefilter', this);
this.matchedNodes = {};
if(!this.searchIndex) {
this.createSearchIndex();
}
var searchPhrase = this.html.filterField.get('value').toLowerCase();
if (this.filterElType == 'list') {
var matchesFound = this.filterList(searchPhrase);
}
else {
var matchesFound = this.filterTable(searchPhrase);
}
if(!matchesFound) {
this._showNoMatchFoundElement();
}else{
this._hideNoMatchFoundText();
}
this.fireEvent('afterfilter', this);
},
_matchAbove : function(node) {
return this.matchedNodes[node.el.id];
},
_nodeMatch : function(indexNode, searchPhrase) {
return indexNode.branchText.indexOf(searchPhrase) >=0;
},
storeNodeMatch : function(indexNode, searchPhrase) {
if(!this.onMatchShowChildren) {
return;
}
var match = indexNode.itemText.indexOf(searchPhrase) >=0;
if(!match && indexNode.parentEl) {
match = this.matchedNodes[indexNode.parentEl.id];
}
this.matchedNodes[indexNode.el.id] = match;
},
_getIdOfNoMatchFoundElement : function() {
return this.html.filterEl.id + 'noMatchFound';
},
_hideNoMatchFoundText : function() {
var el = $(this._getIdOfNoMatchFoundElement());
if(el) {
el.setStyle('display', 'none');
}
},
_showNoMatchFoundElement : function() {
var el = $(this._getIdOfNoMatchFoundElement());
if(el) {
el.setStyle('display', '');
}
},
_createNoMatchFoundElement : function() {
var el = new Element('div');
el.id = this._getIdOfNoMatchFoundElement();
el.inject(this.html.filterEl, 'after');
el.set('html', this.txtNoMatchFound);
el.setStyle('display','none')
},
filterList : function(searchPhrase) {
var matchesFound = false;
for(var i=0;i<this.searchIndex.length;i++) {
this.storeNodeMatch(this.searchIndex[i], searchPhrase)
if(this._nodeMatch(this.searchIndex[i], searchPhrase) || this._matchAbove(this.searchIndex[i])) {
matchesFound = true;
this._showNode(this.searchIndex[i]);
}else{
this._hideNode(this.searchIndex[i]);
}
}
return matchesFound;
},
filterTable : function(searchPhrase) {
var matchesFound = false;
for (var i = 0; i < this.searchIndex.length; i++) {
if(this._nodeMatch(this.searchIndex[i], searchPhrase)) {
matchesFound = true;
this._showNode(this.searchIndex[i]);
}else{
this._hideNode(this.searchIndex[i]);
}
}
return matchesFound;
}
});
