/*
--------------------------------------------------------
incsearch.js - Incremental Search
Version 2.2.0 (Update 2008/04/02)
Copyright (c) 2006-2008 onozaty (http://www.enjoyxstudy.com)
Released under an MIT-style license.
For details, see the web site:
http://www.enjoyxstudy.com/javascript/incsearch
--------------------------------------------------------
*/
if (!IncSearch) {
var IncSearch = {};
}
/*-- Utils --------------------------------------------*/
IncSearch._copyProperties = function(dest, src) {
for (var property in src) {
dest[property] = src[property];
}
return dest;
};
IncSearch._copyProperties = function(dest, src) {
for (var property in src) {
dest[property] = src[property];
}
return dest;
};
IncSearch._getElement = function(element) {
return (typeof element == 'string') ? document.getElementById(element) : element;
};
IncSearch._addEvent = (window.addEventListener ?
function(element, type, func) {
element.addEventListener(type, func, false);
} :
function(element, type, func) {
element.attachEvent('on' + type, func);
});
IncSearch._stopEvent = function(event) {
if (event.preventDefault) {
event.preventDefault();
event.stopPropagation();
} else {
event.returnValue = false;
event.cancelBubble = true;
}
};
IncSearch._getEventElement = function(event) {
return event.target || event.srcElement;
};
/*-- IncSearch.ViewBase -------------------------------*/
IncSearch.ViewBase = function() {
this.initialize.apply(this, arguments);
};
IncSearch.ViewBase.prototype = {
initialize: function(input, viewArea, searchValues) {
this.input = IncSearch._getElement(input);
this.viewArea = IncSearch._getElement(viewArea);
this.searchValues = searchValues;
this.checkLoopTimer = null;
this.oldInput = null;
this.matchList = null;
this.setOptions(arguments[3] || {});
this.nowPage = 0;
this.nowRow = 0;
// check loop start
this.checkLoop();
},
// options
interval: 500,
delay: 0,
dispMax: 20,
initDispNon: false,
ignoreCase: true,
highlight: true,
highClassName: 'high',
highClassNum: 4,
delim: ' ',
escape: false,
pagePrevName: 'prev',
pageNextName: 'next',
useHotkey: false,
focusRowClassName: 'focus',
moveRow: false,
setOptions: function(options) {
IncSearch._copyProperties(this, options);
if (this.useHotkey) {
var keyevent = (window.opera) ? 'keypress' : 'keydown';
IncSearch._addEvent(document, keyevent, this._bindEvent(this.hotkey));
}
},
checkLoop: function() {
var input = this.getInput();
if (this.isChange(input)) {
this.oldInput = input;
if (this.delay == 0) {
this.startSearch(input);
} else {
if (this.startSearchTimer) clearTimeout(this.startSearchTimer);
this.startSearchTimer = setTimeout(this._bind(this.startSearch, input), this.delay);
}
}
if (this.checkLoopTimer) clearTimeout(this.checkLoopTimer);
this.checkLoopTimer = setTimeout(this._bind(this.checkLoop), this.interval);
},
isChange: function(input) {
return (!this.oldInput || (input.join(this.delim) != this.oldInput.join(this.delim)));
},
startSearch: function(input) {
// init
this.clearViewArea();
if (!this.initDispNon || input.length != 0) {
if (this.searchBefore) this.searchBefore();
this.search(input);
this.createViewArea(0, this.dispMax, input);
this.nowPage = 1;
if (this.moveRow) this.changeRow(1);
if (this.pageLink) this.createPageLink(1, this.pageLink);
if (this.searchAfter) this.searchAfter();
}
},
changePage: function(pageNo) {
var start = (pageNo - 1) * this.dispMax;
if (!this.matchList || start >= this.matchList.length) return false;
if (this.changePageBefore) this.changePageBefore(pageNo);
this.createViewArea(start, this.dispMax, this.oldInput);
this.nowPage = pageNo;
this.nowRow = 0;
if (this.moveRow) this.changeRow(1);
if (this.pageLink) this.createPageLink(pageNo, this.pageLink);
if (this.changePageAfter) this.changePageAfter(pageNo);
return true;
},
changeRow: function(rowNo) {
if (this.getListIndex(this.nowPage, rowNo) >= this.matchList.length) {
return;
}
if (rowNo < 1) {
if (this.nowPage > 1) {
this.changePage(this.nowPage - 1);
this.changeRow(this.dispMax);
}
return;
}
if (rowNo > this.dispMax) {
if (this.nowPage < this.getPageCount()) {
this.changePage(this.nowPage + 1);
}
return;
}
var table = this.viewArea.getElementsByTagName('table')[0];
if (this.nowRow != 0 && table.rows[this.nowRow]) {
table.rows[this.nowRow].className = '';
}
var row = table.rows[rowNo];
row.className = this.focusRowClassName;
var margin = 0;
var topPos = (this.viewArea.offsetTop + row.offsetTop) - (this.viewArea.offsetTop + table.rows[1].offsetTop);
var bottomPos = (this.viewArea.offsetTop + row.offsetTop + row.offsetHeight) + 5;
if (topPos < document.documentElement.scrollTop) {
window.scrollTo(0, topPos);
} else if (bottomPos > document.documentElement.clientHeight + document.documentElement.scrollTop) {
window.scrollTo(0, bottomPos - document.documentElement.clientHeight);
}
this.nowRow = rowNo;
},
createPageLink: function(pageNo, pageLinkElm) {
pageLinkElm = IncSearch._getElement(pageLinkElm);
var pageCount = this.getPageCount();
var prev_page = false;
var next_page = false;
if (pageCount > 1) {
if (pageNo == 1) {
next_page = true;
} else if (pageNo == pageCount) {
prev_page = true;
} else {
next_page = true;
prev_page = true;
}
}
pageLinkElm.innerHTML = '';
if (prev_page) {
this.createPageAnchor(pageLinkElm, this.pagePrevName, pageNo - 1);
}
if (next_page) {
if (prev_page) {
pageLinkElm.appendChild(document.createTextNode(' | '));
}
this.createPageAnchor(pageLinkElm, this.pageNextName, pageNo + 1);
}
},
createPageAnchor: function(parent, text, page) {
var a = parent.appendChild(document.createElement('a'));
a.setAttribute('href', 'javascript:void(0)');
a.appendChild(document.createTextNode(text));
IncSearch._addEvent(a, 'click', this._bind(this.changePage, page));
},
getPageCount: function() {
var pageCount = 0;
if (this.matchList && this.matchList.length != 0) {
if (this.dispMax == 0) {
pageCount = 1;
} else {
pageCount = Math.floor((this.matchList.length + this.dispMax - 1) / this.dispMax);
}
}
return pageCount;
},
getListIndex: function(pageNo, rowNo) {
pageNo = pageNo || this.nowPage;
rowNo = rowNo || this.nowRow;
return ((pageNo - 1) * this.dispMax) + rowNo - 1;
},
hotkey: function(event) {
if (event.ctrlKey) {
switch(event.keyCode) {
case 13: // Enter
case 77: // m (Enter Max OS X)
if (this.selectRowCallback) {
this.selectRowCallback(this.searchValues[this.matchList[this.getListIndex()]]);
IncSearch._stopEvent(event);
}
break;
case 37: // Left
if (this.nowPage > 1) {
this.changePage(this.nowPage - 1);
}
IncSearch._stopEvent(event);
break;
case 38: // Up
if (this.moveRow) {
this.changeRow(this.nowRow - 1);
IncSearch._stopEvent(event);
}
break;
case 39: // Right
if (this.nowPage < this.getPageCount()) {
this.changePage(this.nowPage + 1);
}
IncSearch._stopEvent(event);
break;
case 40: // Down
if (this.moveRow) {
this.changeRow(this.nowRow + 1);
IncSearch._stopEvent(event);
}
break;
default:
break;
}
}
},
createViewArea: function(start, count, patternList) {
var elementText = [];
var end = this.matchList.length;
if (count != 0 && end > (start + count)) {
end = start + count;
}
for (var i = start; i < end; i++) {
elementText.push(this.createLineElement(this.matchList[i], patternList));
}
if (elementText.length > 0) {
if (this.startElementText) elementText.unshift(this.startElementText);
if (this.endElementText) elementText.push(this.endElementText);
this.viewArea.innerHTML = elementText.join('');
}
},
clearViewArea: function() {
this.viewArea.innerHTML = '';
this.matchList = null;
this.nowPage = 1;
},
search: function(patternList) {
patternList = patternList || this.oldInput;
this.matchList = [];
for (var i = 0, len = this.searchValues.length; i < len; i++) {
if (this.isMatch(this.searchValues[i], patternList)) {
this.matchList.push(i);
}
}
return this.matchList.length;
},
createElement: function(value, patternList, tagName, highlight) {
return ['<', tagName, '>',
this.createText(value, patternList, highlight),
'', tagName, '>'].join('');
},
createText: function(value, patternList, highlight) {
var textList = [];
if (highlight == null) highlight = this.highlight;
if (highlight) {
var first = this.getFirstMatch(value, patternList);
while (first.listIndex != -1) {
textList.push(this._escapeHTML(value.substr(0, first.matchIndex)));
textList.push('');
textList.push(this._escapeHTML(value.substr(first.matchIndex, patternList[first.listIndex].length)));
textList.push('');
value = value.substr(first.matchIndex + patternList[first.listIndex].length);
first = this.getFirstMatch(value, patternList);
}
}
textList.push(this._escapeHTML(value));
return textList.join('');
},
matchIndex: function(value, pattern) {
if (this.ignoreCase) {
return value.toLowerCase().indexOf(pattern.toLowerCase());
} else {
return value.indexOf(pattern);
}
},
getFirstMatch: function(value, patternList) {
var first = {};
first.listIndex = -1;
first.matchIndex = value.length;
for (var i = 0, len = patternList.length; i < len; i++) {
var index = this.matchIndex(value, patternList[i]);
if (index != -1 && index < first.matchIndex) {
first.listIndex = i;
first.matchIndex = index;
}
}
return first;
},
getInput: function() {
var value = this.input.value;
if (!value) {
return [];
} else if (this.delim) {
var list = value.split(this.delim);
var inputs = [];
for (var i = 0, len = list.length; i < len; i++) {
if (list[i]) inputs.push(list[i]);
}
return inputs;
} else {
return [value];
}
},
// Utils
_bind: function(func) {
var self = this;
var args = Array.prototype.slice.call(arguments, 1);
return function(){ func.apply(self, args); };
},
_bindEvent: function(func) {
var self = this;
var args = Array.prototype.slice.call(arguments, 1);
return function(event){ event = event || window.event; func.apply(self, [event].concat(args)); };
},
_escapeHTML: function(value) {
if (this.escape) {
return value.replace(/\&/g, '&').replace( //g, '>')
.replace(/\"/g, '"').replace(/\'/g, ''').replace(/\n|\r\n/g, '
');
} else {
return value;
}
}
}
/*-- IncSearch.ViewList -------------------------------*/
IncSearch.ViewList = function() {
this.initialize.apply(this, arguments);
};
IncSearch._copyProperties(IncSearch.ViewList.prototype, IncSearch.ViewBase.prototype);
IncSearch.ViewList.prototype.listTagName = 'div';
IncSearch.ViewList.prototype.isMatch = function(value, patternList) {
for (var i = 0, len = patternList.length; i < len; i++) {
if (this.matchIndex(value, patternList[i]) == -1) {
return false;
}
}
return true;
};
IncSearch.ViewList.prototype.createLineElement = function(index, patternList) {
return this.createElement(this.searchValues[index], patternList, this.listTagName);
};
/*-- IncSearch.ViewTable -------------------------------*/
IncSearch.ViewTable = function() {
this.initialize.apply(this, arguments);
};
IncSearch._copyProperties(IncSearch.ViewTable.prototype, IncSearch.ViewBase.prototype);
IncSearch.ViewTable.prototype.startElementText = '