jQuery.uiCollectionFilter = function(jq, elemSelector, phrase, searchScope, ifHidden){
  var new_hidden = false;
  if( this.last_phrase === phrase ) return false;

  var phrase_length = phrase.length;
  var words = phrase.toLowerCase().split(" ");

  var success = function(elem) { elem.show() }
  var failure = function(elem) { elem.hide() }

  var search_text = function(){
      var elem = jQuery(this);
      var searchElem = elem;
      if (searchScope != null && searchScope != '') {
		searchElem = elem.find(searchScope);
      }
      jQuery.uiCollectionFilter.has_words( searchElem.text(), words ) ? elem.show() : elem.hide();
  }

  // if added one letter to last time,
  // just check newest word and only need to hide
  if( (words.size > 1) && (phrase.substr(0, phrase_length - 1) ===
        this.last_phrase) ) {

    if( phrase[-1] === " " )
    { this.last_phrase = phrase; return false; }

    success = function(elem) { elem.hide(); new_hidden = true; }
    failure = function(elem) {;}
    var words = words[-1];
    jq.find(elemSelector + ":visible").each( search_text )
  }
  else {
    new_hidden = true;
    jq.find(elemSelector).each( search_text );
  }

  last_phrase = phrase;
  if( ifHidden && new_hidden ) ifHidden();
  return jq;
};
jQuery.uiCollectionFilter.last_phrase = ""

// not jQuery dependent
// "" [""] -> Boolean
// "" [""] Boolean -> Boolean
jQuery.uiCollectionFilter.has_words = function( str, words, caseSensitive )
{
  var text = caseSensitive ? str : str.toLowerCase();
  for (var i=0; i < words.length; i++) {
    if (text.indexOf(words[i]) === -1) return false;
  }
  return true;
}
