OSDN Git Service

git-svn-id: https://svn.sourceforge.jp/svnroot/nucleus-jp/plugin@1020 1ca29b6e-896d...
authorshizuki <shizuki@1ca29b6e-896d-4ea0-84a5-967f57386b96>
Fri, 26 Jun 2009 08:18:54 +0000 (08:18 +0000)
committershizuki <shizuki@1ca29b6e-896d-4ea0-84a5-967f57386b96>
Fri, 26 Jun 2009 08:18:54 +0000 (08:18 +0000)
304 files changed:
NP_TrackBack/branches/DOM-branch/NP_TrackBack.php [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/CHANGELOG.txt [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/autodetect.php [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/detectlist.php [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/grid.php [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/help.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/index.php [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.help.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/all.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/all_ajax.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/blocked.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/blocked_ajax.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/form.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/index.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/list.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/menu.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/ping.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/response_all.xml [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/response_blocked.xml [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/response_doblock.xml [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/response_dodelete.xml [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/response_dounblock.xml [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/updatetable.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/updatetablefinished.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.help.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/all.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/all_ajax.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/blocked.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/blocked_ajax.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/form.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/index.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/list.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/menu.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/ping.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/response_all.xml [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/response_blocked.xml [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/response_doblock.xml [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/response_dodelete.xml [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/response_dounblock.xml [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/updatetable.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/updatetablefinished.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/prototype.js [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/css/coffee-with-milk.css [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/css/grayedout.css [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/css/greenHdg.css [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/css/iegradient.css [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/css/ricoCalendar.css [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/css/ricoGrid.css [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/css/ricoLiveGridForms.css [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/css/ricoMenu.css [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/css/ricoTree.css [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/css/tanChisel.css [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/css/warmfall.css [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/export-owc.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/export-plain.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/aline.gif [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/calarrow.png [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/calendaricon.gif [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/close.gif [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/divider.gif [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/doc.gif [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/dotbutton.gif [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/drop.gif [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/filtercol.gif [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/folderclosed.gif [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/folderopen.gif [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/grayedout.gif [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/left.gif [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/link.gif [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/node.gif [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/nodeblank.gif [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/nodelast.gif [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/nodeline.gif [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/nodem.gif [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/nodemlast.gif [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/nodep.gif [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/nodeplast.gif [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/resize.gif [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/ricologo.gif [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/right.gif [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/shadow.png [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/shadow_ll.png [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/shadow_ur.png [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/sort_asc.gif [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/sort_desc.gif [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/prototype.js [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/rico.js [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoAjaxEngine.js [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoBehaviors.js [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoCalendar.js [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoColor.js [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoColorPicker.js [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoCommon.js [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoComponents.js [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoDashboard.js [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoDragDrop.js [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoEffects.js [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoGridCommon.js [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoLiveGrid.js [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoLiveGridAjax.js [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoLiveGridForms.js [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoLiveGridMenu.js [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoMenu.js [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoSheet.js [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoSimpleGrid.js [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoSimpleGrid.xsl [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoSimpleGrid2xl.xsl [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoStyles.js [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoTree.js [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/translations/livegrid_de.js [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/translations/livegrid_es.js [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/translations/livegrid_fr.js [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/translations/livegrid_it.js [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/translations/livegrid_ja.js [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/translations/livegrid_pt.js [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/translations/livegrid_ru.js [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/translations/livegrid_ua.js [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/js/rico/translations/livegrid_zh.js [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/language/english.php [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/language/japanese-euc.php [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/language/japanese-utf8.php [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/mkeuc.sh [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/silk/accept.png [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/silk/application_view_list.png [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/silk/cross.png [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/silk/delete.png [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/silk/exclamation.png [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/silk/help.png [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/silk/house_go.png [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/silk/link.png [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/silk/link_break.png [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/silk/plugin_edit.png [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/silk/readme.txt [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/silk/tick.png [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/silk/transmit_go.png [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/template.php [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/templates/all.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/templates/all_ajax.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/templates/blocked.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/templates/blocked_ajax.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/templates/form.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/templates/index.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/templates/list.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/templates/menu.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/templates/ping.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/templates/response_all.xml [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/templates/response_blocked.xml [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/templates/response_doblock.xml [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/templates/response_dodelete.xml [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/templates/response_dounblock.xml [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/templates/updatetable.html [new file with mode: 0644]
NP_TrackBack/branches/DOM-branch/trackback/templates/updatetablefinished.html [new file with mode: 0644]
NP_TrackBack/trunk/NP_TrackBack.php [new file with mode: 0644]
NP_TrackBack/trunk/trackback/CHANGELOG.txt [new file with mode: 0644]
NP_TrackBack/trunk/trackback/autodetect.php [new file with mode: 0644]
NP_TrackBack/trunk/trackback/detectlist.php [new file with mode: 0644]
NP_TrackBack/trunk/trackback/grid.php [new file with mode: 0644]
NP_TrackBack/trunk/trackback/help.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/index.php [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-euc.help.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-euc.templates/all.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-euc.templates/all_ajax.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-euc.templates/blocked.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-euc.templates/blocked_ajax.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-euc.templates/form.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-euc.templates/index.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-euc.templates/list.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-euc.templates/menu.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-euc.templates/ping.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-euc.templates/response_all.xml [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-euc.templates/response_blocked.xml [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-euc.templates/response_doblock.xml [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-euc.templates/response_dodelete.xml [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-euc.templates/response_dounblock.xml [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-euc.templates/updatetable.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-euc.templates/updatetablefinished.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-utf8.help.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-utf8.templates/all.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-utf8.templates/all_ajax.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-utf8.templates/blocked.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-utf8.templates/blocked_ajax.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-utf8.templates/form.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-utf8.templates/index.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-utf8.templates/list.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-utf8.templates/menu.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-utf8.templates/ping.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-utf8.templates/response_all.xml [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-utf8.templates/response_blocked.xml [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-utf8.templates/response_doblock.xml [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-utf8.templates/response_dodelete.xml [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-utf8.templates/response_dounblock.xml [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-utf8.templates/updatetable.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/japanese-utf8.templates/updatetablefinished.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/prototype.js [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/css/coffee-with-milk.css [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/css/grayedout.css [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/css/greenHdg.css [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/css/iegradient.css [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/css/ricoCalendar.css [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/css/ricoGrid.css [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/css/ricoLiveGridForms.css [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/css/ricoMenu.css [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/css/ricoTree.css [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/css/tanChisel.css [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/css/warmfall.css [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/export-owc.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/export-plain.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/images/aline.gif [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/images/calarrow.png [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/images/calendaricon.gif [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/images/close.gif [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/images/divider.gif [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/images/doc.gif [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/images/dotbutton.gif [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/images/drop.gif [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/images/filtercol.gif [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/images/folderclosed.gif [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/images/folderopen.gif [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/images/grayedout.gif [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/images/left.gif [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/images/link.gif [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/images/node.gif [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/images/nodeblank.gif [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/images/nodelast.gif [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/images/nodeline.gif [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/images/nodem.gif [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/images/nodemlast.gif [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/images/nodep.gif [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/images/nodeplast.gif [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/images/resize.gif [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/images/ricologo.gif [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/images/right.gif [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/images/shadow.png [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/images/shadow_ll.png [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/images/shadow_ur.png [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/images/sort_asc.gif [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/images/sort_desc.gif [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/prototype.js [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/rico.js [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/ricoAjaxEngine.js [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/ricoBehaviors.js [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/ricoCalendar.js [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/ricoColor.js [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/ricoColorPicker.js [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/ricoCommon.js [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/ricoComponents.js [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/ricoDashboard.js [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/ricoDragDrop.js [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/ricoEffects.js [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/ricoGridCommon.js [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/ricoLiveGrid.js [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/ricoLiveGridAjax.js [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/ricoLiveGridForms.js [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/ricoLiveGridMenu.js [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/ricoMenu.js [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/ricoSheet.js [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/ricoSimpleGrid.js [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/ricoSimpleGrid.xsl [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/ricoSimpleGrid2xl.xsl [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/ricoStyles.js [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/ricoTree.js [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/translations/livegrid_de.js [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/translations/livegrid_es.js [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/translations/livegrid_fr.js [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/translations/livegrid_it.js [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/translations/livegrid_ja.js [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/translations/livegrid_pt.js [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/translations/livegrid_ru.js [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/translations/livegrid_ua.js [new file with mode: 0644]
NP_TrackBack/trunk/trackback/js/rico/translations/livegrid_zh.js [new file with mode: 0644]
NP_TrackBack/trunk/trackback/language/english.php [new file with mode: 0644]
NP_TrackBack/trunk/trackback/language/japanese-euc.php [new file with mode: 0644]
NP_TrackBack/trunk/trackback/language/japanese-utf8.php [new file with mode: 0644]
NP_TrackBack/trunk/trackback/mkeuc.sh [new file with mode: 0644]
NP_TrackBack/trunk/trackback/silk/accept.png [new file with mode: 0644]
NP_TrackBack/trunk/trackback/silk/application_view_list.png [new file with mode: 0644]
NP_TrackBack/trunk/trackback/silk/cross.png [new file with mode: 0644]
NP_TrackBack/trunk/trackback/silk/delete.png [new file with mode: 0644]
NP_TrackBack/trunk/trackback/silk/exclamation.png [new file with mode: 0644]
NP_TrackBack/trunk/trackback/silk/help.png [new file with mode: 0644]
NP_TrackBack/trunk/trackback/silk/house_go.png [new file with mode: 0644]
NP_TrackBack/trunk/trackback/silk/link.png [new file with mode: 0644]
NP_TrackBack/trunk/trackback/silk/link_break.png [new file with mode: 0644]
NP_TrackBack/trunk/trackback/silk/plugin_edit.png [new file with mode: 0644]
NP_TrackBack/trunk/trackback/silk/readme.txt [new file with mode: 0644]
NP_TrackBack/trunk/trackback/silk/tick.png [new file with mode: 0644]
NP_TrackBack/trunk/trackback/silk/transmit_go.png [new file with mode: 0644]
NP_TrackBack/trunk/trackback/template.php [new file with mode: 0644]
NP_TrackBack/trunk/trackback/templates/all.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/templates/all_ajax.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/templates/blocked.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/templates/blocked_ajax.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/templates/form.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/templates/index.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/templates/list.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/templates/menu.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/templates/ping.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/templates/response_all.xml [new file with mode: 0644]
NP_TrackBack/trunk/trackback/templates/response_blocked.xml [new file with mode: 0644]
NP_TrackBack/trunk/trackback/templates/response_doblock.xml [new file with mode: 0644]
NP_TrackBack/trunk/trackback/templates/response_dodelete.xml [new file with mode: 0644]
NP_TrackBack/trunk/trackback/templates/response_dounblock.xml [new file with mode: 0644]
NP_TrackBack/trunk/trackback/templates/updatetable.html [new file with mode: 0644]
NP_TrackBack/trunk/trackback/templates/updatetablefinished.html [new file with mode: 0644]

diff --git a/NP_TrackBack/branches/DOM-branch/NP_TrackBack.php b/NP_TrackBack/branches/DOM-branch/NP_TrackBack.php
new file mode 100644 (file)
index 0000000..c03d820
--- /dev/null
@@ -0,0 +1,1984 @@
+<?php\r
+\r
+// vim: tabstop=4:shiftwidth=4\r
+\r
+/* ==========================================================================================\r
+ * Trackback 2.0 for Nucleus CMS \r
+ * ==========================================================================================\r
+ * This program is free software and open source software; you can redistribute\r
+ * it and/or modify it under the terms of the GNU General Public License as\r
+ * published by the Free Software Foundation; either version 2 of the License,\r
+ * or (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful, but WITHOUT\r
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\r
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for\r
+ * more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  or visit\r
+ * http://www.gnu.org/licenses/gpl.html\r
+ * ==========================================================================================\r
+ * NP_Trackback.php\r
+ *\r
+ * @author    rakaz\r
+ * @author    nakahara21\r
+ * @author    hsur\r
+ * @author    shizuki\r
+ * @copyright 2002-2009 rakaz\r
+ * @copyright 2002-2009 rakaznakahara21\r
+ * @copyright 2002-2009 hsur\r
+ * @copyright 2002-2009 shizuki\r
+ * @license http://nucleuscms.org/license.txt GNU General Public License\r
+ * @link http://japan.nucleuscms.org/wiki/plugins:trackback\r
+ * @version 2.0.3 jp13 DOMDocument-branche $Id$\r
+ */\r
+\r
+/**\r
+ * class NP_TrackBack\r
+ *\r
+ * @since first version\r
+ */\r
+\r
+class NP_TrackBack extends NucleusPlugin\r
+{\r
+\r
+/**\r
+ * Plugin API calls, for installation, configuration and setup\r
+ */\r
+\r
+// {{{ function getName()\r
+\r
+    /**\r
+     * return PLUGIN's name\r
+     *\r
+     * @retrun string\r
+     */\r
+    function getName()\r
+    {\r
+        return 'Nucleus CMS TrackBack plugin DOM-branche';\r
+    }\r
+\r
+// }}}\r
+// {{{ function getAuthor()\r
+\r
+    /**\r
+     * return PLUGIN's author(s)\r
+     *\r
+     * @retrun string\r
+     */\r
+    function getAuthor()\r
+    {\r
+        return 'rakaz + nakahara21 + hsur + shizuki';\r
+    }\r
+\r
+// }}}\r
+// {{{ function getURL()\r
+\r
+    /**\r
+     * return URL of distribution site or author's e-mail address\r
+     *\r
+     * @retrun string\r
+     */\r
+    function getURL()\r
+    {\r
+        return 'http://japan.nucleuscms.org/wiki/plugins:trackback';\r
+    }\r
+\r
+// }}}\r
+// {{{ function getVersion()\r
+\r
+    /**\r
+     * return PLUGIN's version\r
+     *\r
+     * @retrun string\r
+     */\r
+    function getVersion()\r
+    {\r
+        return '2.0.3 jp13 DOM-branche $Revision$';\r
+    }\r
+\r
+// }}}\r
+// {{{ function getDescription()\r
+\r
+    /**\r
+     * return PLUGIN's description\r
+     *\r
+     * @retrun string\r
+     */\r
+    function getDescription()\r
+    {\r
+        return '[2.0.3 jp13 DOM-branche $Revision$]<br />' . _TB_DESCRIPTION;\r
+    }\r
+\r
+// }}}\r
+// {{{ function getTableList()\r
+\r
+    /**\r
+     * return data base tables this plugin uses\r
+     *\r
+     * @retrun array\r
+     */\r
+    function getTableList()\r
+    {\r
+        $retArr = array(\r
+            sql_table("plugin_tb"),\r
+            sql_table("plugin_tb_lookup"),\r
+            sql_table('plugin_tb_lc')\r
+        );\r
+        return $retArr;\r
+     }\r
+\r
+// }}}\r
+// {{{ function getEventList()\r
+\r
+    /**\r
+     * return Nucleus CMS APIs this plugin uses\r
+     *\r
+     * @retrun array\r
+     */\r
+    function getEventList()\r
+    {\r
+        $retArr = array(\r
+            'QuickMenu',\r
+            'PostAddItem',\r
+            'AddItemFormExtras',\r
+            'EditItemFormExtras',\r
+            'PreUpdateItem',\r
+            'PrepareItemForEdit',\r
+//          'BookmarkletExtraHead',\r
+            'RetrieveTrackback',\r
+            'SendTrackback',\r
+            'InitSkinParse',\r
+            'TemplateExtraFields'\r
+        );\r
+        return $retArr;\r
+    }\r
+\r
+// }}}\r
+// {{{ function getMinNucleusVersion()\r
+\r
+    /**\r
+     * return Lowest Nucleus CMS version by which this plugin operates\r
+     *\r
+     * @retrun array\r
+     */\r
+    function getMinNucleusVersion()\r
+    {\r
+        return 341;\r
+    }\r
+\r
+// }}}\r
+// {{{ function supportsFeature($feature)\r
+\r
+    /**\r
+     * return "true" if feature support\r
+     *\r
+     * @param str feature name\r
+     * @retrun int\r
+     */\r
+    function supportsFeature($feature)\r
+    {\r
+        switch($feature) {\r
+            case 'SqlTablePrefix':\r
+                return 1;\r
+            default:\r
+                return 0;\r
+        }\r
+    }\r
+\r
+// }}}\r
+// {{{ function install()\r
+\r
+    /**\r
+     * setup NP_TrackBack\r
+     *\r
+     * @retrun void\r
+     */\r
+    function install()\r
+    {\r
+        switch (strtoupper(_CHARSET) == 'UTF-8') {\r
+            case 'UTF-8':\r
+                $collate = 'utf8_unicode_ci';\r
+                $charset = 'utf8';\r
+                break;\r
+            case 'EUC-JP':\r
+                $collate = 'ujis_japanese_ci';\r
+                $charset = 'ujis';\r
+                break;\r
+            default:\r
+                $collate = 'latin1_swedish_ci';\r
+                $charset = 'latin1';\r
+                break;\r
+        }\r
+        // Create tables\r
+        sql_query("\r
+            CREATE TABLE IF NOT EXISTS `" . sql_table('plugin_tb') . "` (\r
+                `id`        int(11) NOT NULL AUTO_INCREMENT,\r
+                `tb_id`     int(11) NOT NULL,\r
+                `url`       text COLLATE utf8_unicode_ci NOT NULL,\r
+                `block`     tinyint(4) NOT NULL,\r
+                `spam`      tinyint(4) NOT NULL,\r
+                `link`      tinyint(4) NOT NULL,\r
+                `title`     text COLLATE " . $collate . ",\r
+                `excerpt`   text COLLATE " . $collate . ",\r
+                `blog_name` text COLLATE " . $collate . ",\r
+                `timestamp` datetime DEFAULT NULL,\r
+                PRIMARY     KEY (`id`),\r
+                            KEY `tb_id_block_timestamp_idx` (`tb_id`,`block`,`timestamp`)\r
+            ) ENGINE=MyISAM DEFAULT CHARSET=" . $charset . " COLLATE=" . $collate\r
+        );\r
+\r
+        sql_query("\r
+            CREATE TABLE IF NOT EXISTS `" . sql_table('plugin_tb_lc') . "` (\r
+                `tb_id`   int(11) NOT NULL,\r
+                `from_id` int(11) NOT NULL,\r
+                PRIMARY   KEY (`tb_id`,`from_id`)\r
+            ) ENGINE=MyISAM DEFAULT CHARSET=" . $charset . " COLLATE=" . $collate\r
+        );\r
+\r
+        sql_query("\r
+            CREATE TABLE IF NOT EXISTS `" . sql_table('plugin_tb_lookup') . "` (\r
+                `link`  text COLLATE " . $collate . " NOT NULL,\r
+                `url`   text COLLATE " . $collate . " NOT NULL,\r
+                `title` text COLLATE " . $collate . ",\r
+                PRIMARY KEY (`link`(100))\r
+            ) ENGINE=MyISAM DEFAULT CHARSET=" . $charset . " COLLATE=" . $collate\r
+        );\r
+\r
+        // plugin options\r
+\r
+        // global options\r
+        $this->createOption('AcceptPing',  _TB_AcceptPing,  'yesno', 'yes');\r
+        $this->createOption('SendPings',   _TB_SendPings,   'yesno', 'yes');\r
+        $this->createOption('AutoXMLHttp', _TB_AutoXMLHttp, 'yesno', 'yes');\r
+        $this->createOption('CheckIDs',    _TB_CheckIDs,    'yesno', 'yes');\r
+        $this->createOption('dateFormat',  _TB_dateFormat,  'text',  _TB_dateFormat_VAL);\r
+        $this->createOption('NotifyEmail', _TB_NotifyEmail, 'text',  '');\r
+        $this->createOption('DropTable',   _TB_DropTable,   'yesno', 'no');\r
+        $this->createOption('HideUrl',     _TB_HideUrl,     'yesno', 'yes');\r
+        $this->createOption('ajaxEnabled', _TB_ajaxEnabled, 'yesno', 'no');\r
+\r
+        // default templates\r
+        $this->createOption('tplHeader',      _TB_tplHeader,       'textarea', _TB_tplHeader_VAL);\r
+        $this->createOption('tplEmpty',       _TB_tplEmpty,        'textarea', _TB_tplEmpty_VAL);\r
+        $this->createOption('tplItem',        _TB_tplItem,         'textarea', _TB_tplItem_VAL);\r
+        $this->createOption('tplFooter',      _TB_tplFooter,       'textarea', _TB_tplFooter_VAL);\r
+        $this->createOption('tplLocalHeader', _TB_tplLocalHeader,  'textarea', _TB_tplLocalHeader_VAL);\r
+        $this->createOption('tplLocalEmpty',  _TB_tplLocalEmpty,   'textarea', _TB_tplLocalEmpty_VAL);\r
+        $this->createOption('tplLocalItem',   _TB_tplLocalItem,    'textarea', _TB_tplLocalItem_VAL);\r
+        $this->createOption('tplLocalFooter', _TB_tplLocalFooter,  'textarea', _TB_tplLocalFooter_VAL);\r
+        $this->createOption('tplTbNone',      _TB_tplTbNone,       'text',     "No Trackbacks");\r
+        $this->createOption('tplTbOne',       _TB_tplTbOne,        'text',     "1 Trackback");\r
+        $this->createOption('tplTbMore',      _TB_tplTbMore,       'text',     "<%number%> Trackbacks");\r
+        $this->createOption('tplTbNoAccept',  _TB_tplNO_ACCEPT,    'text',     "Sorry, no trackback pings are accepted.");\r
+\r
+        // blog options\r
+        $this->createBlogOption('NotifyEmailBlog',    _TB_NotifyEmailBlog,   'text',   ''); \r
+        $this->createBlogOption('isAcceptW/OLinkDef', _TB_isAcceptWOLinkDef, 'select', 'block', _TB_isAcceptWOLinkDef_VAL);\r
+        $this->createBlogOption('AllowTrackBack',     _TB_AllowTrackBack,    'yesno',  'yes');\r
+\r
+        // item options\r
+        $this->createItemOption('ItemAcceptPing',  _TB_ItemAcceptPing, 'yesno',  'yes');\r
+        $this->createItemOption('isAcceptW/OLink', _TB_isAcceptWOLink, 'select', 'default', _TB_isAcceptWOLink_VAL);\r
+    }\r
+\r
+// }}}\r
+// {{{ function uninstall()\r
+\r
+    /**\r
+     * delete TrackBack table if uninstall\r
+     *\r
+     * @retrun void\r
+     */\r
+    function uninstall()\r
+    {\r
+        if ($this->getOption('DropTable') == 'yes') {\r
+            sql_query ('DROP TABLE ' . sql_table('plugin_tb'));\r
+            sql_query ('DROP TABLE ' . sql_table('plugin_tb_lookup'));\r
+            sql_query ('DROP TABLE ' . sql_table('plugin_tb_lc'));\r
+        }\r
+    }\r
+\r
+// }}}\r
+// {{{ function init()\r
+\r
+    /**\r
+     * initialize\r
+     *\r
+     * @retrun void\r
+     */\r
+    function init()\r
+    {\r
+        // include language file for this plugin \r
+        $language = ereg_replace( '[\\|/]', '', getLanguageName()); \r
+        if (file_exists($this->getDirectory() . 'language/' . $language . '.php')) {\r
+            include_once($this->getDirectory() . 'language/' . $language . '.php'); \r
+        } else {\r
+            include_once($this->getDirectory() . 'language/english.php'); \r
+        }\r
+        $this->notificationMail      = _TB_NORTIFICATION_MAIL_BODY;\r
+        $this->notificationMailTitle = _TB_NORTIFICATION_MAIL_TITLE;\r
+        $this->userAgent             = $this->getName() . ' ( ' . $this->getVersion() . ' )';\r
+    }\r
+\r
+// }}}\r
+// {{{ function doSkinVar($skinType, $what = '', $tb_id = '', $amount = 'limit-1', $template = '')\r
+\r
+    /**\r
+     * skin vars process\r
+     *\r
+     * @param str\r
+     * @param str\r
+     * @param int\r
+     * @param int/str\r
+     * @param str\r
+     */\r
+    function doSkinVar($skinType, $what = '', $tb_id = '', $amount = 'limit-1', $template = '')\r
+    {\r
+        global $itemid, $manager, $CONF;\r
+        if(preg_match('/limit/i', $tb_id)){\r
+            $amount = $tb_id;\r
+            $tb_id  = '';\r
+        }\r
+        $amount = intval(str_replace('limit', '', $amount));\r
+        if ($tb_id == '') {\r
+            $tb_id = intval($itemid);\r
+        }\r
+        $isAcceptPing = $this->isAcceptTrackBack($tb_id);\r
+        switch ($what) {\r
+            case 'tbcode':\r
+            case 'code':\r
+                // Insert Auto-discovery RDF code\r
+                $spamcheck = array (\r
+                    'type'     => 'tbcode',\r
+                    'id'       => -1,\r
+                    'title'    => '',\r
+                    'excerpt'  => '',\r
+                    'blogname' => '',\r
+                    'url'      => '',\r
+                    'return'   => true,\r
+                    'live'     => true,\r
+                    'data'     => '', //Backwards compatibility with SpamCheck API 1\r
+                    'ipblock'  => true,\r
+                );\r
+//              $manager->notify('SpamCheck', array ('spamcheck' => & $spamcheck));\r
+                $spam = false;\r
+                if (isset($spamcheck['result']) && $spamcheck['result'] == true){\r
+                    $spam = true;\r
+                }\r
+                if($skinType == 'item' && !$spam && $isAcceptPing) {\r
+                    $this->insertCode($tb_id);\r
+                }\r
+                break;\r
+            case 'tburl':\r
+            case 'url':\r
+                // Insert TrackBack URL\r
+                if($isAcceptPing) {\r
+                    echo $this->getTrackBackUrl($tb_id);\r
+                } else {\r
+                    if (!empty($template)) {\r
+                        $template =& $manager->getTemplate($template); \r
+                        $template =  $template['NP_TrackBack_tplTbNoAccept'];\r
+                    } else {\r
+                        $template =  $this->getOption('tplTbNoAccept');\r
+                    }\r
+                    echo TEMPLATE::fill($template, array());\r
+                }\r
+                break;\r
+            case 'form':\r
+            case 'manualpingformlink':\r
+                // Insert manual ping URL\r
+                echo $this->getManualPingUrl($tb_id);\r
+                break;\r
+            case 'sendpinglink':\r
+                echo $manager->addTicketToUrl($this->getAdminURL() . 'index.php?action=ping&amp;id=' . intval($tb_id));\r
+                break;\r
+            case 'count':\r
+                // Insert TrackBack count\r
+                $count = $this->getTrackBackCount($tb_id);\r
+                if (!empty($template)) {\r
+                    $template =& $manager->getTemplate($template);\r
+                }\r
+                switch ($count) {\r
+                    case 0:\r
+                        if (is_array($template)) {\r
+                            $template =  $template['NP_TrackBack_tplTbNone'];\r
+                        } else {\r
+                            $template =  $this->getOption('tplTbNone');\r
+                        }\r
+                        break;\r
+                    case 1:\r
+                        if (is_array($template)) {\r
+                            $template =  $template['NP_TrackBack_tplTbOne'];\r
+                        } else {\r
+                            $template =  $this->getOption('tplTbOne');\r
+                        }\r
+                        break;\r
+                    default:\r
+                        if (is_array($template)) {\r
+                            $template =  $template['NP_TrackBack_tplTbMore'];\r
+                        } else {\r
+                            $template =  $this->getOption('tplTbMore');\r
+                        }\r
+                        break;\r
+                }\r
+                echo TEMPLATE::fill($template, array('number' => $count));\r
+                break;\r
+            case 'list':\r
+            case '':\r
+                // Shows the TrackBack list\r
+                $this->showList($tb_id, $amount);\r
+                break;\r
+            case 'required':\r
+                // show requred URL\r
+                echo  $this->getRequiredURL($tb_id);\r
+                break;\r
+            case 'locallist':\r
+                // shows the Local list\r
+                $this->showLocalList($tb_id);\r
+                break;\r
+            default:\r
+                return;\r
+        }\r
+    }\r
+\r
+// }}}\r
+// {{{ function doTemplateVar(&$item, $what = '', $template = '')\r
+\r
+    /**\r
+     * template vars process\r
+     *\r
+     * @param obj\r
+     * @param str\r
+     * @param str\r
+     */\r
+    function doTemplateVar(&$item, $what = '', $template = '')\r
+    {\r
+        $this->doSkinVar('template', $what, $item->itemid, $template);\r
+    }\r
+\r
+// }}}\r
+// {{{ function doTemplateCommentsVar(&$item, &$comment, $what = '', $template = '')\r
+\r
+    /**\r
+     * comment template vars process\r
+     *\r
+     * @param obj\r
+     * @param obj\r
+     * @param str\r
+     * @param str\r
+     */\r
+    function doTemplateCommentsVar(&$item, &$comment, $what = '', $template = '')\r
+    {\r
+        $this->doSkinVar('templatecomments', $what, $item->itemid, $template);\r
+    }\r
+\r
+// }}}\r
+// {{{ function doAction($type)\r
+\r
+    /**\r
+     * A trackback ping is to be received on the URL\r
+     * http://yourdomain.com/action.php?action=plugin&name=TrackBack&tb_id=1234\r
+     * Extra variables to be passed along are url, title, excerpt, blog_name\r
+     *\r
+     * @param str\r
+     */\r
+    function doAction($type)\r
+    {\r
+        global $CONF,$manager;\r
+        $aActionsNotToCheck = array(\r
+            '',\r
+            'ping',\r
+            'form',\r
+            'redirect',\r
+            'left',\r
+        );\r
+        if (!in_array($type, $aActionsNotToCheck)) {\r
+            if (!$manager->checkTicket()) return _ERROR_BADTICKET;\r
+        }\r
+        switch ($type) {\r
+            case '':\r
+            // When no action type is given, assume it's a ping\r
+                $errorMsg = $this->handlePing();\r
+                $this->xmlResponse($errorMsg);\r
+                break; \r
+            case 'ping':\r
+            // Manual ping\r
+                $errorMsg = $this->handlePing();\r
+                if ($errorMsg != '') {\r
+                    $this->showManualPingError(intRequestVar('tb_id'), $errorMsg);\r
+                } else {\r
+                    $this->showManualPingSuccess(intRequestVar('tb_id'));\r
+                }\r
+                break; \r
+            case 'form':\r
+            // Show manual ping form\r
+                $tb_id        = intRequestVar('tb_id');\r
+                $isAcceptPing = $this->isAcceptTrackBack($tb_id);\r
+                if ($isAcceptPing) {\r
+                    $this->showManualPingForm($tb_id);\r
+                } else {\r
+                    if (!empty(requestVar['template'])) {\r
+                        $template =& $manager->getTemplate(requestVar['template']); \r
+                        $template =  $template['NP_TrackBack_tplTbNoAccept'];\r
+                    } else {\r
+                        $template =  $this->getOption('tplTbNoAccept');\r
+                    }\r
+                    echo TEMPLATE::fill($template, array());\r
+                }\r
+                break;\r
+            case 'detect':\r
+            // Detect trackback\r
+                list($url, $title) = $this->getURIfromLink(html_entity_decode(requestVar('tb_link')));\r
+                $url   = addslashes($url);\r
+                $url   = $this->_utf8_to_javascript($url);\r
+                $title = addslashes($title);\r
+                $title = $this->_utf8_to_javascript($title);\r
+                echo "tbDone('" . requestVar('tb_link') . "', '" . $url . "', '" . $title . "');";\r
+                break;\r
+            case 'redirect':\r
+            // redirect \r
+                return $this->redirect(intRequestVar('tb_id'), requestVar('urlHash'));\r
+                break;\r
+            case 'left':\r
+                echo $this->showLeftList(intRequestVar('tb_id'), intRequestVar('amount'));\r
+                break;\r
+            case 'deletelc':\r
+            // delete a trackback(local)\r
+                $err = $this->deleteLocal(intRequestVar('tb_id'), intRequestVar('from_id'));\r
+                if ($err) {\r
+                    return $err;\r
+                }\r
+                header('Location: ' . serverVar('HTTP_REFERER'));\r
+                break;\r
+        }\r
+        exit;\r
+    }\r
+\r
+// }}}\r
+// {{{ function doIf($key = '', $value = '')\r
+\r
+    /**\r
+     * COMPARE key and value\r
+     *\r
+     * @param str\r
+     * @param str\r
+     */\r
+    function doIf($key = '', $value = '')\r
+    {\r
+        global $itemid;\r
+        //echo "key: $key, value: $value";\r
+        switch (strtolower($key)) {\r
+            case '':\r
+            case 'accept':\r
+                if ($value == '') {\r
+                    $value = 'yes';\r
+                }\r
+                $value = ($value == 'no' || (!$value)) ? false : true;\r
+                $ret   = false;\r
+                if ($itemid) {\r
+                    $ret = $this->isAcceptTrackBack($itemid);\r
+                } else {\r
+                    $ret = $this->isAcceptTrackBack();\r
+                }\r
+                return ($value == false) ? (!$ret) : $ret;\r
+            case 'required':\r
+                if ($value == '') {\r
+                    $value = 'yes';\r
+                }\r
+                $value = ($value == 'no' || (!$value)) ? false : true;\r
+                $ret = false;\r
+                if( $itemid ) {\r
+                    $ret = $this->isEnableLinkCheck($itemid);\r
+                }\r
+                return ($value == false) ? (!$ret) : $ret;\r
+            default:\r
+                return false;\r
+        }\r
+    }\r
+\r
+// }}}\r
+// {{{ function event_InitSkinParse(&$data)\r
+\r
+    /**\r
+     * A trackback ping is to be received on the URL\r
+     * http://yourdomain.com/item/1234.trackback\r
+     * Extra variables to be passed along are url, title, excerpt, blog_name\r
+     *\r
+     * @param arr\r
+     */\r
+    function event_InitSkinParse(&$data)\r
+    {\r
+        global $CONF, $itemid;\r
+        $format = requestVar('format');\r
+        if ($CONF['URLMode'] == 'pathinfo') {\r
+            if (preg_match('/(\/|\.)(trackback)(\/|$)/', serverVar('PATH_INFO'), $matches)) {\r
+                $format = $matches[2];\r
+            }\r
+        }\r
+        \r
+        if ($format == 'trackback' && $data['type'] == 'item') {\r
+            $errorMsg = $this->handlePing(intval($itemid));\r
+            if ($errorMsg != '') {\r
+                $this->xmlResponse($errorMsg);\r
+            } else {\r
+                $this->xmlResponse();\r
+            }\r
+            exit;\r
+        }\r
+    }\r
+\r
+// }}}\r
+// {{{ function event_TemplateExtraFields(&$data)\r
+\r
+    /**\r
+     * extra template field\r
+     *\r
+     * @param arr\r
+     */\r
+    function event_TemplateExtraFields(&$data)\r
+    {\r
+        $data['fields']['NP_TrackBack'] = array(\r
+            'NP_TrackBack_tplTbNoAccept' => _TB_tplNO_ACCEPT,\r
+            'NP_TrackBack_tplTbNone'     => _TB_tplTbNone,\r
+            'NP_TrackBack_tplTbOne'      => _TB_tplTbOne,\r
+            'NP_TrackBack_tplTbMore'     => _TB_tplTbMore,\r
+            'NP_TrackBack_tplItem'       => _TB_tplItem,\r
+        );\r
+    }\r
+\r
+// }}}\r
+// {{{ function event_SendTrackback($data)\r
+\r
+    /**\r
+     * trackbackping send via xmlrpc\r
+     *\r
+     * @param arr\r
+     */\r
+    function event_SendTrackback($data)\r
+    {\r
+        global $manager;\r
+        // Enable sending trackbacks for the XML-RPC API, otherwise we would \r
+        // get an error because the current user is not exactly logged in.\r
+        $this->xmlrpc =  true;\r
+        $itemid       =  intval($data['tb_id']);\r
+        $item         =& $manager->getItem($itemid, 0, 0);\r
+        if (!$item) {\r
+            return; // don't ping for draft & future\r
+        }\r
+        if ($item['draft']) {\r
+            return;   // don't ping on draft items\r
+        }\r
+        // gather some more information, needed to send the ping (blog name, etc)\r
+        $blog      =& $manager->getBlog(getBlogIDFromItemID($itemid));\r
+        $blog_name =  $blog->getName();\r
+        $title     =  $data['title'] != '' ? $data['title'] : $item['title'];\r
+        $title     =  strip_tags($title);\r
+        $excerpt   =  $data['body']  != '' ? $data['body']  : $item['body'];\r
+        $excerpt   =  strip_tags($excerpt);\r
+        $excerpt   =  $this->_cut_string($excerpt, 200);\r
+        $url       =  $this->_createItemLink($itemid, $blog);\r
+        \r
+        while (list(,$url) = each($data['urls'])) {\r
+            $res = $this->sendPing($itemid, $title, $url, $excerpt, $blog_name, $url);\r
+            if ($res) {\r
+                ACTIONLOG::add(WARNING, 'TrackBack Error:' . $res . ' (' . $url . ')');\r
+            }\r
+        }\r
+    }\r
+\r
+// }}}\r
+// {{{ function event_RetrieveTrackback($data)\r
+\r
+    /**\r
+     * trackbackping receive via xmlrpc\r
+     *\r
+     * @param arr\r
+     */\r
+    function event_RetrieveTrackback($data)\r
+    {\r
+        \r
+        $res = sql_query('\r
+            SELECT \r
+                `url`,\r
+                `title`,\r
+                UNIX_TIMESTAMP(`timestamp`) AS timestamp\r
+            FROM\r
+                `' . sql_table('plugin_tb') . '`\r
+            WHERE\r
+                `tb_id` = ' . intval($data['tb_id']) . ' AND\r
+                `block` = 0\r
+            ORDER BY\r
+                `timestamp` ASC\r
+        ');\r
+        \r
+        while ($row = sql_fetch_assoc($res)) {\r
+            $trackback = array(\r
+                'title' => $row['title'],\r
+                'url'   => $row['url'],\r
+                'ip'    => ''\r
+            );\r
+            $data['trackbacks'][] = $trackback;\r
+        }\r
+    }\r
+\r
+// }}}\r
+// {{{ function event_BookmarkletExtraHead($data)\r
+\r
+    /**\r
+     * insert extra code to <head /> tags on bookmarklet\r
+     *\r
+     * @param arr\r
+     *\r
+    function event_BookmarkletExtraHead($data)\r
+    {\r
+        global $NP_TB_URL;\r
+        list ($NP_TB_URL,) = $this->getURIfromLink(requestVar('loglink'));\r
+    } \r
+\r
+// }}}\r
+// {{{ function event_PrepareItemForEdit($data)\r
+\r
+    /**\r
+     * auto discover from item body\r
+     *\r
+     * @param arr\r
+     */\r
+    function event_PrepareItemForEdit($data)\r
+    {\r
+        if ($this->getOption('AutoXMLHttp') == 'no') {\r
+            // The space between body and more is to make sure we didn't join 2 words accidently....\r
+            $this->larray = $this->autoDiscovery($data['item']['body'] . ' ' . $data['item']['more']);\r
+        }\r
+    } \r
+\r
+// }}}\r
+// {{{ function event_PostAddItem($data)\r
+\r
+    /**\r
+     * After an item has been added to the database, send out a ping if requested\r
+     * (trackback_ping_url variable in request)\r
+     *\r
+     * @param arr\r
+     */\r
+    function event_PostAddItem($data)\r
+    {\r
+        $this->pingTrackback($data);\r
+    }\r
+\r
+// }}}\r
+// {{{ function event_PreUpdateItem($data)\r
+\r
+    /**\r
+     * After an item has been updated on the database, send out a ping if requested\r
+     * (trackback_ping_url variable in request)\r
+     *\r
+     * @param arr\r
+     */\r
+    function event_PreUpdateItem($data)\r
+    {\r
+        $this->pingTrackback($data);\r
+    }\r
+\r
+// }}}\r
+// {{{ function event_AddItemFormExtras($data)\r
+\r
+    /**\r
+     * Add trackback options to add item form/bookmarklet\r
+     *\r
+     * @param arr\r
+     */\r
+    function event_AddItemFormExtras($data)\r
+    {\r
+        $this->itemFormExtra($data, 'add');\r
+    }\r
+\r
+// }}}\r
+// {{{ function event_PreUpdateItem($data)\r
+\r
+    /**\r
+     * Add trackback options to edit item form/bookmarklet\r
+     *\r
+     * @param arr\r
+     */\r
+    function event_EditItemFormExtras($data)\r
+    {\r
+        $this->itemFormExtra($data, 'edit');\r
+    }\r
+\r
+// }}}\r
+// {{{ function showLeftList($tb_id, $offset = 0, $amount = 99999999, $templateName = '')\r
+\r
+    /**\r
+     * Show a list of left trackbacks for this ID\r
+     *\r
+     * @param int\r
+     * @param int\r
+     * @param int\r
+     * @param str\r
+     */\r
+    function showLeftList($tb_id, $offset = 0, $amount = 99999999, $templateName = '')\r
+    {\r
+        global $manager, $blog, $CONF;\r
+        $tb_id = intval($tb_id);\r
+        $out   = array();\r
+        $query = '\r
+            SELECT \r
+                `url`, \r
+                md5(`url`) as urlHash,\r
+                `blog_name`,\r
+                `excerpt`,\r
+                `title`,\r
+                UNIX_TIMESTAMP(`timestamp`) AS timestamp\r
+            FROM\r
+                `' . sql_table('plugin_tb') . '`\r
+            WHERE\r
+                `tb_id` = ' . $tb_id . ' AND\r
+                `block` = 0\r
+            ORDER BY \r
+                `timestamp` DESC\r
+        ';\r
+        if ($offset) {\r
+            $query .= ' LIMIT ' . intval($offset) . ', ' . intval($amount);\r
+        }\r
+        $res       = sql_query($query);\r
+        $templates = '';\r
+        if (!empty($templateName)) {\r
+            $templates =& $manager->getTemplate($templateName);\r
+        }\r
+        while($row = sql_fetch_array($res)) {\r
+            $row['blog_name'] = htmlspecialchars($row['blog_name'], ENT_QUOTES);\r
+            $row['title']     = htmlspecialchars($row['title'], ENT_QUOTES);\r
+            $row['excerpt']   = htmlspecialchars($row['excerpt'], ENT_QUOTES);\r
+            if (strtoupper(_CHARSET) != 'UTF-8') {\r
+                $row['blog_name'] = $this->_restore_to_utf8($row['blog_name']);\r
+                $row['title']     = $this->_restore_to_utf8($row['title']);\r
+                $row['excerpt']   = $this->_restore_to_utf8($row['excerpt']);\r
+                $row['blog_name'] = $this->_utf8_to_entities($row['blog_name']);\r
+                $row['title']     = $this->_utf8_to_entities($row['title']);\r
+                $row['excerpt']   = $this->_utf8_to_entities($row['excerpt']);\r
+            }\r
+            $iVars = array(\r
+                'action'  => $this->getTrackBackUrl($tb_id),\r
+                'form'    => $this->getManualPingUrl($tb_id),\r
+                'name'    => $row['blog_name'], ENT_QUOTES),\r
+                'title'   => $row['title'],\r
+                'excerpt' => $this->_cut_string($row['excerpt'], 400),\r
+                'url'     => htmlspecialchars($row['url'], ENT_QUOTES),\r
+                'date'    => htmlspecialchars(strftime($this->getOption('dateFormat'), $row['timestamp']), ENT_QUOTES)\r
+            );\r
+            if ($this->getOption('HideUrl') == 'yes') {\r
+                $iVars['url'] = $CONF['ActionURL'] . '?action=plugin&amp;name=TrackBack'\r
+                              . '&amp;type=redirect&amp;tb_id=' . $tb_id\r
+                              . '&amp;urlHash=' . $row['urlHash']\r
+                              . '&amp;template=' . $templateName;\r
+            } else {\r
+                $iVars['url'] = $row['url'];\r
+            }\r
+            if (is_array($templates)) {\r
+                $template = $templates['NP_TrackBack_tplItem'];\r
+            } else {\r
+                $template = $this->getOption('tplItem');\r
+            }\r
+            $out[] = TEMPLATE::fill($template, $iVars);\r
+        }\r
+        sql_free_result($res);\r
+        return implode("\n", $out);\r
+    }\r
+\r
+// }}}\r
+// {{{ function showList($tb_id, $amount = 0, $templateName = '')\r
+\r
+    /**\r
+     * Show a list of trackbacks for this ID\r
+     *\r
+     * @param int\r
+     * @param int\r
+     * @param str\r
+     */\r
+    function showList($tb_id, $amount = 0, $templateName = '')\r
+    {\r
+        $tb_id = intval($tb_id);\r
+        global $manager, $blog, $CONF, $member;\r
+        $enableHideurl = true;\r
+        // for TB LinkLookup\r
+        if( \r
+           strpos(serverVar('HTTP_USER_AGENT'), 'Hatena Diary Track') === false\r
+        || strpos(serverVar('HTTP_USER_AGENT'), 'NP_TrackBack') === false\r
+        || strpos(serverVar('HTTP_USER_AGENT'), 'TBPingLinkLookup') === false\r
+        || strpos(serverVar('HTTP_USER_AGENT'), 'MT::Plugin::BanNoReferTb') === false\r
+        || strpos(serverVar('HTTP_USER_AGENT'), 'livedoorBlog') === false\r
+        ) {\r
+            $enableHideurl = false;\r
+            $amount        = '-1';\r
+        }\r
+        $query = '\r
+            SELECT \r
+                `url`, \r
+                md5(`url`) as urlHash,\r
+                `blog_name`,\r
+                `excerpt`,\r
+                `title`,\r
+                UNIX_TIMESTAMP(`timestamp`) AS timestamp\r
+            FROM\r
+                `' . sql_table('plugin_tb') . '`\r
+            WHERE\r
+                `tb_id` = ' . $tb_id . ' AND\r
+                `block` = 0\r
+            ORDER BY \r
+                `timestamp` DESC\r
+        ';\r
+        if ($amount == '-1') {\r
+            $query .= ' LIMIT 9999999';\r
+        } elseif($amount) {\r
+            $query .= ' LIMIT ' . intval($amount);\r
+        }\r
+        if ($amount != 0) {\r
+            $res = sql_query($query);\r
+        }\r
+        $gVars = array(\r
+            'action'   => $this->getTrackBackUrl($tb_id),\r
+            'form'     => $this->getManualPingUrl($tb_id),\r
+            'required' => $this->getRequiredURL($tb_id),\r
+        );\r
+        $templates = '';\r
+        if (!empty($templateName)) {\r
+            $templates =& $manager->getTemplate($templateName);\r
+        }\r
+        if ($member->isLoggedIn()) {\r
+            $adminurl          = $manager->addTicketToUrl($this->getAdminURL() . 'index.php?action=list&id=' . $tb_id);\r
+            $pingformurl       = $manager->addTicketToUrl($this->getAdminURL() . 'index.php?action=ping&id=' . $tb_id);\r
+            $gVars['admin']    = '<a href="' . htmlspecialchars($adminurl, ENT_QUOTES) . '" target="_blank">[admin]</a>';\r
+            $gVars['pingform'] = '<a href="' . htmlspecialchars($pingformurl, ENT_QUOTES) . '" target="_blank">[pingform]</a>';\r
+        }\r
+        if (is_array($templates)) {\r
+            $tpl_Head = $templates['NP_TrackBack_tplHeader'];\r
+            $tpl_Item = $templates['NP_TrackBack_tplItem'];\r
+            $tpl_Empt = $templates['NP_TrackBack_tplEmpty'];\r
+            $tpl_Foot = $templates['NP_TrackBack_tplFooter'];\r
+        } else {\r
+            $tpl_Head = $this->getOption('tplHeader');\r
+            $tpl_Item = $this->getOption('tplItem');\r
+            $tpl_Empt = $this->getOption('tplEmpty');\r
+            $tpl_Foot = $this->getOption('tplFooter');\r
+        }\r
+        echo TEMPLATE::fill($tpl_Head, $gVars);\r
+        while ($amount != 0 && $row = sql_fetch_array($res)) {\r
+            $row['blog_name'] = htmlspecialchars($row['blog_name'], ENT_QUOTES);\r
+            $row['title']     = htmlspecialchars($row['title'], ENT_QUOTES);\r
+            $row['excerpt']   = htmlspecialchars($row['excerpt'], ENT_QUOTES);\r
+            if (strtoupper(_CHARSET) != 'UTF-8') {\r
+                $row['blog_name'] = $this->_restore_to_utf8($row['blog_name']);\r
+                $row['title']     = $this->_restore_to_utf8($row['title']);\r
+                $row['excerpt']   = $this->_restore_to_utf8($row['excerpt']);\r
+                $row['blog_name'] = mb_convert_encoding($row['blog_name'], _CHARSET, 'UTF-8');\r
+                $row['title']     = mb_convert_encoding($row['title'], _CHARSET, 'UTF-8');\r
+                $row['excerpt']   = mb_convert_encoding($row['excerpt'], _CHARSET, 'UTF-8');\r
+            }\r
+            $iVars = array(\r
+                'action'    => $this->getTrackBackUrl($tb_id),\r
+                'form'      => $this->getManualPingUrl($tb_id),\r
+                'name'      => htmlspecialchars($row['blog_name'], ENT_QUOTES),\r
+                'title'     => htmlspecialchars($row['title'], ENT_QUOTES),\r
+                'excerpt'   => htmlspecialchars($this->_cut_string($row['excerpt'], 400), ENT_QUOTES),\r
+                'url'       => htmlspecialchars($row['url'], ENT_QUOTES),\r
+                'date'      => htmlspecialchars(strftime($this->getOption('dateFormat'), $row['timestamp']), ENT_QUOTES)\r
+            );\r
+            if ($enableHideurl && $this->getOption('HideUrl') == 'yes') {\r
+                $iVars['url'] = $CONF['ActionURL'] . '?action=plugin&amp;name=TrackBack'\r
+                              . '&amp;type=redirect&amp;tb_id=' . $tb_id\r
+                              . '&amp;urlHash=' . $row['urlHash'];\r
+            } else {\r
+                $iVars['url'] = $row['url'];\r
+            }\r
+            echo TEMPLATE::fill($tpl_Item, $iVars);\r
+        }\r
+        $q = '\r
+            SELECT \r
+                count(*) \r
+            FROM \r
+                `' . sql_table('plugin_tb') . '`\r
+            WHERE \r
+                `tb_id` = ' . $tb_id . ' AND\r
+                `block` = 0\r
+            ORDER BY\r
+                `timestamp` DESC\r
+        ';\r
+        $result = sql_query($q);\r
+        $total  = sql_result($result, 0, 0);\r
+        if ($amount != -1 && $total > $amount) {\r
+            $leftcount = $total - $amount;\r
+            $adminURL  = $this->getAdminURL();\r
+            $tb_id     = intval($tb_id);\r
+            $amount    = intval($amount);\r
+            echo <<<___SCRIPTCODE___\r
+    <script type="text/javascript" src="{$adminURL}detectlist.php?tb_id={$tb_id}&amp;amount={$amount}"></script>';\r
+    <a name="restoftrackback" id="restoftrackback"></a>\r
+    <div id="tbshownavi"><a href="#restoftrackback" onclick="resttbStart(); return false;" id="tbshow">Show left {$leftcount} Trackbacks</a></div>\r
+    <div id="tbhidenavi" style="display: none;"><a href="#restoftrackback" onclick="hideresttb(); return false;">Hide {$leftcount} Trackbacks</a></div>\r
+    <div id="resttb"></div>\r
+\r
+___SCRIPTCODE___;\r
+        }\r
+        if (sql_num_rows($res) == 0) {\r
+                echo TEMPLATE::fill($tpl_Empt, $gVars);\r
+        }\r
+        sql_free_result($res);\r
+        echo TEMPLATE::fill($tpl_Foot, $gVars);\r
+    }\r
+\r
+// }}}\r
+// {{{ function getTrackBackCount($tb_id)\r
+\r
+    /**\r
+     * Returns the TrackBack count for a TrackBack item\r
+     *\r
+     * @param int\r
+     * @return str\r
+     */\r
+    function getTrackBackCount($tb_id)\r
+    {\r
+        $query = 'SELECT COUNT(*) as result FROM %s WHERE tb_id=%d AND block = 0'\r
+        return quickQuery(sprintf($query, sql_table('plugin_tb'), $tb_id));\r
+    }\r
+\r
+// }}}\r
+// {{{ function getManualPingUrl($itemid)\r
+\r
+    /**\r
+     * Returns the manual ping URL\r
+     *\r
+     * @param int\r
+     * @return str\r
+     */\r
+    function getManualPingUrl($itemid)\r
+    {\r
+        global $CONF;\r
+        return $CONF['ActionURL'] . '?action=plugin&amp;name=TrackBack&amp;type=form&amp;tb_id=' . intval($itemid);\r
+    }\r
+\r
+// }}}\r
+// {{{ function showManualPingError($itemid, $status = '')\r
+\r
+    /**\r
+     * Show the manual ping form\r
+     *\r
+     * @param int\r
+     * @param str\r
+     */\r
+    function showManualPingError($itemid, $status = '')\r
+    {\r
+        global $CONF;\r
+        $form    = true;\r
+        $error   = true;\r
+        $success = false;\r
+        sendContentType('text/html', 'admin-trackback', _CHARSET);  \r
+        require_once($this->getDirectory() . '/template.php');\r
+        $mTemplate = new Trackback_Template(null, $this->getDirectory());\r
+        $mTemplate->set ('CONF', $CONF);\r
+        $mTemplate->set ('itemid', $itemid);\r
+        $mTemplate->set ('form', $form);\r
+        $mTemplate->set ('error', $error);\r
+        $mTemplate->set ('success', $success);\r
+        $mTemplate->set ('status', $status);\r
+        $mTemplate->template('templates/form.html');\r
+        echo $mTemplate->fetch();\r
+    }\r
+\r
+// }}}\r
+// {{{ function showManualPingSuccess($itemid, $status = '')\r
+\r
+    /**\r
+     * Show the manual ping form\r
+     *\r
+     * @param int\r
+     * @param str\r
+     */\r
+    function showManualPingSuccess($itemid, $status = '')\r
+    {\r
+        global $CONF;\r
+        $form    = false;\r
+        $error   = false;\r
+        $success = true;\r
+        sendContentType('text/html', 'admin-trackback', _CHARSET);  \r
+        require_once($this->getDirectory() . '/template.php');\r
+        $mTemplate = new Trackback_Template(null, $this->getDirectory());\r
+        $mTemplate->set ('CONF', $CONF);\r
+        $mTemplate->set ('itemid', $itemid);\r
+        $mTemplate->set ('form', $form);\r
+        $mTemplate->set ('error', $error);\r
+        $mTemplate->set ('success', $success);\r
+        $mTemplate->set ('status', $status);\r
+        $mTemplate->template('templates/form.html');\r
+        echo $mTemplate->fetch();\r
+    }\r
+\r
+// }}}\r
+// {{{ function showManualPingForm($itemid, $text = '', $templateName = '')\r
+\r
+    /**\r
+     * Show the manual ping form\r
+     *\r
+     * @param int\r
+     * @param str\r
+     * @param str\r
+     */\r
+    function showManualPingForm($itemid, $text = '', $templateName = '')\r
+    {\r
+        global $CONF;\r
+        $form    = true;\r
+        $error   = false; \r
+        $success = false;\r
+        // Check if we are allowed to accept pings\r
+        if ( !$this->isAcceptTrackBack($itemid) ) {\r
+            if (!empty($templateName)) {\r
+                $templates =& $manager->getTemplate($templateName); \r
+                $template  =  $templates['NP_TrackBack_tplTbNoAccept'];\r
+            } else {\r
+                $template =  $this->getOption('tplTbNoAccept');\r
+            }\r
+            $text  =  TEMPLATE::fill($template, array());\r
+            $form  = false;\r
+            $error = true;\r
+        }\r
+        sendContentType('text/html', 'admin-trackback', _CHARSET);  \r
+        require_once($this->getDirectory() . '/template.php');\r
+        $mTemplate = new Trackback_Template(null, $this->getDirectory());\r
+        $mTemplate->set ('CONF', $CONF);\r
+        $mTemplate->set ('itemid', $itemid);\r
+        $mTemplate->set ('form', $form);\r
+        $mTemplate->set ('error', $error);\r
+        $mTemplate->set ('success', $success);\r
+        $mTemplate->set ('status', $status);\r
+        $mTemplate->template('templates/form.html');\r
+        echo $mTemplate->fetch();\r
+    }\r
+\r
+// }}}\r
+// {{{ function getTrackBackUrl($itemid)\r
+\r
+    /**\r
+     * Returns the trackback URL\r
+     *\r
+     * @param int\r
+     */\r
+    function getTrackBackUrl($itemid)\r
+    {\r
+        global $CONF;\r
+        return $CONF['ActionURL'] . '?action=plugin&amp;name=TrackBack&amp;tb_id='.$itemid;\r
+    }\r
+\r
+// }}}\r
+// {{{ function itemFormExtra($data, $type = 'add')\r
+\r
+    /**\r
+     * Add trackback options to add/edit item form/bookmarklet\r
+     *\r
+     * @param int\r
+     * @param str\r
+     */\r
+    function itemFormExtra($data, $type = 'add')\r
+    {\r
+        $listIt = _TB_LIST_IT;\r
+        $admURL = $this->getAdminURL();\r
+        echo <<<___FORMEXTRA___\r
+    <h3>TrackBack</h3>\r
+    <p>\r
+        <label for="plug_tb_url">TrackBack URL:</label><br />\r
+        <textarea id="plug_tb_url" name="trackback_ping_url" cols="60" rows="5" style="font:normal xx-small Tahoma, Arial, verdana ;"></textarea>\r
+        <input type="button" name="btnAdd" value="{$listIt}" onClick="AddStart()" /><br />\r
+\r
+___FORMEXTRA___;\r
+        $XMLHttp = $this->getOption('AutoXMLHttp');\r
+        if ($XMLHttp == 'yes') {\r
+            echo <<<___FORMEXTRA___\r
+        <div id="tb_auto">\r
+            <input type="button" name="discoverit" value="Auto Discover" onclick="tbSetup();" />\r
+            <img id='tb_busy' src='{$admURL}busy.gif' style="display:none;" /><br />\r
+            <div id="tb_auto_title"></div>\r
+            <table border="1">\r
+                <tbody id="tb_ping_list"></tbody>\r
+            </table>\r
+            <input type="hidden" id="tb_url_amount" name="tb_url_amount" value="0" /> \r
+        </div>\r
+\r
+___FORMEXTRA___;\r
+            $this->jsautodiscovery();\r
+    } elseif ($type == 'edit' && $XMLHttp != 'yes') {\r
+            if (count($this->larray) > 0) {\r
+                echo "\nAuto Discovered Ping URL's:<br />\n";\r
+                echo '<input type="hidden" name="tb_url_amount" value="'.count($this->larray).'" />';\r
+                $i = 0;\r
+                while (list($url, $title) = each($this->larray)) {\r
+                    if (_CHARSET != 'UTF-8') {\r
+                        $title = $this->_utf8_to_entities($title);\r
+                        $title = mb_convert_encoding($title, _CHARSET, 'UTF-8');\r
+                    }\r
+                    echo '<input type="checkbox" name="tb_url_' . $i . '" value="' . $url . '" id="tb_url_' . $i . '" />';\r
+                    echo '<label for="tb_url_' . $i . '" title="' . $url . '">' . $title . '</label><br />';\r
+                    $i++;\r
+                }\r
+            }\r
+        }\r
+        echo "</p>\n";\r
+    }\r
+\r
+// }}}\r
+// {{{ function jsautodiscovery()\r
+\r
+    /**\r
+     * Insert Javascript AutoDiscovery routines\r
+     */\r
+    function jsautodiscovery()\r
+    {\r
+        echo '<script type="text/javascript" src="' . $this->getAdminURL() . 'autodetect.php"></script>';\r
+    }\r
+\r
+// }}}\r
+// {{{ function insertCode($itemid)\r
+\r
+    /**\r
+     * Insert RDF code for item\r
+     *\r
+     * @param int\r
+     */\r
+    function insertCode($itemid)\r
+    {\r
+        $itemid = intval($itemid);\r
+        global $manager, $CONF;\r
+        $item  =& $manager->getItem($itemid, 0, 0);\r
+        $blog  =& $manager->getBlog(getBlogIDFromItemID($item['itemid']));\r
+        $uri   =  $this->_createItemLink($item['itemid'], $blog);\r
+        $title =  strip_tags($item['title']);\r
+        $desc  =  strip_tags($item['body']);\r
+        $desc  =  $this->_cut_string($desc, 200);\r
+        $desc  =  htmlspecialchars($desc, ENT_QUOTES);\r
+        $tburl =  $this->getTrackBackUrl($itemid);\r
+        $time  =  strftime('%Y-%m-%dT%H:%M:%S');\r
+        echo <<<___RDFCODE___\r
+<!--\r
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"\r
+    xmlns:dc="http://purl.org/dc/elements/1.1/"\r
+    xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">\r
+<rdf:Description\r
+    rdf:about="{$uri}"\r
+    dc:identifier="{$uri}"\r
+    dc:title="{$title}"\r
+    dc:description="{$desc}"\r
+    trackback:ping="{$tburi}"\r
+    dc:date="{$time}" />\r
+</rdf:RDF>\r
+-->\r
+\r
+___RDFCODE___;\r
+    }\r
+\r
+// }}}\r
+// {{{ function rssResponse($tb_id)\r
+\r
+    /**\r
+     * Retrieving TrackBack Pings (when __mode=rss)\r
+     *\r
+     * @param int\r
+     */\r
+    function rssResponse($tb_id)\r
+    {\r
+        $tb_id = intval($tb_id);\r
+        global $manager, $CONF;\r
+        $item =& $manager->getItem($tb_id, 0, 0);\r
+        if ($item) {\r
+            $blog      =& $manager->getBlog(getBlogIDFromItemID($item['itemid']));\r
+            $blog_name =  $this->_restore_to_utf8($blog->getName());\r
+            $title     =  $this->_restore_to_utf8($item['title']);\r
+            $excerpt   =  $this->_restore_to_utf8($item['body']);\r
+            $excerpt   =  $this->_cut_string($excerpt, 200);\r
+            $url       =  $this->_createItemLink($item['itemid'], $blog);\r
+\r
+            // Create response XML\r
+            $dom      =  new DOMDocument('1.0', 'UTF-8');\r
+            $response =  $dom->appendChild($dom->createElement('response'));\r
+            $response->appendChild($dom->createElement('error', '0'));\r
+            $rss      =  $response->appendChild($dom->createElement('rss'));\r
+            $rss->setAttribute("version", "0.91");\r
+            $channel  =  $rss->appendChild($dom->createElement('channel'));\r
+            $channel->appendChild($dom->createElement('title', htmlspecialchars($title, ENT_QUOTES)));\r
+            $channel->appendChild($dom->createElement('link', htmlspecialchars($url, ENT_QUOTES)));\r
+            $channel->appendChild($dom->createElement('description', htmlspecialchars($excerpt, ENT_QUOTES)));\r
+\r
+            $query = 'SELECT '\r
+                   .    '`url`, '\r
+                   .    '`blog_name`, '\r
+                   .    '`excerpt`, '\r
+                   .    '`title`, '\r
+                   .    'UNIX_TIMESTAMP(`timestamp`) as timestamp '\r
+                   . 'FROM '\r
+                   .    sql_table('plugin_tb') . ' '\r
+                   . 'WHERE '\r
+                   .    '`tb_id` = ' . $tb_id . ' AND '\r
+                   .    '`block` = 0 '\r
+                   . 'ORDER BY '\r
+                   .    '`timestamp` DESC';\r
+            $res   = sql_query($query);\r
+            while($data = sql_fetch_assoc($res)) {\r
+                $data['title']   = htmlspecialchars($this->_restore_to_utf8($data['title']), ENT_QUOTES);\r
+                $data['excerpt'] = htmlspecialchars($this->_restore_to_utf8($data['excerpt']), ENT_QUOTES);\r
+                $data['url']     = htmlspecialchars($data['url'], ENT_QUOTES);\r
+                $item            = $channel->appendChild($dom->createElement('item'));\r
+                $item->appendChild($dom->createElement('title', $data['title']);\r
+                $item->appendChild($dom->createElement('link', $data['url']);\r
+                $item->appendChild($dom->createElement('description', $data['excerpt']);\r
+            }\r
+            header('Content-Type: text/xml');\r
+            echo $dom->saveXML();\r
+        } else {\r
+            $this->xmlResponse(_ERROR_NOSUCHITEM);\r
+        }\r
+    }\r
+\r
+// }}}\r
+// {{{ function sendPing($itemid, $title, $url, $excerpt, $blog_name, $ping_url)\r
+\r
+    /**\r
+     * Send a Trackback ping to another website\r
+     *\r
+     * @param int\r
+     * @param str\r
+     * @param str\r
+     * @param str\r
+     * @param str\r
+     * @param str\r
+     */\r
+    function sendPing($itemid, $title, $url, $excerpt, $blog_name, $ping_url)\r
+    {\r
+//        $sendEncoding = 'UTF-8';\r
+        // 1. Check some basic things\r
+        if (!$this->canSendPing()) {\r
+            return _TB_msgNOTALLOWED_SEND;\r
+        }\r
+        if ($this->getOption('SendPings') == 'no') {\r
+            return _TB_msgDISABLED_SEND;\r
+        }\r
+        if ($ping_url == '') {\r
+            return _TB_msgNO_SENDER_URL;\r
+        }\r
+        // 2. Check if protocol is correct http URL\r
+        $parsed_url = parse_url($ping_url);\r
+        if (strpos($parsed_url['scheme'], 'http') !== 0 || !$parsed_url['host']) {\r
+                return _TB_msgBAD_SENDER_URL;\r
+        }\r
+        // 3. Create contents\r
+//        if ($sendEncoding != _CHARSET) {\r
+        if (strtoupper(_CHARSET) != 'UTF-8') {\r
+            $title     = mb_convert_encoding($title, 'UTF-8', _CHARSET);\r
+            $excerpt   = mb_convert_encoding($excerpt, 'UTF-8', _CHARSET);\r
+            $blog_name = mb_convert_encoding($blog_name, 'UTF-8', _CHARSET);\r
+        }\r
+        $ch      = curl_init();\r
+        $data    = array(\r
+            'title'     => $title,\r
+            'url'       => $url,\r
+            'excerpt'   => $excerpt,\r
+            'blog_name' => $blog_name\r
+        );\r
+        $options = array(\r
+            CURLOPT_URL            => $ping_url,\r
+            CURLOPT_POST           => 1,\r
+            CURLOPT_POSTFIELDS     => $data,\r
+            CURLOPT_RETURNTRANSFER => 1,\r
+        );\r
+        curl_setopt_array($ch, $options);\r
+        $response = curl_exec($ch);\r
+        if ($response === false) {\r
+            return sprintf(_TB_msgCOULDNOT_SEND_PING, curl_error($ch), curl_errno($ch));\r
+        }\r
+        $respCd = curl_getinfo($ch, CURLINFO_HTTP_CODE);\r
+        if ($respCd != 200) {\r
+            return sprintf(_TB_msgRESP_HTTP_ERROR, $respCd, curl_error($ch));\r
+        }\r
+        $domDoc   = new DOMDocument;\r
+        $domDoc->preserveWhiteSpace = false;\r
+        $domDoc->loadXML($response);\r
+        $encoding = $dom->encoding;\r
+        if (empty($encoding)) {\r
+            $encoding = $this->_detect_encoding($response);  //mb_detect_encoding($response, 'ASCII,ISO-2022-JP,UTF-8,EUC-JP,SJIS')\r
+        }\r
+        if (strtoupper($encoding) != "UTF-8" && strtoupper($encoding) != "ISO-8859-1") {\r
+            $response = @mb_convert_encoding($response, "UTF-8", $encoding);\r
+            $domDoc   = new DOMDocument;\r
+            $domDoc->preserveWhiteSpace = false;\r
+            $domDoc->loadXML($response);\r
+        }\r
+        $errors  = $domDoc->getElementsByTagName('error');\r
+        $error   = $errors->item(0)->nodeValue;\r
+        if (intval($error)) {\r
+            $mesages = $domDoc->getElementsByTagName('message');\r
+            $mesage  = $mesages->item(0)->nodeValue;\r
+            if (strtoupper(_CHARSET) != 'UTF-8') {\r
+                $mesage = @mb_convert_encoding($mesage, _CHARSET, "UTF-8");\r
+            }\r
+            return sprintf(_TB_msgAN_ERROR_OCCURRED, htmlspecialchars($mesage, ENT_QUOTES));\r
+        }\r
+        return '';\r
+    }\r
+\r
+// }}}\r
+// {{{ function handlePing($tb_id = 0)\r
+\r
+    /**\r
+     * Send a Trackback ping to another website\r
+     *\r
+     * @param int\r
+     * @return str\r
+     */\r
+    function handlePing($tb_id = 0)\r
+    {\r
+        global $manager;\r
+        // Defaults\r
+        $span  = false;\r
+        $link  = false;\r
+        $block = false;\r
+        $rss   = false;\r
+        if ($tb_id == 0) {\r
+            $tb_id = intRequestVar('tb_id');\r
+        }\r
+        if (requestVar('__mode') == 'rss') {\r
+            $rss = true;\r
+        }\r
+        if ($this->isEnableLinkCheck($tb_id)) {\r
+            $block = true;\r
+        }\r
+        if (!$tb_id) {\r
+            return _TB_msgTBID_IS_MISSING;\r
+        }\r
+        if ((!$manager->existsItem($tb_id,0,0)) && ($this->getOption('CheckIDs') == 'yes')) {\r
+            return _ERROR_NOSUCHITEM;\r
+        }\r
+        // 0. Check if we need to output the list as rss\r
+        if ($rss) {\r
+            $this->rssResponse($tb_id);\r
+            return;\r
+        }\r
+        // check: accept pings.\r
+        $blogId       = getBlogIDFromItemID($tb_id);\r
+        $isAcceptPing = $this->isAcceptTrackBack($tb_id);\r
+        if (!$isAcceptPing) {\r
+            return _TB_tplNO_ACCEPT;\r
+        }\r
+        // 1. Get attributes\r
+        $b         =& $manager->getBlog(intval($blogId));\r
+        $url       =  requestVar('url');\r
+        $title     =  requestVar('title');\r
+        $excerpt   =  requestVar('excerpt');\r
+        $blog_name =  requestVar('blog_name');\r
+        if ($url && preg_match('/https?:\/\/([^\/]+)/', $url, $matches) ){\r
+            if( gethostbynamel($matches[1]) === FALSE ) {\r
+                return _TB_msgBAD_SENDER_URL;\r
+            }\r
+        } else {\r
+            return _TB_msgNO_SENDER_URL;\r
+        }\r
+        // 2. Conversion of encoding...\r
+        $encoding = $this->_detect_encoding($excerpt);\r
+        if (strtoupper(_CHARSET) != 'UTF-8') {\r
+            $title     = $this->_strip_controlchar(strip_tags(mb_convert_encoding($title, _CHARSET, $encoding)));\r
+            $excerpt   = $this->_strip_controlchar(strip_tags(mb_convert_encoding($excerpt, _CHARSET, $encoding)));\r
+            $blog_name = $this->_strip_controlchar(strip_tags(mb_convert_encoding($blog_name, _CHARSET, $encoding)));\r
+        } else {\r
+            $title     = $this->_strip_controlchar($this->_convert_to_utf8($title, $encoding));\r
+            $title     = $this->_decode_entities(strip_tags($title));\r
+            $excerpt   = $this->_strip_controlchar($this->_convert_to_utf8($excerpt, $encoding));\r
+            $excerpt   = $this->_decode_entities(strip_tags($excerpt));\r
+            $blog_name = $this->_strip_controlchar($this->_convert_to_utf8($blog_name, $encoding));\r
+            $blog_name = $this->_decode_entities(strip_tags($blog_name));\r
+        }\r
+        // 3. Save data in the DB\r
+        $res = sql_query("\r
+            SELECT \r
+                `tb_id`,\r
+                `block`,\r
+                `spam`\r
+            FROM \r
+                `' . sql_table('plugin_tb') . '`\r
+            WHERE \r
+                `url`   = '" . sql_real_escape_string($url) . "' AND \r
+                `tb_id` = '" . intval($tb_id) . "'\r
+        ");\r
+        if (sql_num_rows($res) != 0) {\r
+            $rows = sql_fetch_assoc($res);\r
+            $spam = ($rows['block'] || $rows['spam'] ) ? true : false;\r
+            $res  = sql_query("\r
+                UPDATE\r
+                    `" . sql_table('plugin_tb') . "`\r
+                SET \r
+                    `title`     = '" . sql_real_escape_string($title) . "', \r
+                    `excerpt`   = '" . sql_real_escape_string($excerpt) . "', \r
+                    `blog_name` = '" . sql_real_escape_string($blog_name) . "', \r
+                    `timestamp` = '  . mysqldate($b->getCorrectTime()) . '\r
+                WHERE \r
+                    `url`       = '" . sql_real_escape_string($url) . "' AND \r
+                    `tb_id`     = '" . sql_real_escape_string(intval($tb_id)) . "'\r
+            ');\r
+            if (!$res) {\r
+                return sprintf(_TB_msgTB_COULDNOT_TB_UPDATE, sql_error());\r
+            }\r
+        } else {\r
+            // spam block\r
+            $res = sql_query('\r
+                SELECT \r
+                    `id` \r
+                FROM \r
+                    `' . sql_table('plugin_tb') . '` \r
+                WHERE \r
+                    `block` = 1 and \r
+                    `url`   = "' . sql_real_escape_string($url) . '"\r
+            ');\r
+            if (mysql_num_rows($res) != 0) {\r
+                // NP_Trackback has blocked tb !\r
+                ACTIONLOG :: add(INFO, sprintf(_TB_msgDUPLICATED_TB_BLOCKED, $tb_id, $url));\r
+                return _TB_tplNO_ACCEPT;\r
+            }\r
+            // 4. SPAM check (for SpamCheck API 2 /w compat. API 1)\r
+            $spamcheck = array (\r
+                'type'     => 'trackback',\r
+                'id'       => $tb_id,\r
+                'title'    => $title,\r
+                'excerpt'  => $excerpt,\r
+                'blogname' => $blog_name,\r
+                'url'      => $url,\r
+                'return'   => true,\r
+                'live'     => true,\r
+                /* Backwards compatibility with SpamCheck API 1*/\r
+                'data'     => $url . "\n" . $title . "\n" . $excerpt . "\n" . $blog_name . "\n" . serverVar('HTTP_USER_AGENT'),\r
+                'ipblock'  => true,\r
+            );\r
+            $manager->notify('SpamCheck', array ('spamcheck' => & $spamcheck));\r
+            if (isset($spamcheck['result']) && $spamcheck['result'] == true) {\r
+                $spam = true;\r
+            }\r
+            // 5. Content check (TO DO)\r
+            $enableLinkCheck = $this->isEnableLinkCheck($tb_id);\r
+            if ($spam == false || $enableLinkCheck == 'ignore') {\r
+                if ($enableLinkCheck) {\r
+                    $contents  = $this->retrieveUrl($url);\r
+                    $linkArray = $this->getPermaLinksFromText($contents);\r
+                    if (defined('NP_TRACKBACK_LINKCHECK_STRICT')) {\r
+                        $itemLink = $this->_createItemLink($tb_id, $b);\r
+                    } else {\r
+                        $itemLink = $b->getURL();\r
+                    }\r
+                    $itemLinkPat = '{^' . preg_quote($itemLink) .'}i';\r
+                    $itemLinkPat = str_replace('&','&(amp;)?', $itemLinkPat);\r
+                    foreach ($linkArray as $l) {\r
+                        if(preg_match($itemLinkPat, $l)) {\r
+                            ACTIONLOG :: add(INFO, sprintf(_TB_msgLINK_CHECK_OK, $l, $itemLinkPat));\r
+                            $link = true;\r
+                            break;\r
+                        }\r
+                    }\r
+                    if (!$link) {\r
+                        $cnt = @count($linkArray);\r
+                        if ($enableLinkCheck == 'ignore') {\r
+                            ACTIONLOG :: add(INFO, sprintf(_TB_msgLINK_CHECK_IGNORE, $tb_id, $url, $cnt, $itemLinkPat));\r
+                            return _TB_tplNO_ACCEPT;\r
+                        } else {\r
+                            ACTIONLOG :: add(INFO, sprintf(_TB_msgLINK_CHECK_BLOCK, $tb_id, $url, $cnt, $itemLinkPat));\r
+                        }\r
+                    }\r
+                }\r
+            }\r
+            // 6. Determine if Trackback is safe...\r
+            if ($enableLinkCheck) {\r
+                $block = ($spam == true || $link == false);\r
+            } else {\r
+                $block = $spam == true;\r
+            }\r
+            $query = '\r
+                INSERT INTO \r
+                    `' . sql_table('plugin_tb') . '` \r
+                SET\r
+                    `tb_id`     = \'' . sql_real_escape_string(intval($tb_id)) . '\',\r
+                    `block`     = \'' . ($block ? '1' : '0') . '\',\r
+                    `spam`      = \'' . ($spam ? '1'  : '0') . '\',\r
+                    `link`      = \'' . ($link ? '1'  : '0') . '\',\r
+                    `url`       = \'' . sql_real_escape_string($url) . '\',\r
+                    `title`     = \'' . sql_real_escape_string($title) . '\',\r
+                    `excerpt`   = \'' . sql_real_escape_string($excerpt) . '\',\r
+                    `blog_name` = \'' . sql_real_escape_string($blog_name) . '\',\r
+                    `timestamp` = ' . mysqldate($b->getCorrectTime()) . '\r
+            ';\r
+            $res = sql_query($query);\r
+            if (!$res) {\r
+                return _TB_msgCOULDNOT_SAVE_DOUBLE . mysql_error() . $query;\r
+            }\r
+        }\r
+        // 7. Send notification e-mail if needed\r
+        $notifyAddrs = $this->getOption('NotifyEmail');\r
+        $notifyAddrs = ($notifyAddrs ? $notifyAddrs . ';' : '') \r
+                     . $this->getBlogOption($blogId, 'NotifyEmailBlog');\r
+        if ($notifyAddrs && $spam == false) {\r
+            $vars = array (\r
+                'tb_id'    => $tb_id,\r
+                'url'      => $url,\r
+                'title'    => $title,\r
+                'excerpt'  => $excerpt,\r
+                'blogname' => $blog_name\r
+            );\r
+            $mailto_title = TEMPLATE::fill($this->notificationMailTitle, $vars);\r
+            $mailto_msg   = TEMPLATE::fill($this->notificationMail, $vars);\r
+            global $CONF, $DIR_LIBS;\r
+            // make sure notification class is loaded\r
+            if (!class_exists('notification')) {\r
+                include($DIR_LIBS . 'NOTIFICATION.php');\r
+            }\r
+            $notify = new NOTIFICATION($notifyAddrs);\r
+            $notify->notify($mailto_title, $mailto_msg , $CONF['AdminEmail']);\r
+            if ($manager->pluginInstalled('NP_Cache')) {\r
+                $p =& $manager->getPlugin('NP_Cache');\r
+                $p->setCurrentBlog($tb_id);\r
+                $p->cleanItem($tb_id);\r
+                $p->cleanArray(array('index'));\r
+            }\r
+        }\r
+        if( $block ) {\r
+            return _TB_tplNO_ACCEPT;\r
+        }\r
+            return '';\r
+    }\r
+\r
+// }}}\r
+// {{{ function xmlResponse($errorMessage = '')\r
+\r
+    /**\r
+     * Send a Trackback ping to another website\r
+     *\r
+     * @param str\r
+     */\r
+    function xmlResponse($errorMessage = '')\r
+    {\r
+        $dom      =  new DOMDocument('1.0', 'UTF-8');\r
+        $response =  $dom->appendChild($dom->createElement('response'));\r
+        if ($errorMessage) {\r
+            if (strtoupper(_CHARSET) != 'UTF-8') {\r
+                $errorMessage = mb_convert_encoding($errorMessage, 'UTF-8');\r
+                $response->appendChild($dom->createElement('error', '1'));\r
+                $response->appendChild($dom->createElement('message', htmlspecialchars($errorMessage, ENT_QUOTES)));\r
+            } elase {\r
+                $response->appendChild($dom->createElement('error', '0'));\r
+            }\r
+        }\r
+        exit;\r
+    }\r
+\r
+// }}}\r
+// {{{ function canSendPing()\r
+\r
+    /**\r
+     * Check if member may send ping (check if logged in)\r
+     *\r
+     * @return bool\r
+     */\r
+    function canSendPing()\r
+    {\r
+        global $member;\r
+        return $member->isLoggedIn() || $this->xmlrpc;\r
+    }\r
+\r
+// }}}\r
+// {{{ function redirect($tb_id, $urlHash)\r
+\r
+    /**\r
+     * Redirect to trackbacked\r
+     *\r
+     * @param int\r
+     * @param str\r
+     */\r
+    function redirect($tb_id, $urlHash)\r
+    {\r
+        $que = '\r
+            SELECT\r
+                `url` as result\r
+            FROM\r
+                `%s`\r
+            WHERE\r
+                `tb_id`    = %d AND\r
+                md5(`url`) = "%s"\r
+        ';\r
+        $url = htmlspecialchars(quickQuery(sprintf($que, $tb_id, $url_Hash)), ENT_QUOTES);\r
+        if (empty($url)) {\r
+            global $CONF;\r
+            $url = $CONF['SiteURL'];\r
+        }\r
+        $url = htmlspecialchars_decode(stripslashes($url), ENT_QUOTES);\r
+        header('Location: ' . $url);\r
+    }\r
+\r
+// }}}\r
+// {{{ function getRequiredURL($itemid)\r
+\r
+    /**\r
+     * Get required URL for link check\r
+     *\r
+     * @param int\r
+     * @return str\r
+     */\r
+    function getRequiredURL($itemid)\r
+    {\r
+        global $manager;\r
+        $blog =& $manager->getBlog(getBlogIDFromItemID(intval($itemid)));\r
+        if ($this->isEnableLinkCheck(intval($itemid))) {\r
+            return $this->_createItemLink(intval($itemid), $blog);\r
+        }\r
+        return '';\r
+    }\r
+\r
+// }}}\r
+// {{{ function isEnableLinkCheck($itemid)\r
+\r
+    /**\r
+     * Is link check Enable ?\r
+     *\r
+     * @param int\r
+     * @return bool\r
+     */\r
+    function isEnableLinkCheck($itemid)\r
+    {\r
+        switch($this->getItemOption($itemid, 'isAcceptW/OLink')) {\r
+            case 'yes':\r
+                return false;\r
+                break;\r
+            case 'no':\r
+                return true;\r
+                break;\r
+            case 'default'\r
+            default:\r
+                $blogid = getBlogIDFromItemID(intval($itemid));\r
+                $def    = $this->getBlogOption(intval($blogid), 'isAcceptW/OLinkDef');\r
+                return $def != 'yes';\r
+                break;\r
+        }\r
+    }\r
+\r
+// }}}\r
+// {{{ function isAcceptTrackBack($itemid = null)\r
+\r
+    /**\r
+     * Is TrackBack Accept ?\r
+     *\r
+     * @param int\r
+     * @return bool\r
+     */\r
+    function isAcceptTrackBack($itemid = null)\r
+    {\r
+        $ret = false;\r
+        if ($this->getOption('AcceptPing') == 'yes') {\r
+            if ($itemid) {\r
+                $bid = getBlogIDFromItemID(intval($itemid));\r
+            } else {\r
+                global $blog;\r
+                if ($blog) {\r
+                    $bid = $blog->getID();\r
+                } else {\r
+                    global $CONF;\r
+                    $bid = $CONF['DefaultBlog'];\r
+                }\r
+            }\r
+            if ($this->getBlogOption($bid, 'AllowTrackBack') == 'yes') {\r
+                if ($itemid) {\r
+                    $ret = $this->$this->getItemOption(intval($itemid), 'ItemAcceptPing') == 'yes' ? true : false;\r
+                } else {\r
+                    $ret = true;\r
+                }\r
+            } else {\r
+                $ret = false;\r
+            }\r
+        }\r
+        return $ret;\r
+    }\r
+\r
+// }}}\r
+// {{{ function pingTrackback($data)\r
+\r
+    /**\r
+     * Ping all URLs\r
+     *\r
+     * @param array\r
+     */\r
+    function pingTrackback($data)\r
+    {\r
+        global $manager, $CONF;\r
+        $ping_urls_count = 0;\r
+        $ping_urls       = array();\r
+        $ping_url        = requestVar('trackback_ping_url');\r
+        $localflag       = array();\r
+        if (trim($ping_url)) {\r
+            $ping_urlsTemp = array();\r
+            $ping_urlsTemp = preg_split("/[\s,]+/", trim($ping_url));\r
+            for ($i=0; $i<count($ping_urlsTemp); $i++) {\r
+        }\r
+        $ping_urls_count = count($ping_urls);\r
+        $tb_url_amount   = requestVar('tb_url_amount');\r
+\r
+\r
+\r
+\r
+\r
+\r
+               /**\r
+                * Ping all URLs\r
+                */\r
+               function pingTrackback($data) {\r
+                       global $manager, $CONF;\r
+                       \r
+                       $ping_urls_count = 0;\r
+                       $ping_urls = array();\r
+                       $localflag = array();\r
+                       \r
+                       $ping_url = requestVar('trackback_ping_url');\r
+//modify start+++++++++\r
+/*\r
+                       if ($ping_url) {\r
+                               $ping_urls[0] = $ping_url;\r
+                               $ping_urls_count++;\r
+                       }\r
+*/\r
+                       if (trim($ping_url)) {\r
+                               $ping_urlsTemp = array();\r
+                               $ping_urlsTemp = preg_split("/[\s,]+/", trim($ping_url));\r
+                               for($i=0;$i<count($ping_urlsTemp);$i++){\r
+                                       $ping_urls[] = trim($ping_urlsTemp[$i]);\r
+                                       $ping_urls_count++;\r
+                               }\r
+                       }\r
+//modify end+++++++++\r
+       \r
+                       $tb_url_amount = requestVar('tb_url_amount');\r
+                       for ($i=0;$i<$tb_url_amount;$i++) {\r
+                               $tb_temp_url = requestVar('tb_url_'.$i);\r
+                               if ($tb_temp_url) {\r
+                                       $ping_urls[$ping_urls_count] = $tb_temp_url;\r
+                                       $localflag[$ping_urls_count] = (requestVar('tb_url_'.$i.'_local') == 'on')? 1: 0;\r
+                                       $ping_urls_count++;\r
+                               }\r
+                       }\r
+       \r
+                       if ($ping_urls_count <= 0) {\r
+                               return;\r
+                       }\r
+       \r
+                       $itemid = $data['itemid'];\r
+                       $item = &$manager->getItem($itemid, 0, 0);\r
+                       if (!$item) return; // don't ping for draft & future\r
+                       if ($item['draft']) return;   // don't ping on draft items\r
+       \r
+                       // gather some more information, needed to send the ping (blog name, etc)      \r
+                       $blog =& $manager->getBlog(getBlogIDFromItemID($itemid));\r
+                       $blog_name      = $blog->getName();\r
+\r
+                       $title      = $data['title'] != '' ? $data['title'] : $item['title'];\r
+                       $title          = strip_tags($title);\r
+\r
+                       $excerpt    = $data['body'] != '' ? $data['body'] : $item['body'];\r
+                       $excerpt        = strip_tags($excerpt);\r
+                       $excerpt    = $this->_cut_string($excerpt, 200);\r
+       \r
+/*\r
+                       $CONF['ItemURL'] = preg_replace('/\/$/', '', $blog->getURL());   \r
+                       $url = createItemLink($itemid);\r
+*/\r
+                       $url    = $this->_createItemLink($item['itemid'],$blog);        \r
+       \r
+                       // send the ping(s) (add errors to actionlog)\r
+                       for ($i=0; $i<count($ping_urls); $i++) {\r
+                               if( ! $localflag[$i] )\r
+                                       $res = $this->sendPing($itemid, $title, $url, $excerpt, $blog_name, $ping_urls[$i]);\r
+                               else\r
+                                       $res = $this->handleLocalPing($itemid, $title, $excerpt, $blog_name, $ping_urls[$i]);\r
+                               if ($res) ACTIONLOG::add(WARNING, 'TrackBack Error:' . $res . ' (' . $ping_urls[$i] . ')');\r
+                       }\r
+               }\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+            $dom      =  new DOMDocument('1.0', 'UTF-8');\r
+            $response =  $dom->appendChild($dom->createElement('response'));\r
+            $response->appendChild($dom->createElement('error', '0'));\r
+            $rss      =  $response->appendChild($dom->createElement('rss'));\r
+            $rss->setAttribute("version", "0.91");\r
+            $channel  =  $rss->appendChild($dom->createElement('channel'));\r
+            $channel->appendChild($dom->createElement('title', htmlspecialchars($title, ENT_QUOTES)));\r
+            $channel->appendChild($dom->createElement('link', htmlspecialchars($url, ENT_QUOTES)));\r
+            $channel->appendChild($dom->createElement('description', htmlspecialchars($excerpt, ENT_QUOTES)));\r
+\r
+            $query = 'SELECT '\r
+                   .    '`url`, '\r
+                   .    '`blog_name`, '\r
+                   .    '`excerpt`, '\r
+                   .    '`title`, '\r
+                   .    'UNIX_TIMESTAMP(`timestamp`) as timestamp '\r
+                   . 'FROM '\r
+                   .    sql_table('plugin_tb') . ' '\r
+                   . 'WHERE '\r
+                   .    '`tb_id` = ' . $tb_id . ' AND '\r
+                   .    '`block` = 0 '\r
+                   . 'ORDER BY '\r
+                   .    '`timestamp` DESC';\r
+            $res   = sql_query($query);\r
+            while($data = sql_fetch_assoc($res)) {\r
+                $data['title']   = htmlspecialchars($this->_restore_to_utf8($data['title']), ENT_QUOTES);\r
+                $data['excerpt'] = htmlspecialchars($this->_restore_to_utf8($data['excerpt']), ENT_QUOTES);\r
+                $data['url']     = htmlspecialchars($data['url'], ENT_QUOTES);\r
+                $item            = $channel->appendChild($dom->createElement('item'));\r
+                $item->appendChild($dom->createElement('title', $data['title']);\r
+                $item->appendChild($dom->createElement('link', $data['url']);\r
+                $item->appendChild($dom->createElement('description', $data['excerpt']);\r
+            }\r
+            header('Content-Type: text/xml');\r
+            echo $dom->saveXML();\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/CHANGELOG.txt b/NP_TrackBack/branches/DOM-branch/trackback/CHANGELOG.txt
new file mode 100644 (file)
index 0000000..299c1d4
--- /dev/null
@@ -0,0 +1,123 @@
+       \r
+       \r
+       0.9.0   Initial version of Referer by Xiffy\r
+       0.9.1   Added the possibility to call pop, 10 to show the most populair pages\r
+                       Minor bugfixes for htmlspecialchars\r
+                       Added substr to trim the line. Internet Explorer makes long lines instead of braking them\r
+       0.9.2   Added timeoffset functionality to refWhen ...\r
+       0.9.3   Added three display options (needs reinstallation!)\r
+                       Bugfixes and another calling option; lastall\r
+                       Added "-" on each refer*\r
+       \r
+       ------------------------------------------------------------\r
+       \r
+       1.0     Initial release\r
+       1.1     Version that takes advantage of the new features in Nucleus v2.0\r
+                       (v1.55 users still need to addapt their templates)\r
+       1.2             -  Fix: Typo (cechkids)\r
+                       -  Fix: $CONF['ActionURL'] instead of $CONF['SiteURL'] . 'action.php'\r
+                       -  Fix: also works with php option 'short_open_tags' set to Off\r
+                       -  Added: manualpingform\r
+                       -  Requires Nucleus v2.0...\r
+                       -  Support for tableprefix (Nucleus versions > 2.0)\r
+       \r
+       1.3     Release by caw\r
+                       -  Removed: Table backwards compatibility code\r
+                       -  Added: Support for adding TrackBack when editing item\r
+                       -  Change: Table name changed from [nucleus_]plugin_tb to [nucleus_]plug_trackback\r
+\r
+       1.4     Release by TeRanEX \r
+                       (didn't wrote anything myself, only merged some modifications)\r
+                       -  Added: Table backwards compatibility code (was removed in 1.3 but I don't \r
+                          see any reason why)\r
+                       -  Change: Table name changed from [nucleus_]plug_trackback to [nucleus_]plugin_tb \r
+                          again (what was the reason for the change in 1.3?\r
+                       -  Added all fixes/mods/additions of thread http://forum.nucleuscms.org/viewtopic.php?t=3247\r
+                       -  Send a ping on edit item\r
+                       -  sendPing with POST instead of GET\r
+                       -  "Retrieving TrackBack Pings" Implementation \r
+                       -  "Auto-Discovery of TrackBack Ping URLs" Implementation \r
+                       -  automatically-detecting trackbackURL of permalink linked by item\r
+                       -  Change: the RDF output so that it looks the same as in the MT TrackBack Spec\r
+                          (see http://forum.nucleuscms.org/viewtopic.php?t=1974)\r
+       \r
+       1.5             Release by admun and TeRanEX\r
+                       -  Added: Trackback updates, sending the newest data\r
+                       -  Added: Autodiscovery to the bookmarklet\r
+                       -  Added: Autodiscovery to the pingform\r
+                       -  Fixed: Autodiscovery now looks also in the 'more'-part of an item\r
+                       -  Changed/fixed: autodiscovery when editing an item, now you can check a checkbox for\r
+                          every trackback that was discovered and you want to ping\r
+                       -  Added: License info\r
+                       -  Changed: The description of the plugin\r
+       \r
+       ------------------------------------------------------------\r
+       \r
+       2.0a    Release by Niels Leenheer (rakaz)\r
+                       -  Added: Caching of auto-detected trackback URLs in a database table\r
+                       -  Added: If the cURL extension is present a HEAD request is send first, to make\r
+                          sure we are dealing with a (X)HTML page and not some large binary file format.\r
+                       -  Added: The auto-detection of trackback URLs now happen in real time - as you type -\r
+                          thanks to client-side Javascript and the XmlHttpRequest object which requests\r
+                          the required data from the plugin.\r
+       \r
+       2.0b    -  Added spinning auto-detection indicator\r
+                   -  Added support for multiple character encoding methods. The plugin\r
+                          works internally fully in UTF-8 (Unicode) and can convert other\r
+                          character encodings. The output of the plugin is in UTF-8 or in US-ASCII\r
+                          with unicode characters encoded using numeric entities.\r
+                       -  Added spam protection using the Blacklist plugin (thanks to Xiffy for\r
+                          helping me out by adding a generic spam check API to his plugin).\r
+                       -  Added a check to see if the page which send the trackback actually\r
+                          contains a link to our server. If not, then it is probably a spamming\r
+                          attempt and block by default.\r
+                       -  The output of this plugin is now fully configurable. You can specify\r
+                          you're own (X)HTML code.\r
+                       -  Added a admin interface which can be used to manage trackbacks and \r
+                          manually send trackbacks to other sites. It is possible to delete \r
+                          trackbacks, but also to block and unblock trackbacks. All trackbacks\r
+                          which are marked as spam are not deleted automatically, but they end\r
+                          up in a list called 'Blocked trackbacks'. You can manually verify this\r
+                          list and unblock any trackback which is marked as spam by mistake.\r
+                       -  Fixed a number of bugs, including missing hostnames and double // in\r
+                          URLs. Fixed a bug introduced in 2.0a which prevented the title and \r
+                          excerpt from showing up when sending trackbacks from a newly created\r
+                          story. Also filtering of tags is more stringent.\r
+                          \r
+                       -  REMOVED: Manual ping forms. The form which is need to ping other\r
+                          weblogs is now integrated into the admin interface. The form needed\r
+                          for other weblog authors to manually add trackbacks to your website\r
+                          will return in the next release.\r
+                       -  REMOVED: The ability to show a list of trackbacks in a popup window.\r
+                          This will probably return in the next release.\r
+                       -  REMOVED: The ability to delete trackbacks directly from the list\r
+                          shown to administrators. This is now handled by the admin interface.\r
+\r
+       2.0     final   \r
+                       -  Made the help page Nucleus 3.2 compatible\r
+                       -  Added a manual ping form, which allows weblog authors to add a trackback\r
+                          to your stories even when their software doesn't support trackbacks.\r
+                       -  Removed <language>en</language> from the RSS output, because we can't\r
+                          be sure about the language of the contents of the RSS stream.\r
+\r
+       2.0.1   -  Security fix: Plugin admin interface was exposed to all logged in users,\r
+                          not only to users with admin rights.\r
+\r
+       2.0.2   -  Added autodetection of the encoding of trackbacks, which is needed when\r
+                          a trackback is send in a foreign encoding by a sender which does not \r
+                          support version 1.2 of the trackback specifications. Supported encodings:\r
+                          US-ASCII, ISO-2022-JP, UTF-8, EUC-JP, Shift_JIS. If the encoding is not\r
+                          specified according to version 1.2 of the specs AND it is not one of the\r
+                          encodings specified above, the plugin will assume it is encoding using\r
+                          ISO-8859-1.\r
+                       -  Added two new events SendTrackback and RetrieveTrackback which can be\r
+                          used by other plugins or the XML-RPC APIs to allow external blog editors\r
+                          to send trackbacks. (This functionality does require a modification to\r
+                          the XML-RPC APIs).\r
+                       -  Added support for more clean Trackback URLs, for example:\r
+                          http://www.rakaz.nl/nucleus/item/84.trackback  or \r
+                          http://www.rakaz.nl/nucleus/item.php?id=84&format=trackback\r
+                          \r
+       2.0.3   -  Added support for a more advanced version of the SpamCheck API\r
+                          \r
+                          
\ No newline at end of file
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/autodetect.php b/NP_TrackBack/branches/DOM-branch/trackback/autodetect.php
new file mode 100644 (file)
index 0000000..a3f107d
--- /dev/null
@@ -0,0 +1,259 @@
+<?php
+       $strRel = '../../../'; 
+       include($strRel . 'config.php');
+       
+       global $manager, $CONF;
+       $action = $manager->addTicketToUrl($CONF['ActionURL'] . '?action=plugin&name=TrackBack&type=detect')    
+?>
+       var xmlhttp = false;
+       var inProgress = false;
+       
+       var TrackbackAction = "<?php echo $action; ?>";
+       var TrackbackSource = new Array;
+       var TrackbackName   = new Array;
+       var TrackbackURL    = new Array;
+       
+       var LookupTable     = new Array;
+       var Lookup                      = '';
+       var countTotal                  = 0;
+       
+       var regexp = /href\s*=\s*([\"\'])(http:[^\"\'>]+)([\"\'])/ig;
+               
+               
+       function tbParseLinks ()
+       {
+               oinputbody = document.getElementById('inputbody');
+               oinputmore = document.getElementById('inputmore');
+               full = oinputbody.value + ' ' + oinputmore.value;
+
+               while (vArray = regexp.exec(full)) 
+               {
+                       unused = true;
+                       
+                       if (Lookup == vArray[2])
+                               unused = false;
+
+                       for (var i = 0; i < LookupTable.length; i++)
+                               if (LookupTable[i] == vArray[2])
+                                       unused = false;
+
+                       for (var i = 0; i < TrackbackSource.length; i++) 
+                               if (TrackbackSource[i] == vArray[2])
+                                       unused = false;
+                       
+                       if (unused == true)
+                               LookupTable.push(vArray[2]);
+               }
+       }
+       
+       function tbAutoDetect()
+       {
+               if (LookupTable.length > 0)
+               {
+                       tbBusy(true);
+
+                       if (!inProgress)
+                       {
+                               // We have something to do and the connection is free
+                               Lookup = LookupTable.shift();
+                               inProgress = true;
+       
+                               // The reason we use GET instead of POST is because
+                               // Opera does not properly support setting headers yet,
+                               // which is a requirement for using POST.
+                               xmlhttp.open("GET", TrackbackAction + "&tb_link=" + escape(Lookup), true);
+                               xmlhttp.onreadystatechange = tbStateChange;
+                               xmlhttp.send('');
+                       }
+                       else
+                       {
+                               // Still busy... simply wait until next turn
+                       }
+               }
+               else
+               {
+                       // Nothing to do, check back later...
+                       if (Lookup == '')
+                       {
+                               tbBusy(false);
+                       }
+               }
+       }
+
+       function tbStateChange ()
+       {
+               if (inProgress == true && xmlhttp.readyState == 4 && xmlhttp.status == 200) 
+               {
+                       eval (xmlhttp.responseText);
+                       inProgress = false;
+                       Lookup = '';
+               }
+       }
+
+       function tbBusy(toggle)
+       {
+
+               if (toggle)
+               {
+                               document.forms[0].discoverit.style.color = "#888888";
+                               document.forms[0].discoverit.style.fontWeight="bold";
+                               document.forms[0].discoverit.value = "  Loading ....";
+               }
+               else
+               {
+                               document.forms[0].discoverit.style.color = "#888888";
+                               document.forms[0].discoverit.style.fontWeight="bold";
+                               document.forms[0].discoverit.value = "  D o n e !  ";
+               }
+
+               o = document.getElementById('tb_busy');
+               
+               if (o)
+               {
+                       if (toggle)
+                               o.style.display = '';
+                       else
+                               o.style.display = 'none'
+               }
+       }
+
+       function tbDone(source, url, name)
+       {
+               TrackbackSource.push(source);
+               TrackbackURL.push(url);
+               TrackbackName.push(name);
+                       
+//             var parent = document.getElementById('tb_auto');
+               var amount = document.getElementById('tb_url_amount');
+               var subtitle = document.getElementById('tb_auto_title');
+               var listtable = document.getElementById('tb_ping_list');
+
+               if (url != '')
+               {
+//                     count = parseInt(amount.value);
+                       count = countTotal;
+
+                       mycurrent_row=document.createElement("TR");
+
+                       checkbox = document.createElement("input");
+                       checkbox.type = 'checkbox';
+                       checkbox.name = "tb_url_" + count;
+                       checkbox.id = "tb_url_" + count;
+                       checkbox.value = url;
+                       checkbox.defaultChecked = true;
+
+                       label = document.createElement("label"); 
+                       label.htmlFor = "tb_url_" + count;
+                       label.title = source;
+                       
+                       text = document.createTextNode(name);
+                       label.appendChild(text);
+                       
+                       
+//                     br = document.createElement("br"); 
+
+//                     subtitle.innerHTML = "Auto Discovered Ping URL's:";
+//                     parent.appendChild(checkbox);
+//                     parent.appendChild(label);
+
+                       mycurrent_cell=document.createElement("TD");
+                       mycurrent_cell.appendChild(checkbox);
+                       mycurrent_row.appendChild(mycurrent_cell);
+                       mycurrent_cell=document.createElement("TD");
+                       mycurrent_cell.appendChild(label);
+                       mycurrent_row.appendChild(mycurrent_cell);
+                       
+                       mycurrent_row.appendChild(mycurrent_cell);
+
+
+                       if(url.indexOf("<?php echo $CONF['IndexURL'];?>",0) != -1)
+                       {
+                               //local?
+                               checkboxL = document.createElement("input");
+                               checkboxL.type = 'checkbox';
+                               checkboxL.name = "tb_url_" + count + "_local";
+                               checkboxL.id = "tb_url_" + count + "_local";
+                               checkboxL.defaultChecked = true;
+
+                               labelL =        document.createElement("label"); 
+                               labelL.htmlFor = "tb_url_" + count + "_local";
+                               labelL.title = "local?";
+
+                               text = document.createTextNode("local?");
+                               labelL.appendChild(text);
+//                             parent.appendChild(checkboxL);
+//                             parent.appendChild(labelL);
+                               mycurrent_cell=document.createElement("TD");
+                               mycurrent_cell.appendChild(checkboxL);
+                               mycurrent_cell.appendChild(labelL);
+                               mycurrent_row.appendChild(mycurrent_cell);
+
+                       }
+                       else
+                       {
+                               mycurrent_cell=document.createElement("TD");
+                               mycurrent_row.appendChild(mycurrent_cell);
+                       }
+//                     parent.appendChild(br);
+                       listtable.appendChild(mycurrent_row);
+
+//                     amount.value = count + 1;
+                       countTotal++;
+                       amount.value = countTotal;
+               }
+               else
+               {
+                       subtitle.innerHTML = "No Trackbak URLs.";
+               }
+       }
+
+       function tbSetup() 
+       {
+               try 
+               {
+                       xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
+               } 
+               catch (e) 
+               {
+                       try 
+                       {
+                               xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
+                       } 
+                       catch (e) 
+                       {
+                               xmlhttp = false;
+                       }
+               }
+
+               if (!xmlhttp && typeof XMLHttpRequest!='undefined') 
+               {
+                       xmlhttp = new XMLHttpRequest();
+               }
+               
+               setInterval ('tbParseLinks();', 500);
+               setInterval ('tbAutoDetect();', 500);
+               
+               if (window.onloadtrackback)
+                       window.onloadtrackback();                               
+       }
+
+       function AddStart()
+       {
+               var strString = "";
+               strString = document.forms[0].trackback_ping_url.value;
+               strArray = strString.split("\n");
+                               for (var i = 0; i < strArray.length; i++)
+                               {
+                                       strTemp = trim(strArray[i]);
+                                       if (strTemp != "" && strTemp.match(/^http/))
+                                       {
+                                               tbDone(strTemp,strTemp,strTemp);
+                                       }
+                               }
+               document.forms[0].trackback_ping_url.value = '';
+       }
+
+       function trim(string) 
+       { 
+               return string.replace(/(^\s*)|(\s*$)/g,''); 
+       }
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/detectlist.php b/NP_TrackBack/branches/DOM-branch/trackback/detectlist.php
new file mode 100644 (file)
index 0000000..c3affe6
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+       $strRel = '../../../'; 
+       include($strRel . 'config.php');
+
+?>
+       var TrackbackAction = "<?php echo $CONF['ActionURL'];?>";
+       var tb_id = "<?php echo intRequestVar('tb_id')?>";
+       var tb_amount = "<?php echo intRequestVar('amount')?>";
+       var xmlhttp = false;
+
+       function resttbStart() 
+       {
+               document.getElementById("tbhidenavi").style.display = "block";
+               document.getElementById("tbshownavi").style.display = "none";
+               try 
+               {
+                       xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
+               } 
+               catch (e) 
+               {
+                       try 
+                       {
+                               xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
+                       } 
+                       catch (e) 
+                       {
+                               xmlhttp = false;
+                       }
+               }
+
+               if (!xmlhttp && typeof XMLHttpRequest!='undefined') 
+               {
+                       xmlhttp = new XMLHttpRequest();
+               }
+               
+               if (xmlhttp)
+               {
+                       loadXMLDoc();
+               }
+               
+       }
+
+       function loadXMLDoc()
+       {
+
+               var url =  TrackbackAction + '?action=plugin&name=TrackBack&type=left&tb_id=' + tb_id + '&amount=' + tb_amount;
+               
+               xmlhttp.onreadystatechange=xmlhttpChange
+               xmlhttp.open("GET",url,true)
+               xmlhttp.send('')
+       }
+
+       function xmlhttpChange()
+       {
+               if (xmlhttp.readyState == 4 && xmlhttp.status == 200) 
+               {
+                       var result = document.getElementById("resttb");
+                       result.innerHTML = xmlhttp.responseText;
+               }
+       }
+
+       function hideresttb()
+       {
+               var result = document.getElementById("resttb");
+               result.innerHTML = "";
+               
+               document.getElementById("tbhidenavi").style.display = "none";
+               document.getElementById("tbshownavi").style.display = "block";
+               
+       }
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/grid.php b/NP_TrackBack/branches/DOM-branch/trackback/grid.php
new file mode 100644 (file)
index 0000000..5b3ed15
--- /dev/null
@@ -0,0 +1,238 @@
+<?php
+
+       $strRel = '../../../'; 
+       include($strRel . 'config.php');
+       include($DIR_LIBS . 'PLUGINADMIN.php');
+       include('template.php');
+
+       // Send out Content-type
+       header('Pragma: no-cache');     
+       header("Content-Type: text/xml");
+       sendContentType('text/xml', 'admin-trackback', _CHARSET);       
+
+       $oPluginAdmin = new PluginAdmin('TrackBack');
+
+       if ( ! $member->isLoggedIn() )
+       {
+               $oPluginAdmin->start();
+               echo '<p>' . _ERROR_DISALLOWED . '</p>';
+               $oPluginAdmin->end();
+               exit;
+       }
+       
+       // Actions
+       $action = requestVar('action');
+       $aActionsNotToCheck = array(
+               '',
+       );
+       if (!in_array($action, $aActionsNotToCheck)) {
+               if (!$manager->checkTicket()) doError(_ERROR_BADTICKET);
+       }
+       
+//modify start+++++++++
+               $plug =& $oPluginAdmin->plugin;
+               $tableVersion = $plug->checkTableVersion();
+
+               // include language file for this plugin 
+               $language = ereg_replace( '[\\|/]', '', getLanguageName()); 
+               if (file_exists($plug->getDirectory().'language/'.$language.'.php')) 
+                       include_once($plug->getDirectory().'language/'.$language.'.php'); 
+               else 
+                       include_once($plug->getDirectory().'language/'.'english.php');
+//modify end+++++++++
+
+       $oTemplate = new Trackback_Template();
+       $oTemplate->set ('CONF', $CONF);
+       $oTemplate->set ('plugindirurl', $oPluginAdmin->plugin->getAdminURL());
+       $oTemplate->set ('ticket', $manager->_generateTicket());
+               
+       $whereClause = '';
+       if( ! $member->isAdmin() ){
+               // where clause
+               $res = sql_query('SELECT tblog FROM '.sql_table('team').' WHERE tadmin = 1 AND tmember = '.$member->getID() );
+               $adminBlog = array();
+               while ($row = mysql_fetch_array($res)){
+                       $adminBlog[] = $row[0];
+               }
+               if($adminBlog)
+                       $whereClause =  ' i.iblog in (' . implode(', ', $adminBlog) . ') ';
+                       
+               if( $whereClause )
+                       $whereClause = ' AND ( i.iauthor = '.$member->getID().' OR ' . $whereClause . ' )';
+               else
+                       $whereClause = ' AND i.iauthor = '.$member->getID();
+       }
+                       
+       $requiredItemEditRights = array(
+               'dodelete',
+               'doblock',
+               'dounblock',
+       );
+       $safeids = array();
+       if (in_array($action, $requiredItemEditRights)) {
+               $ids = explode(',', requestVar('ids'));
+               $safeids = array();
+               foreach( $ids as $id ){
+                       $id = trim($id);
+                       if( is_numeric($id) )
+                               $safeids[] = $id;
+               }       
+               if( ! $member->isAdmin() ){
+                       $query = 'SELECT t.id  FROM ' . sql_table('plugin_tb') . ' t, ' . sql_table('item') . ' i WHERE t.tb_id = i.inumber AND t.id in ( '. implode(',', $safeids) . ' ) '. $whereClause ;
+                       $res = sql_query($query);
+                       $safeids = array();
+                       while ($row = mysql_fetch_array($res)){
+                               $safeids[] = $row[0];
+                       }
+               }
+       }
+       
+       // Pages 
+       switch($action) {
+               
+               case 'ajax':
+                       $type = requestVar('type') == 'all' ? 'all' : 'blocked' ;
+                       $filter['all'] = ' t.block = 0 ';
+                       $filter['blocked'] = ' t.block = 1 ';
+
+                       $start  = intRequestVar('offset') ? intRequestVar('offset') : 0;
+                       $amount = intRequestVar('page_size') ? intRequestVar('page_size') : 25;
+
+                       $colname = array();
+                       $colname['date'] = 'timestamp';
+                       $colname['item'] = 'story_id';
+                       $colname['title'] = 'title';
+                       
+                       $sort_col = requestVar('sort_col');
+                       $sort_col = $colname[$sort_col];
+                       if( !$sort_col ) $sort_col = $colname['date'];
+
+                       $sort_dir = ( requestVar('sort_dir') == 'ASC' ) ? 'ASC' : 'DESC';
+
+                       $rres = sql_query ("
+                       SELECT
+                       count(*) as count
+                       FROM
+                       ".sql_table('plugin_tb')." AS t,
+                       ".sql_table('item')." AS i
+                       WHERE
+                       t.tb_id = i.inumber AND
+                       ".$filter[$type].$whereClause);
+                       $rrow = mysql_fetch_array($rres);
+                       $count = $rrow['count'];
+                       
+                       $rres = sql_query ("
+                       SELECT
+                       i.ititle AS story,
+                       i.inumber AS story_id,
+                       t.id AS id,
+                       t.title AS title,
+                       t.blog_name AS blog_name,
+                       t.excerpt AS excerpt,
+                       t.url AS url,
+                       t.spam AS spam,
+                       UNIX_TIMESTAMP(t.timestamp) AS timestamp
+                       FROM
+                       ".sql_table('plugin_tb')." AS t,
+                       ".sql_table('item')." AS i
+                       WHERE
+                       t.tb_id = i.inumber AND
+                       ".$filter[$type].$whereClause."
+                       ORDER BY
+                       ".$sort_col." ".$sort_dir." 
+                       LIMIT
+                       ".$start.",".$amount."
+                       ");
+                       
+                       $items = array();
+                       
+                       while ($rrow = mysql_fetch_array($rres))
+                       {
+                               $rrow['title']          = $oPluginAdmin->plugin->_cut_string($rrow['title'], 50);
+                               $rrow['title']          = $oPluginAdmin->plugin->_strip_controlchar($rrow['title']);
+                               $rrow['title']          = htmlspecialchars($rrow['title']);
+                               $rrow['title']          = preg_replace("/-+/","-",$rrow['title']);
+                               
+                               $rrow['blog_name']      = $oPluginAdmin->plugin->_cut_string($rrow['blog_name'], 50);
+                               $rrow['blog_name']      = $oPluginAdmin->plugin->_strip_controlchar($rrow['blog_name']);
+                               $rrow['blog_name']      = htmlspecialchars($rrow['blog_name']);
+                               $rrow['blog_name']              = preg_replace("/-+/","-",$rrow['blog_name']);
+                               
+                               $rrow['excerpt']        = $oPluginAdmin->plugin->_cut_string($rrow['excerpt'], 100);
+                               $rrow['excerpt']        = $oPluginAdmin->plugin->_strip_controlchar($rrow['excerpt']);
+                               $rrow['excerpt']        = htmlspecialchars($rrow['excerpt']);
+                               $rrow['excerpt']                = preg_replace("/-+/","-",$rrow['excerpt']);
+                               
+                               $rrow['url']            = htmlspecialchars($rrow['url'], ENT_QUOTES);
+                               
+                               $blog = & $manager->getBlog(getBlogIDFromItemID($rrow['story_id']));
+                               $rrow['story_url'] = $oPluginAdmin->plugin->_createItemLink($rrow['story_id'], $blog);
+                               $rrow['story'] = htmlspecialchars(strip_tags($rrow['story']), ENT_QUOTES);
+                               
+                               $items[] = $rrow;
+                       }
+                       
+                       $oTemplate->set ('amount', $amount);
+                       $oTemplate->set ('count', $count);
+                       $oTemplate->set ('start', $start);
+                       $oTemplate->set ('items', $items);
+                       $oTemplate->template('templates/response_'.$type.'.xml');                       
+                       break;
+                       
+               case 'dodelete':
+                       if( count($safeids) > 0 ){              
+                               $safeids = implode(',',$safeids);
+                               
+                               $res = sql_query(
+                                               ' DELETE FROM '
+                                               . sql_table('plugin_tb')
+                                               . ' WHERE id in (' . $safeids. ')'
+                               );
+                               $oTemplate->set ('message', $safeids . ' deleted.');
+                       } else {
+                               $oTemplate->set ('message', 'no rows deleted.');
+                       }
+                       
+                       $oTemplate->template('templates/response_dodelete.xml');
+                       break;
+                       
+               case 'doblock':
+                       if( count($safeids) > 0 ){              
+                               $safeids = implode(',',$safeids);
+                               
+                               $res = sql_query(
+                                               ' UPDATE '
+                                               . sql_table('plugin_tb')
+                                               .' SET block = 1 '
+                                               . ' WHERE id in (' . $safeids. ')'
+                               );
+                               $oTemplate->set ('message', $safeids . ' blocked.');
+                       } else {
+                               $oTemplate->set ('message', 'no rows blocked.');
+                       }
+                       
+                       $oTemplate->template('templates/response_doblock.xml');
+                       break;
+                                               
+               case 'dounblock':
+                       if( count($safeids) > 0 ){              
+                               $safeids = implode(',',$safeids);
+                               
+                               $res = sql_query(
+                                               ' UPDATE '
+                                               . sql_table('plugin_tb')
+                                               .' SET block = 0 '
+                                               . ' WHERE id in (' . $safeids. ')'
+                               );
+                               $oTemplate->set ('message', $safeids . ' unblocked.');
+                       } else {
+                               $oTemplate->set ('message', 'no rows unblocked.');
+                       }
+                       
+                       $oTemplate->template('templates/response_dounblock.xml');
+                       break;
+       }
+
+       // Create the admin area page
+       echo $oTemplate->fetch();
+       
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/help.html b/NP_TrackBack/branches/DOM-branch/trackback/help.html
new file mode 100644 (file)
index 0000000..41a60ae
--- /dev/null
@@ -0,0 +1,111 @@
+<h2>Help</h2>
+
+
+<h3>Skin variables</h3>
+
+<p>
+       <code>&lt;%TrackBack(list)%&gt;</code>
+</p>
+<p>
+       Show a list of trackbacks for the current page. Optionally it is also possible to
+       add an additional parameter, the story id number, which makes it possible to
+       show a list of trackbacks from a different page. The output of this variable is
+       fully configurable in the plugin options.
+</p>
+
+<p>
+       <code>&lt;%TrackBack(code)%&gt;</code>
+</p>
+<p>
+       Inserts a small piece of invisible RDF code in the page, which is used by other
+       weblogs to auto-detect the trackback URL. If you do not include this variable in
+       your skin for item pages, other weblogs will not be able to auto-detect that your
+       weblog is able to accept trackbacks.
+</p>
+
+<p>
+       <code>&lt;%TrackBack(form)%&gt;</code>
+</p>
+<p>
+       Insert the URL to a manual ping form. Using this form authors of other weblogs
+       can add trackbacks to your stories, even when their software does not support
+       trackbacks, such as Blogger. A link to this form is included in the footer of
+       the trackback list by default. If you want to place this link somewhere else 
+       on your page, simply remove the <code>&lt;%form%&gt;</code> variable from the 
+       <em>Footer</em> field in the plugin settings and add the skinvar somewhere on
+       your webpage.
+</p>
+
+<p>
+       <code>&lt;%TrackBack(url)%&gt;</code>
+</p>
+<p>
+       Insert the Trackback Ping URL. A this URL is also included in the footer of
+       the trackback list by default. If you want to place this link somewhere else 
+       on your page, simply remove the <code>&lt;%action%&gt;</code> variable from the 
+       <em>Footer</em> field in the plugin settings and add the skinvar somewhere on
+       your webpage.
+</p>
+       
+<h3>Template variables</h3>
+
+<p>
+       All the skin variables documented above are also available as template variables.
+</p>
+
+<p>
+       <code>&lt;%TrackBack(count)%&gt;</code>
+</p>
+<p>
+       If you want to include an indication of how many trackbacks are present for each
+       page you can use this variable. By default this variable will show: "No Trackbacks", 
+       "1 Trackback", "2 Trackbacks", etc. This is however also fully configurable in
+       the plugin options.
+</p>
+
+
+<h3>Changing the output</h3>
+
+<p>
+       It is possible to fully change the output of this plugin. You can manually change
+       the XHTML code in the plugin options, but you can also use CSS to style the default
+       output to your liking. Below you will find an example of what a little snippet of
+       CSS can do.
+</p>
+
+<pre>div.tb {
+       border: 1px solid #000; background: #FFF;
+}
+div.tb div.head {
+       padding: 4px;
+       background: #000; color: #FFF;
+       font-weight: bold; text-transform: lowercase; letter-spacing: 0.6em;
+}
+div.tb div.empty {
+       padding: 4px;
+       font-size: 95%;
+}
+div.tb div.item {
+       padding: 4px;
+}
+div.tb div.item div.name {
+       margin-bottom: 8px;
+       font-size: 120%; font-weight: bold;
+}
+div.tb div.item div.body {
+       font-size: 95%;
+}
+div.tb div.item div.body a {
+       font-weight: bold;
+}
+div.tb div.item div.date {
+       margin-bottom: 8px;
+       color: #888;
+       font-size: 85%; text-align: right;
+}
+div.tb div.info {
+       padding: 4px;
+       color: #FFF; background: #888;
+       font-size: 85%; font-style: italic;
+}</pre>
+
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/index.php b/NP_TrackBack/branches/DOM-branch/trackback/index.php
new file mode 100644 (file)
index 0000000..ef09755
--- /dev/null
@@ -0,0 +1,496 @@
+<?php
+
+       $strRel = '../../../'; 
+       include($strRel . 'config.php');
+       include($DIR_LIBS . 'PLUGINADMIN.php');
+       include('template.php');
+       
+       
+       $oPluginAdmin = new PluginAdmin('TrackBack');
+
+       if ( !$member->isLoggedIn() )
+       {
+               $oPluginAdmin->start();
+               echo '<p>' . _ERROR_DISALLOWED . '</p>';
+               $oPluginAdmin->end();
+               exit;
+       }
+       
+       // Actions
+       $action = requestVar('action');
+       $aActionsNotToCheck = array(
+               '',
+               'ping',
+       );
+       if (!in_array($action, $aActionsNotToCheck)) {
+               if (!$manager->checkTicket()) doError(_ERROR_BADTICKET);
+       }
+
+       $oPluginAdmin->start();
+       
+//modify start+++++++++
+               $plug =& $oPluginAdmin->plugin;
+               $tableVersion = $plug->checkTableVersion();
+
+               // include language file for this plugin 
+               $language = ereg_replace( '[\\|/]', '', getLanguageName()); 
+               if (file_exists($plug->getDirectory().'language/'.$language.'.php')) 
+                       include_once($plug->getDirectory().'language/'.$language.'.php'); 
+               else 
+                       include_once($plug->getDirectory().'language/'.'english.php');
+//modify end+++++++++
+
+       $mTemplate = new Trackback_Template();
+       $mTemplate->set ('CONF', $CONF);
+       $mTemplate->set ('plugid', $plug->getID());
+       $mTemplate->set ('plugindirurl', $oPluginAdmin->plugin->getAdminURL());
+       $mTemplate->template('templates/menu.html');
+       echo $mTemplate->fetch();
+
+       $oTemplate = new Trackback_Template();
+       $oTemplate->set ('CONF', $CONF);
+       $oTemplate->set ('plugindirurl', $oPluginAdmin->plugin->getAdminURL());
+       $oTemplate->set ('ticket', $manager->_generateTicket());
+       $ajaxEnabled = ($oPluginAdmin->plugin->getOption('ajaxEnabled') == 'yes') ? true : false;
+       $oTemplate->set ('ajaxEnabled', $ajaxEnabled);
+       
+       $whereClause = '';
+       if( ! $member->isAdmin() ){
+               // where clause
+               $res = sql_query('SELECT tblog FROM '.sql_table('team').' WHERE tadmin = 1 AND tmember = '.$member->getID() );
+               $adminBlog = array();
+               while ($row = mysql_fetch_array($res)){
+                       $adminBlog[] = $row[0];
+               }
+               if($adminBlog)
+                       $whereClause =  ' i.iblog in (' . implode(', ', $adminBlog) . ') ';
+                       
+               if( $whereClause )
+                       $whereClause = ' AND ( i.iauthor = '.$member->getID().' OR ' . $whereClause . ' )';
+               else
+                       $whereClause = ' AND i.iauthor = '.$member->getID();
+       }
+       //echo "<p>Debug: $whereClause<p>";
+       
+       $requiredAdminRights = array(
+               'tableUpgrade',
+               'blocked_clear',
+               'blocked_spamclear',
+       );
+       if (in_array($action, $requiredAdminRights)) {
+               if( ! $member->isAdmin() ){
+                       echo '<p>' . _ERROR_DISALLOWED . '</p>';
+                       echo '<p>Reason: ' . __LINE__ . '</p>';
+                       $oPluginAdmin->end();
+                       exit;
+               }
+       }
+       
+       $requiredItemEditRights = array(
+               'block',
+               'unblock',
+               'delete',
+       );
+       if (in_array($action, $requiredItemEditRights)) {
+               if( ! $member->isAdmin() ){
+                       $tb = intRequestVar('tb');
+                       $query = 'SELECT i.inumber FROM ' . sql_table('plugin_tb') . ' t, ' . sql_table('item') . ' i WHERE t.tb_id = i.inumber AND t.id = '. $tb . $whereClause ;
+                       $res = sql_query($query);
+                       if( ! @mysql_num_rows($res) ){
+                               echo '<p>' . _ERROR_DISALLOWED . '</p>';
+                               echo '<p>Reason: ' . __LINE__ . '</p>';
+                               $oPluginAdmin->end();
+                               exit;
+                       }
+               }
+       }
+
+       switch($action) {
+
+//modify start+++++++++
+               case 'tableUpgrade':
+                       sql_query("
+                               CREATE TABLE IF NOT EXISTS
+                                       ".sql_table('plugin_tb_lookup')."
+                               (
+                                       `link`      TEXT            NOT NULL, 
+                                       `url`       TEXT            NOT NULL, 
+                                       `title`     TEXT, 
+                                       
+                                       PRIMARY KEY (`link` (100))
+                               )
+                       ");
+                       echo $q = "ALTER TABLE ".sql_table('plugin_tb')."
+                                ADD `block` TINYINT( 4 ) NOT NULL AFTER `url` ,
+                                ADD `spam` TINYINT( 4 ) NOT NULL AFTER `block` ,
+                                ADD `link` TINYINT( 4 ) NOT NULL AFTER `spam` ,
+                                CHANGE `url` `url` TEXT NOT NULL,
+                                CHANGE `title` `title` TEXT NOT NULL,
+                                CHANGE `excerpt` `excerpt` TEXT NOT NULL,
+                                CHANGE `blog_name` `blog_name` TEXT NOT NULL,
+                                DROP PRIMARY KEY,
+                                ADD `id` INT( 11 ) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST ;";
+                       $res = @sql_query($q);
+                       if (!$res){
+                               echo 'Could not alter table: ' . mysql_error();
+                       }else{
+                               $tableVersion = 1;
+                               $oTemplate->template('templates/updatetablefinished.html');
+                       }
+                       @sql_query('ALTER TABLE `' . sql_table('plugin_tb') . '` ADD INDEX `tb_id_block_timestamp_idx` ( `tb_id`, `block`, `timestamp` DESC )');
+                       break;
+//modify end+++++++++
+
+               case 'block':
+                       $tb = intRequestVar('tb');
+
+                       $res = sql_query ("
+                               UPDATE
+                                       ".sql_table('plugin_tb')."
+                               SET
+                                       block = 1
+                               WHERE
+                                       id = '".$tb."'
+                       ");
+
+                       $action = requestVar('next');
+                       break;
+                       
+               case 'blocked_clear':
+                       $res = sql_query ("DELETE FROM ".sql_table('plugin_tb')." WHERE block = 1");
+                       $action = requestVar('next');
+                       break;
+                       
+               case 'blocked_spamclear':
+                       $res = sql_query ("DELETE FROM ".sql_table('plugin_tb')." WHERE block = 1 and spam = 1");
+                       $action = requestVar('next');
+                       break;
+
+               case 'unblock':
+                       $tb = intRequestVar('tb');
+
+                       $res = sql_query ("
+                               UPDATE
+                                       ".sql_table('plugin_tb')."
+                               SET
+                                       block = 0
+                               WHERE
+                                       id = '".$tb."'
+                       ");
+
+                       $action = requestVar('next');
+                       break;
+
+               case 'delete':
+                       $tb = intRequestVar('tb');
+
+                       $res = sql_query ("
+                               DELETE FROM
+                                       ".sql_table('plugin_tb')."
+                               WHERE
+                                       id = '".$tb."'
+                       ");
+
+                       $action = requestVar('next');
+                       break;
+
+               case 'sendping':
+                       $title     = requestVar('title');
+                       $url       = requestVar('url');
+                       $excerpt   = requestVar('excerpt');
+                       $blog_name = requestVar('blog_name');
+                       $ping_url  = requestVar('ping_url');            
+
+                       // No charset conversion needs to be done here, because
+                       // the charset used to receive the info is used to send
+                       // it...
+
+                       if ($ping_url) {
+                               $error = $oPluginAdmin->plugin->sendPing(0, $title, $url, $excerpt, $blog_name, $ping_url);
+                               
+                               if ($error) {
+                                       echo '<b>TrackBack Error:' . $error . '</b>';
+                               }
+                       }               
+                       
+                       $action = requestVar('next');
+                       break;
+                       
+               case 'ping':
+                       $id  = intRequestVar('id');
+                       
+                       $usePathInfo = ($CONF['URLMode'] == 'pathinfo');
+                       if ($usePathInfo)
+                       @ include($strRel . 'fancyurls.config.php');
+                       
+                       global $manager;
+                       $itemData = $manager->getItem($id, 0, 0);
+                       
+                       if(is_array($itemData)){
+                               $blog =& $manager->getBlog($itemData['blogid']);
+                               $CONF['ItemURL'] = ($usePathInfo)? preg_replace('/\/$/', '', $blog->getURL()): $blog->getURL();
+                               $itemData['url'] = createItemLink($id);
+                               $itemData['excerpt'] = shorten(strip_tags($itemData['body'].$itemData['more']), 250, '...');
+                               $itemData['blogname'] = $blog->getName();
+                       }else{
+                               $itemData = array();
+                               $itemData['url'] = $CONF['IndexURL'];
+                               $itemData['blogname'] = $CONF['SiteName'];
+                       }
+                       $oTemplate->set('item', $itemData);
+                       
+                       $oTemplate->template('templates/ping.html');
+                       break;                  
+       }
+
+       // Pages 
+       switch($action) {
+               
+               case 'help':
+                       $oTemplate->template('help.html');                      
+                       break;
+
+               case 'ping':
+                       $oTemplate->template('templates/ping.html');                    
+                       break;
+
+               case 'blocked':
+               case 'all':     
+                       $rres = sql_query ("
+                               SELECT
+                                       COUNT(*) AS count
+                               FROM
+                                       ".sql_table('plugin_tb')." AS t,
+                                       ".sql_table('item')." AS i
+                               WHERE
+                                       t.tb_id = i.inumber AND
+                                       t.block = " . (( $action == 'all') ? 0 : 1) . $whereClause );                           
+                                               
+                       if ($row = mysql_fetch_array($rres))
+                               $count = $row['count'];
+                       else
+                               $count = 0;
+                       $oTemplate->set('count', $count);
+
+                       if($ajaxEnabled){
+                               if( $action == 'all') 
+                                       $oTemplate->template('templates/all_ajax.html');
+                               else                    
+                                       $oTemplate->template('templates/blocked_ajax.html');
+                       } else {
+                               $start  = intRequestVar('start') ? intRequestVar('start') : 0;
+                               $amount = intRequestVar('amount') ? intRequestVar('amount') : 25;
+
+                               $rres = sql_query ("
+                                       SELECT
+                                       i.ititle AS story,
+                                       i.inumber AS story_id,
+                                       t.id AS id,
+                                       t.title AS title,
+                                       t.blog_name AS blog_name,
+                                       t.excerpt AS excerpt,
+                                       t.url AS url,
+                                       UNIX_TIMESTAMP(t.timestamp) AS timestamp,
+                                       t.spam AS spam,
+                                       t.link AS link
+                                       FROM
+                                       ".sql_table('plugin_tb')." AS t,
+                                       ".sql_table('item')." AS i
+                                       WHERE
+                                       t.tb_id = i.inumber AND
+                                       t.block = " . (( $action == 'all') ? 0 : 1) . $whereClause ."
+                                       ORDER BY
+                                       timestamp DESC
+                                       LIMIT
+                                       ".$start.",".$amount);                          
+                               
+                               $items = array();
+                               
+                               while ($rrow = mysql_fetch_array($rres)){
+                                       $rrow['title']          = $oPluginAdmin->plugin->_cut_string($rrow['title'], 50);
+                                       $rrow['title']          = $oPluginAdmin->plugin->_strip_controlchar($rrow['title']);
+                                       $rrow['title']          = htmlspecialchars($rrow['title']);
+                                       
+                                       $rrow['blog_name']      = $oPluginAdmin->plugin->_cut_string($rrow['blog_name'], 50);
+                                       $rrow['blog_name']      = $oPluginAdmin->plugin->_strip_controlchar($rrow['blog_name']);
+                                       $rrow['blog_name']      = htmlspecialchars($rrow['blog_name']);
+                                       
+                                       $rrow['excerpt']        = $oPluginAdmin->plugin->_cut_string($rrow['excerpt'], 800);
+                                       $rrow['excerpt']        = $oPluginAdmin->plugin->_strip_controlchar($rrow['excerpt']);
+                                       $rrow['excerpt']        = htmlspecialchars($rrow['excerpt']);
+                                       
+                                       $rrow['url']            = htmlspecialchars($rrow['url'], ENT_QUOTES);
+                                       $rrow['timestamp']              = htmlspecialchars($rrow['timestamp'], ENT_QUOTES);
+                                       
+                                       $blog = & $manager->getBlog(getBlogIDFromItemID($item['itemid']));
+                                       $rrow['story_url'] = $oPluginAdmin->plugin->_createItemLink($rrow['story_id'], $blog);
+                                       $rrow['story'] = htmlspecialchars(strip_tags($rrow['story']), ENT_QUOTES);
+                                       
+                                       $items[] = $rrow;
+                               }
+                               
+                               $oTemplate->set('amount', $amount);
+                               $oTemplate->set('start', $start);
+                               $oTemplate->set('items', $items);
+                               
+                               if( $action == 'all') 
+                                       $oTemplate->template('templates/all.html');
+                               else                    
+                                       $oTemplate->template('templates/blocked.html');
+                       }
+                       break;
+                       
+               case 'list':
+                       $id     = requestVar('id');
+                       $start  = intRequestVar('start') ? intRequestVar('start') : 0;
+                       $amount = intRequestVar('amount') ? intRequestVar('amount') : 25;
+
+                       $ires = sql_query ("
+                               SELECT
+                                       i.ititle,
+                                       i.inumber
+                               FROM
+                                       ".sql_table('item')." i 
+                               WHERE
+                                       i.inumber = '".$id."'
+                       ". $whereClause );
+                       
+                       if ($irow = mysql_fetch_array($ires))
+                       {
+                               $story['id']    = $id;
+                               $story['title'] = $irow['ititle'];
+
+                               $rres = sql_query ("
+                                       SELECT
+                                               COUNT(*) AS count
+                                       FROM
+                                               ".sql_table('plugin_tb')." AS t
+                                       WHERE
+                                               t.tb_id = '".$id."' AND
+                                               t.block = 0
+                               ");                             
+                                                       
+                               if ($row = mysql_fetch_array($rres))
+                                       $count = $row['count'];
+                               else
+                                       $count = 0;
+                                       
+                               $rres = sql_query ("
+                                       SELECT
+                                               t.id AS id,
+                                               t.title AS title,
+                                               t.blog_name AS blog_name,
+                                               t.excerpt AS excerpt,
+                                               t.url AS url,
+                                       UNIX_TIMESTAMP(t.timestamp) AS timestamp
+                                       FROM
+                                               ".sql_table('plugin_tb')." AS t
+                                       WHERE
+                                               t.tb_id = '".$id."' AND
+                                               t.block = 0
+                                       ORDER BY
+                                               timestamp DESC
+                                       LIMIT
+                                               ".$start.",".$amount."
+                               ");                             
+                               
+                               $items = array();
+       
+                               while ($rrow = mysql_fetch_array($rres))
+                               {
+                                       $rrow['title']          = $oPluginAdmin->plugin->_cut_string($rrow['title'], 50);
+                                       $rrow['title']          = $oPluginAdmin->plugin->_strip_controlchar($rrow['title']);
+                                       $rrow['title']          = htmlspecialchars($rrow['title']);
+//                                     $rrow['title']          = _CHARSET == 'UTF-8' ? $rrow['title'] : $oPluginAdmin->plugin->_utf8_to_entities($rrow['title']);
+       
+                                       $rrow['blog_name']      = $oPluginAdmin->plugin->_cut_string($rrow['blog_name'], 50);
+                                       $rrow['blog_name']      = $oPluginAdmin->plugin->_strip_controlchar($rrow['blog_name']);
+                                       $rrow['blog_name']      = htmlspecialchars($rrow['blog_name']);
+//                                     $rrow['blog_name']      = _CHARSET == 'UTF-8' ? $rrow['blog_name'] : $oPluginAdmin->plugin->_utf8_to_entities($rrow['blog_name']);
+       
+                                       $rrow['excerpt']        = $oPluginAdmin->plugin->_cut_string($rrow['excerpt'], 800);
+                                       $rrow['excerpt']        = $oPluginAdmin->plugin->_strip_controlchar($rrow['excerpt']);
+                                       $rrow['excerpt']        = htmlspecialchars($rrow['excerpt']);
+//                                     $rrow['excerpt']        = _CHARSET == 'UTF-8' ? $rrow['excerpt'] : $oPluginAdmin->plugin->_utf8_to_entities($rrow['excerpt']);
+       
+                                       $rrow['url']            = htmlspecialchars($rrow['url'], ENT_QUOTES);
+                                       $rrow['story'] = htmlspecialchars(strip_tags($rrow['story']), ENT_QUOTES);
+                                       $items[] = $rrow;
+                               }
+                               
+                               $oTemplate->set ('amount', $amount);
+                               $oTemplate->set ('count', $count);
+                               $oTemplate->set ('start', $start);
+                               $oTemplate->set ('items', $items);
+                               $oTemplate->set ('story', $story);
+                               $oTemplate->template('templates/list.html');                    
+                       }
+                       
+                       break;
+                                                       
+               
+               case 'index':
+                       $bres = sql_query ("
+                               SELECT
+                                       bnumber AS bnumber,
+                                       bname AS bname,
+                                       burl AS burl
+                               FROM
+                                       ".sql_table('blog')."
+                               ORDER BY
+                                       bname
+                       ");
+                       
+                       $blogs = array();
+                       
+                       while ($brow = mysql_fetch_array($bres))
+                       {
+                               if( !$member->isTeamMember($brow['bnumber']) ) continue;
+                               $ires = sql_query ("
+                                       SELECT
+                                               i.inumber AS inumber,
+                                           i.ititle AS ititle,
+                                           COUNT(*) AS total
+                                       FROM
+                                               ".sql_table('item')." AS i,
+                                               ".sql_table('plugin_tb')." AS t
+                                       WHERE
+                                               i.iblog = ".$brow['bnumber']." AND
+                                               t.tb_id = i.inumber AND
+                                               t.block = 0 ".$whereClause." 
+                                       GROUP BY
+                                               i.inumber
+                    ORDER BY
+                       i.inumber DESC
+                               ");                             
+
+                               $items = array();
+
+                               while ($irow = mysql_fetch_array($ires))
+                               {
+                                       $items[] = $irow;
+                               }
+
+                               $brow['items'] = $items;
+                               $blogs[] = $brow;
+                       }
+
+                       $oTemplate->set ('blogs', $blogs);
+                       $oTemplate->template('templates/index.html');
+                       break;
+
+               default:
+                       //modify start+++++++++
+                       if(!$tableVersion){
+                               $oTemplate->template('templates/updatetable.html');
+                       }
+                       //modify end+++++++++
+                       break;
+       }
+
+       // Create the admin area page
+       echo $oTemplate->fetch();
+       
+       echo '<div align="right">Powered by <a href="http://www.famfamfam.com/lab/icons/silk/">Silk icon</a></div>';
+       $oPluginAdmin->end();   
+
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.help.html b/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.help.html
new file mode 100644 (file)
index 0000000..aa0b185
--- /dev/null
@@ -0,0 +1,116 @@
+<h2>¥Ø¥ë¥×</h2>\r
+\r
+\r
+<h3>¥¹¥­¥ó¤Ø¤Îµ­½ÒÎã</h3>\r
+<p>\r
+Nucleus CMS Japan Wiki¤Î<a href="http://japan.nucleuscms.org/wiki/plugins:trackback">NP_TrackBack¤Î¥Ú¡¼¥¸</a>¤ò»²¾È¤·¤Æ¤¯¤À¤µ¤¤¡£\r
+</p>\r
+\r
+<h3>ɽ¼¨¤Î¥«¥¹¥¿¥Þ¥¤¥º</h3>\r
+<p>\r
+       ¥È¥é¥Ã¥¯¥Ð¥Ã¥¯´ØÏ¢¤Îɽ¼¨ÊýË¡¤Ï¥Æ¥ó¥×¥ì¡¼¥È¤òÊÔ½¸¤¹¤ë¤³¤È¤Ë¤è¤Ã¤Æ¥«¥¹¥¿¥Þ¥¤¥º¤Ç¤­¤Þ¤¹¤¬¡¢¿§¤ä;Çò¤Ê¤É¤Î¥Ç¥¶¥¤¥ó¤ÏCSS¦¤Ç»ØÄꤹ¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£¥Æ¥ó¥×¥ì¡¼¥ÈÆâ¤ËÆþÎϤ·¤¿¥¯¥é¥¹Ì¾¤Î¥×¥í¥Ñ¥Æ¥£¤Ïcss¤Ç»ØÄꤷ¤Æ¤¯¤À¤µ¤¤¡£¥Ç¥Õ¥©¥ë¥È¤Î¥Æ¥ó¥×¥ì¡¼¥È¤ò¾þ¤ëCSS¥×¥í¥Ñ¥Æ¥£¤ÎÎã¤ò¤¢¤²¤Æ¤ª¤­¤Þ¤¹¡£\r
+</p>\r
+\r
+<pre>div.tb {\r
+       border: 1px solid #000; background: #FFF;\r
+}\r
+div.tb div.head {\r
+       padding: 4px;\r
+       background: #000; color: #FFF;\r
+       font-weight: bold; text-transform: lowercase; letter-spacing: 0.6em;\r
+}\r
+div.tb div.empty {\r
+       padding: 4px;\r
+       font-size: 95%;\r
+}\r
+div.tb div.item {\r
+       padding: 4px;\r
+}\r
+div.tb div.item div.name {\r
+       margin-bottom: 8px;\r
+       font-size: 120%; font-weight: bold;\r
+}\r
+div.tb div.item div.body {\r
+       font-size: 95%;\r
+}\r
+div.tb div.item div.body a {\r
+       font-weight: bold;\r
+}\r
+div.tb div.item div.date {\r
+       margin-bottom: 8px;\r
+       color: #888;\r
+       font-size: 85%; text-align: right;\r
+}\r
+div.tb div.info {\r
+       padding: 4px;\r
+       color: #FFF; background: #888;\r
+       font-size: 85%; font-style: italic;\r
+}</pre>\r
+\r
+<h3>ÆüËܸìÈǹ¹¿·ÍúÎò</h3>\r
+\r
+<ul>\r
+       <li>Version 2.0.3jp13 : (2008/12/14)</li>\r
+       <li>¡¡[Fixed] Ping¥Õ¥©¡¼¥à¤Ø¤Î¥ê¥ó¥¯¤ò³«¤³¤¦¤È¤¹¤ë¤ÈInvalid or expired ticket.¤Ë¤Ê¤ëÌäÂê¤ò½¤Àµ</li>\r
+       <li>¡¡[Fixed] TrackBackÁ÷¿®»þ¤Î¥¨¥é¡¼¥Ï¥ó¥É¥ê¥ó¥°¤ò²þÎɤ·¤¿</li>\r
+       <li>¡¡[Fixed] TrackBack¤Îʸ»ú¥³¡¼¥É¤Î¸¡½Ð¤¬Àµ¤·¤¯¹Ô¤ï¤ì¤Ê¤¤¾ì¹ç¤¬¤¢¤ëÌäÂê¤ò½¤Àµ</li>\r
+       \r
+       <li>Version 2.0.3jp12 : (2008/01/12)</li>\r
+       <li>¡¡[Fixed] ¥¨¥é¡¼¥á¥Ã¥»¡¼¥¸¤Îʸ»ú¥³¡¼¥É¤¬Å¬ÀڤǤʤ«¤Ã¤¿ÌäÂê¤ò½¤Àµ</li>\r
+       <li>¡¡[Fixed] ¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤ÎÁ÷¿®Ê¸»ú¥³¡¼¥É¤òUTF-8¸ÇÄê¤Ë¤·¤¿</li>\r
+       <li>¡¡[Fixed] ¥È¥é¥Ã¥¯¥Ð¥Ã¥¯²Ä/ÉԲĤÎȽÄ꤬¤ª¤«¤·¤«¤Ã¤¿ÌäÂê¤ò½¤Àµ</li>\r
+       \r
+       <li>Version 2.0.3jp11 : (2007/09/30)</li>\r
+       <li>¡¡[Added] SuperAdmin°Ê³°¤Ç¤âTrackBack¤¬´ÉÍý¤Ç¤­¤ë¤è¤¦¤Ë¤·¤¿</li>\r
+       \r
+       <li>Version 2.0.3jp10 : (2007/06/30)</li>\r
+       <li>¡¡[Fixed] mysql_query()¤òsql_query()¤ËÊѹ¹</li>\r
+       <li>¡¡[Changed] ¼ÂÂλ²¾È¥Æ¡¼¥Ö¥ë¤Ë¤Ä¤¤¤ÆNucleusɸ½à¤â¤Î¤ò»È¤¦¤è¤¦¤Ë¤·¤¿</li>\r
+       <li>¡¡[Changed] ¥¤¥ó¥¹¥È¡¼¥ë¤Ç¤­¤ë¥Ð¡¼¥¸¥ç¥ó¤ò3.3°Ê¹ß¤·¤¿</li>\r
+       <li>¡¡[Changed] Rico¤ò2.0¤Ë¥¢¥Ã¥×¥Ç¡¼¥È¤·¤¿¤Î¤Ëȼ¤¤¡¢´ÉÍý²èÌ̤ε¡Ç½¤òÁý¶¯</li>\r
+       <li>¡¡[Added] ¥³¥á¥ó¥ÈÉôʬ¤Ç¤â¥Æ¥ó¥×¥ì¡¼¥ÈÊÑ¿ô¤¬»È¤¨¤ë¤è¤¦¤Ë¤·¤¿</li>\r
+       <li>¡¡[Fixed] ¥È¥é¥Ã¥¯¥Ð¥Ã¥¯Á÷¿®Éôʬ¤ÎÉÔ¶ñ¹ç¤ò½¤Àµ(FC2Âкö)</li>\r
+       <li>¡¡[Changed] UserAgent¤ò¥ª¥ê¥¸¥Ê¥ëÈǤˤ¢¤ï¤»¤ÆÊѹ¹</li>\r
+       <li>¡¡[Changed] TrackBack¤Î¥ì¥¹¥Ý¥ó¥¹¤Î²òÀϤËXML¥Ñ¡¼¥µ¡¼¤ò»È¤¦¤è¤¦¤Ë¤·¤¿</li>\r
+       <li>¡¡[Fixed] ¸ÀµÚ¥ê¥ó¥¯¥Á¥§¥Ã¥¯¤ÎÉÔ¶ñ¹ç¤ò½¤Àµ</li>\r
+       <li>¡¡[Added] ÊÝα¤Ë¤·¤Æ¤¤¤ëURL¤ÈƱ¤¸URL¤Î¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤ò̵»ë¤¹¤ë¤è¤¦¤Ë¤·¤¿</li>\r
+       <!-- 10.1, 10.2, 10.3 -->\r
+       <li>¡¡[Fixed] ¸ÀµÚ¥ê¥ó¥¯¥Á¥§¥Ã¥¯¤ÎÍѤÎURLÀ¸À®¥ë¡¼¥Á¥ó¤ÎÉÔ¶ñ¹ç¤ò½¤Àµ</li>\r
+       <!-- 10.4 -->\r
+       <li>¡¡[Added] spam¥È¥é¥Ã¥¯¥Ð¥Ã¥¯°ì³ç¾Ãµî»þ¤Ë³Îǧ¤¬½Ð¤ë¤è¤¦¤Ë¤·¤¿</li>\r
+       \r
+       <li>Version 2.0.3jp9 : (2007/05/04)</li>\r
+       <li>¡¡[Added] doIf()¤òÄɲÃ(Nucleus 3.3¸þ¤±)</li>\r
+       <li>¡¡[Added] URL¤¬Ìµ¸ú¤Ê¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤ò̵»ë¤¹¤ë¤è¤¦¤Ë¤·¤¿</li>\r
+       \r
+       <li>Version 2.0.3jp8 : (2007/03/18)</li>\r
+       <li>¡¡[Fixed] ´ÉÍý²èÌ̤ÇStory¤Î¥ê¥ó¥¯¤ÎÉÔ¶ñ¹ç¤ò½¤Àµ</li>\r
+       <li>¡¡[Changed] URLÃê½Ð¥ë¡¼¥Á¥ó¤ò²þÎÉ</li>\r
+       <li>¡¡[Changed] ´ÉÍý²èÌ̤ؤΥê¥ó¥¯¤ò½¤Àµ</li>\r
+       <li>¡¡[Changed] ´ÉÍý²èÌ̤Υڡ¼¥¸¥ó¥°¤òAjaxÂбþ¤Ë¤·¤¿</li>\r
+       <li>¡¡[Changed] ¥È¥é¥Ã¥¯¥Ð¥Ã¥¯ÄÌÃÎ¥¢¥É¥ì¥¹¤ò¥Ö¥í¥°¤´¤È¤ËÀßÄê¤Ç¤­¤ë¤è¤¦¤Ë¤·¤¿</li>\r
+       <li>¡¡[Fixed] php¤Îshort open¥¿¥°¤ò½¤Àµ</li>\r
+       <li>¡¡[Fixed] ´ÉÍý²èÌ̤Υȥé¥Ã¥¯¥Ð¥Ã¥¯°ìÍ÷¤ÇÆüÉÕ¤¬Àµ¾ï¤Ëɽ¼¨¤µ¤ì¤Ê¤¤ÌäÂê¤ò½¤Àµ</li>\r
+       \r
+       <li>Version 2.0.3jp7 : (2006/11/26)</li>\r
+       <li>¡¡[Changed] SpamChek¤Ë¤Ä¤¤¤ÆÈùÄ´À°</li>\r
+       <li>¡¡[Added] Ticket½èÍý¤òÄɲÃ(CSRFÂкö)</li>\r
+       <li>¡¡[Fixed] URL¤Ë&amp;¤¬Æþ¤Ã¤Æ¤¤¤ë¤È¤­¤ÎÆ°ºî¤òÊѹ¹</li>\r
+       <li>¡¡[Added] ´ÉÍý²èÌ̤˥¢¥¤¥³¥ó¤òÄɲÃ</li>\r
+       \r
+       <li>Version 2.0.3jp6 : (2006/09/30)</li>\r
+       <li>¡¡[Fixed] ¥»¥­¥å¥ê¥Æ¥£¤Î¸þ¾å</li>\r
+       \r
+       <li>Version 2.0.3jp5 : (2006/09/16)</li>\r
+       <li>¡¡[Fixed] getPermaLinksFromText()Æâ¤ÎURLÃê½Ð¥ë¡¼¥Á¥ó¤ÎÉÔ¶ñ¹ç¤ò½¤Àµ</li>\r
+       <li>¡¡[Fixed] Auto-Discovery»þ¤ËSQL¥¨¥é¡¼¤¬½Ð¤ëÉÔ¶ñ¹ç¤ò½¤Àµ</li>\r
+       <li>¡¡[Fixed] ´ÉÍý²èÌ̤ǥѡ¼¥¹¥¨¥é¡¼¤¬½Ð¤ë¾ì¹ç¤¬¤¢¤ëÉÔ¶ñ¹ç¤ò½¤Àµ</li>\r
+       <li>¡¡[Changed] SQL¤Î¥¯¥©¡¼¥È¤ò"¤«¤é'¤ËÊѹ¹</li>\r
+       <li>¡¡[Fixed] mb_emulator´Ä¶­¤Ë¤Æ¥¨¥é¡¼¤¬½Ð¤ëÌäÂê¤ò½¤Àµ</li>\r
+       <li>¡¡[Fixed] curl¤¬Í­¸ú¤Ê´Ä¶­¤Ç¥¨¥é¡¼¤¬½Ð¤ëÌäÂê¤ò½¤Àµ</li>\r
+       \r
+       <li>Version 2.0.3jp4 : (2006/07/15)</li>\r
+       <li>¡¡[Added] AutoDiscoveryURL½ÐÎÏ»þ¤ËSpamCheck¤ò¹Ô¤¦¤è¤¦¤Ë¤·¤¿</li>\r
+       <li>¡¡[Added] ¥á¥Ã¥»¡¼¥¸¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤòÆüËܸ첽</li>\r
+       <li>¡¡[Added] Âç¼êASP¤Î¸ÀµÚ¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¥Á¥§¥Ã¥¯¤ÎºÝ¤ËURL¤Î¥ê¥À¥¤¥ì¥¯¥È¤ò²ò½ü¤¹¤ë¤è¤¦¤Ë¤·¤¿</li>\r
+</ul>
\ No newline at end of file
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/all.html b/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/all.html
new file mode 100644 (file)
index 0000000..67ea054
--- /dev/null
@@ -0,0 +1,106 @@
+<?php global $manager; ?>\r
+<h2>\r
+       All trackbacks\r
+       <?php if ($count > $amount): ?>\r
+               (Page <?php echo ceil($start / $amount) + 1;?> of <?php echo ceil($count / $amount);?>)\r
+       <?php endif; ?>\r
+</h2>\r
+\r
+<?php if(count($items)): ?>\r
+<?php if ($count > $amount): ?>\r
+<table class="navigation">\r
+       <tr>\r
+               <td style='padding: 0;'>\r
+                       <?php if ($start > 0): ?>\r
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">\r
+                               <div>\r
+                                       <input type="submit" value="&lt;&lt; Previous" />       \r
+                                       <input type="hidden" name="action" value="all" />\r
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />\r
+                                       <?php $manager->addTicketHidden(); ?>\r
+                               </div>\r
+                       </form>\r
+                       <?php endif; ?>\r
+               </td>\r
+               <td style='padding: 0; text-align: right;'>     \r
+                       <?php if ($start + $amount < $count): ?>\r
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">\r
+                               <div>\r
+                                       <input type="submit" value="Next &gt; &gt;" />\r
+                                       <input type="hidden" name="action" value="all" />\r
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />\r
+                                       <?php $manager->addTicketHidden(); ?>\r
+                               </div>\r
+                       </form>\r
+                       <?php endif; ?>\r
+               </td>\r
+       </tr>\r
+</table>\r
+<?php endif; ?>\r
+\r
+<table>\r
+       <thead>\r
+               <tr>\r
+                       <th>Date</th>\r
+                       <th>Story</th>\r
+                       <th>Title, Blog and Excerpt</th>\r
+                       <th colspan="2">Actions</th>\r
+               </tr>\r
+       </thead>\r
+       <tbody>\r
+               <?php while (list(,$item) = each ($items)): ?>\r
+               <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>\r
+                       <td>\r
+                               <?php echo str_replace(' ', '&nbsp;', date("Y-m-d @ H:i",$item['timestamp']));?>\r
+                       </td>\r
+                       <td>\r
+                               <a href="<?php echo $item['story_url']; ?>"><?php echo $item['story'];?></a>\r
+                       </td>\r
+                       <td>\r
+                               <a href="<?php echo $item['url'];?>"><img alt="Visit" border="0" src="<?php echo $plugindirurl?>silk/house_go.png" /></a>\r
+                               <strong><?php echo $item['title'];?></strong> \r
+                               <em>(<?php echo $item['blog_name'];?>)</em><br />\r
+                               <?php echo $item['excerpt'];?>\r
+                       </td>\r
+                       <td>\r
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=block&tb='.$item['id'].'&next=all&start='.$start),ENT_QUOTES);?>"><img alt="Block" border="0" src="<?php echo $plugindirurl?>silk/delete.png" /></a>\r
+                       </td>\r
+                       <td>\r
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=delete&tb='.$item['id'].'&next=all&start='.$start),ENT_QUOTES);?>"><img alt="Delete" border="0" src="<?php echo $plugindirurl?>silk/cross.png" /></a>\r
+                       </td>\r
+               </tr>\r
+               <?php endwhile; ?>\r
+       </tbody>\r
+</table>\r
+\r
+<?php if ($count > $amount): ?>\r
+<table class="navigation">\r
+       <tr>\r
+               <td style='padding: 0;'>\r
+                       <?php if ($start > 0): ?>\r
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">\r
+                               <div>\r
+                                       <input type="submit" value="&lt;&lt; Previous" />       \r
+                                       <input type="hidden" name="action" value="all" />\r
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />\r
+                                       <?php $manager->addTicketHidden(); ?>\r
+                               </div>\r
+                       </form>\r
+                       <?php endif; ?>\r
+               </td>\r
+               <td style='padding: 0; text-align: right;'>     \r
+                       <?php if ($start + $amount < $count): ?>\r
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">\r
+                               <div>\r
+                                       <input type="submit" value="Next &gt; &gt;" />\r
+                                       <input type="hidden" name="action" value="all" />\r
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />\r
+                                       <?php $manager->addTicketHidden(); ?>\r
+                               </div>\r
+                       </form>\r
+                       <?php endif; ?>\r
+               </td>\r
+       </tr>\r
+</table>\r
+<?php endif; ?>\r
+<?php endif; ?>
\ No newline at end of file
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/all_ajax.html b/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/all_ajax.html
new file mode 100644 (file)
index 0000000..f75b50c
--- /dev/null
@@ -0,0 +1,130 @@
+<?php global $manager; ?>\r
+<h2>\r
+       All trackbacks\r
+</h2>\r
+\r
+<div id="message" style="color: red;"></div>\r
+\r
+<div style="width: 95%">\r
+<span id="tb_grid_bookmark"></span>\r
+\r
+<table id="tb_grid" style="border:0; margin:0;">\r
+       <colgroup>\r
+               <col style="width:25px;" />\r
+               <col style="width:40px;" />\r
+               <col style="width:70px;" />\r
+               <col style="width:150px;" />\r
+               <col style="width:200px;"/>\r
+               <col style="width:25px;" />\r
+       </colgroup>\r
+       <thead>\r
+               <tr>\r
+                       <th>&#160;</th>\r
+                       <th>id</th>\r
+                       <th>Date</th>\r
+                       <th>Story</th>\r
+                       <th>Title, Blog and Excerpt</th>\r
+                       <th>&#160;</th>\r
+               </tr>\r
+       </thead>\r
+</table>\r
+\r
+¾åµ­¤ÇÁªÂò¤·¤¿¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤ò°ì³ç¤·¤Æ½èÍý¤·¤Þ¤¹\r
+<a href="javascript:doDelete()" onclick=""><img alt="Delete" border="0" src="<?php echo $plugindirurl?>silk/cross.png" /></a>\r
+<a href="javascript:doBlock()" onclick=""><img alt="Block" border="0" src="<?php echo $plugindirurl?>silk/delete.png" /></a>\r
+</div>\r
+\r
+<!--\r
+<textarea id='tb_grid_debugmsgs' rows='5' cols='80' style='font-size:smaller;'></textarea>\r
+-->\r
+\r
+<script type="text/javascript">\r
+//<![CDATA[\r
+       Rico.loadModule('LiveGridAjax');\r
+       Rico.loadModule('LiveGridMenu');\r
+       Rico.include('translations/livegrid_ja.js');\r
+       Rico.include('ricoAjaxEngine.js');\r
+       \r
+       Rico.onLoad( function() {\r
+               var params = [\r
+                       'action=ajax',\r
+                       'type=all',\r
+                       'ticket=<?php echo $ticket ;?>'\r
+               ]; \r
+               \r
+               var cb = new Rico.TableColumn.checkbox('1','0');\r
+               var colspec = [\r
+                       {canHide:false, type:'control', control:cb, ClassName:'aligncenter'},\r
+                       {type:'raw'},\r
+                       {type:'raw'},\r
+                       ,\r
+                       ,\r
+                       ,\r
+               ];\r
+               \r
+               var opts = {\r
+                       saveColumnInfo   : {width:true, filter:false, sort:false}, \r
+                       menuEvent       : 'none',\r
+                       frozenColumns   : 2,\r
+                       canSortDefault  : false,\r
+                       canHideDefault  : true,\r
+                       allowColResize  : true,\r
+                       canFilterDefault: false,\r
+                       highlightElem   : 'none',\r
+                       columnSpecs     : colspec\r
+               };\r
+               \r
+               buffer = new Rico.Buffer.AjaxSQL('<?php echo $CONF['PluginURL'].'trackback/';?>grid.php',\r
+                               {TimeOut:10, requestParameters:params, sortParmFmt: 'displayName'}\r
+               );\r
+               orderGrid=new Rico.LiveGrid ('tb_grid', buffer, opts);\r
+               orderGrid.menu=new Rico.GridMenu({});\r
+               \r
+               // ajaxEngine\r
+               ajaxEngine = new Rico.AjaxEngine;\r
+               ajaxEngine.registerRequest('updateData', '<?php echo $CONF['PluginURL'].'trackback/';?>grid.php' );\r
+               ajaxEngine.registerAjaxElement('message');\r
+       });\r
+\r
+       function checkUpdateIds(){\r
+               var updateIds = [];\r
+               Rico.writeDebugMsg('check updated rows');\r
+               for(var i = 0; i < buffer.size; i++){\r
+                       row = buffer.rows[i];\r
+                       if( row[0].content && row[0].content == '1' ){\r
+                               updateIds.push(row[1].content);\r
+                               Rico.writeDebugMsg('id: '+row[1].content+' updated');\r
+                       }\r
+               }\r
+               return updateIds;\r
+       }\r
+       \r
+       function doBlock(){\r
+               var ids = checkUpdateIds();\r
+               if( !(ids.length && ids.length > 0) ) return ;\r
+               var params = [\r
+                       'action=doblock',\r
+                       'ticket=<?php echo $ticket ;?>',\r
+                       'ids='+ids.join(',')\r
+               ]; \r
+               ajaxEngine.sendRequest('updateData', {parameters: ajaxEngine._createQueryString(params, 0)});\r
+               orderGrid.resetContents('tb_grid');\r
+               buffer.fetch(-1);\r
+       }\r
+       \r
+       function doDelete(){\r
+               var ids = checkUpdateIds();\r
+               if( !(ids.length && ids.length > 0) ) return ;\r
+               if( !confirm('ËÜÅö¤Ëºï½ü¤·¤Þ¤¹¤«¡©') ) return ;\r
+               \r
+               var params = [\r
+                       'action=dodelete',\r
+                       'ticket=<?php echo $ticket ;?>',\r
+                       'ids='+ids.join(',')\r
+               ];\r
+               ajaxEngine.sendRequest('updateData', {parameters: ajaxEngine._createQueryString(params, 0)});\r
+               orderGrid.resetContents('tb_grid');\r
+               buffer.fetch(-1);\r
+       }\r
+//]]>\r
+</script>\r
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/blocked.html b/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/blocked.html
new file mode 100644 (file)
index 0000000..e510f86
--- /dev/null
@@ -0,0 +1,119 @@
+<?php global $manager; ?>\r
+<h2>\r
+       ¥Ö¥í¥Ã¥¯¤µ¤ì¤¿¥È¥é¥Ã¥¯¥Ð¥Ã¥¯\r
+       <?php if ($count > $amount): ?>\r
+               (Page <?php echo ceil($start / $amount) + 1;?> of <?php echo ceil($count / $amount);?>)\r
+       <?php endif; ?>\r
+</h2>\r
+\r
+<ul>\r
+       <li><a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=blocked_clear&next=blocked'),ENT_QUOTES); ?>" onClick="return confirm('¥Ö¥í¥Ã¥¯¤µ¤ì¤¿¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤ò¥¯¥ê¥¢¤·¤Æ¤â¤è¤í¤·¤¤¤Ç¤¹¤«¡©');">¥Ö¥í¥Ã¥¯¤µ¤ì¤¿¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤Î¥¯¥ê¥¢</a></li>\r
+       <li><a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=blocked_spamclear&next=blocked'),ENT_QUOTES); ?>" onClick="return confirm('spamȽÄꤵ¤ì¤¿¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤ò¥¯¥ê¥¢¤·¤Æ¤â¤è¤í¤·¤¤¤Ç¤¹¤«¡©');">spamȽÄꤵ¤ì¤¿¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤Î¥¯¥ê¥¢</a></li> \r
+</ul>\r
+\r
+<?php if(count($items)): ?>\r
+<?php if ($count > $amount): ?>\r
+<table class="navigation">\r
+       <tr>\r
+               <td style='padding: 0;'>\r
+                       <?php if ($start > 0): ?>\r
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">\r
+                               <div>\r
+                                       <input type="submit" value="&lt;&lt; Previous" />       \r
+                                       <input type="hidden" name="action" value="blocked" />\r
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />\r
+                                       <?php $manager->addTicketHidden(); ?>\r
+                               </div>\r
+                       </form>\r
+                       <?php endif; ?>\r
+               </td>\r
+               <td style='padding: 0; text-align: right;'>     \r
+                       <?php if ($start + $amount < $count): ?>\r
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">\r
+                               <div>\r
+                                       <input type="submit" value="Next &gt; &gt;" />\r
+                                       <input type="hidden" name="action" value="blocked" />\r
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />\r
+                                       <?php $manager->addTicketHidden(); ?>\r
+                               </div>\r
+                       </form>\r
+                       <?php endif; ?>\r
+               </td>\r
+       </tr>\r
+</table>\r
+<?php endif; ?>\r
+\r
+<table>\r
+       <thead>\r
+               <tr>\r
+                       <th>Date</th>\r
+                       <th>Story</th>\r
+                       <th>Title, Blog and Excerpt</th>\r
+                       <th colspan="2">Actions</th>\r
+               </tr>\r
+       </thead>\r
+       <tbody>\r
+               <?php while (list(,$item) = each ($items)): ?>\r
+               <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>\r
+                       <td>\r
+                               <?php echo str_replace(' ', '&nbsp;', date("Y-m-d @ H:i",$item['timestamp']));?>\r
+                       </td>\r
+                       <td>\r
+                               <a href="<?php echo $item['story_url']; ?>"><?php echo $item['story'];?></a>\r
+                       </td>\r
+                       <td>\r
+                               <a href="<?php echo $item['url'];?>"><img alt="Visit" border="0" src="<?php echo $plugindirurl;?>silk/house_go.png" /></a>\r
+                               <strong><?php echo $item['title'];?></strong> \r
+                               <em>(<?php echo $item['blog_name'];?>)</em>\r
+                               <?php echo $item['spam'] ? \r
+                                       '<img alt="spam" border="0" src="' . $plugindirurl . 'silk/delete.png" />' : \r
+                                       '';?>\r
+                               <?php echo $item['link'] ? \r
+                                       '' : \r
+                                       '<img alt="NOT Linked" border="0" src="' . $plugindirurl . 'silk/link_break.png" />';?>\r
+                               <br />\r
+                               <?php echo $item['excerpt'];?>\r
+                       </td>\r
+                       <td>\r
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=unblock&tb='.$item['id'].'&next=blocked&start='.$start),ENT_QUOTES);?>"><img alt="Unblock" border="0" src="<?php echo $plugindirurl;?>silk/accept.png" /></a>\r
+                       </td>\r
+                       <td>\r
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=delete&tb='.$item['id'].'&next=blocked&start='.$start),ENT_QUOTES);?>"><img alt="Delete" border="0" src="<?php echo $plugindirurl;?>silk/cross.png" /></a>\r
+                       </td>\r
+               </tr>\r
+               <?php endwhile; ?>\r
+       </tbody>\r
+</table>\r
+\r
+<?php if ($count > $amount): ?>\r
+<table class="navigation">\r
+       <tr>\r
+               <td style='padding: 0;'>\r
+                       <?php if ($start > 0): ?>\r
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">\r
+                               <div>\r
+                                       <input type="submit" value="&lt;&lt; Previous" />       \r
+                                       <input type="hidden" name="action" value="blocked" />\r
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />\r
+                                       <?php $manager->addTicketHidden(); ?>\r
+                               </div>\r
+                       </form>\r
+                       <?php endif; ?>\r
+               </td>\r
+               <td style='padding: 0; text-align: right;'>     \r
+                       <?php if ($start + $amount < $count): ?>\r
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">\r
+                               <div>\r
+                                       <input type="submit" value="Next &gt; &gt;" />\r
+                                       <input type="hidden" name="action" value="blocked" />\r
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />\r
+                                       <?php $manager->addTicketHidden(); ?>\r
+                               </div>\r
+                       </form>\r
+                       <?php endif; ?>\r
+               </td>\r
+       </tr>\r
+</table>\r
+<?php endif; ?>\r
+<?php endif; ?>\r
+\r
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/blocked_ajax.html b/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/blocked_ajax.html
new file mode 100644 (file)
index 0000000..7f5aa82
--- /dev/null
@@ -0,0 +1,134 @@
+<?php global $manager; ?>\r
+<h2>\r
+       ¥Ö¥í¥Ã¥¯¤µ¤ì¤¿¥È¥é¥Ã¥¯¥Ð¥Ã¥¯\r
+</h2>\r
+\r
+<ul>\r
+       <li><a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=blocked_clear&next=blocked'),ENT_QUOTES); ?>" onClick="return confirm('¥Ö¥í¥Ã¥¯¤µ¤ì¤¿¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤ò¥¯¥ê¥¢¤·¤Æ¤â¤è¤í¤·¤¤¤Ç¤¹¤«¡©');">¥Ö¥í¥Ã¥¯¤µ¤ì¤¿¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤Î¥¯¥ê¥¢</a></li>\r
+       <li><a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=blocked_spamclear&next=blocked'),ENT_QUOTES); ?>" onClick="return confirm('spamȽÄꤵ¤ì¤¿¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤ò¥¯¥ê¥¢¤·¤Æ¤â¤è¤í¤·¤¤¤Ç¤¹¤«¡©');">spamȽÄꤵ¤ì¤¿¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤Î¥¯¥ê¥¢</a></li> \r
+</ul>\r
+\r
+<div id="message" style="color: red;"></div>\r
+\r
+<div style="width: 95%">\r
+<span id="tb_grid_bookmark"></span>\r
+\r
+<table id="tb_grid" style="border:0; margin:0;">\r
+       <colgroup>\r
+               <col style="width:25px;" />\r
+               <col style="width:40px;" />\r
+               <col style="width:70px;" />\r
+               <col style="width:150px;" />\r
+               <col style="width:200px;"/>\r
+               <col style="width:25px;" />\r
+       </colgroup>\r
+       <thead>\r
+               <tr>\r
+                       <th>&#160;</th>\r
+                       <th>id</th>\r
+                       <th>Date</th>\r
+                       <th>Story</th>\r
+                       <th>Title, Blog and Excerpt</th>\r
+                       <th>&#160;</th>\r
+               </tr>\r
+       </thead>\r
+</table>\r
+¾åµ­¤ÇÁªÂò¤·¤¿¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤ò°ì³ç¤·¤Æ½èÍý¤·¤Þ¤¹\r
+<a href="javascript:doUnBlock()" onclick=""><img alt="Unblock" border="0" src="<?php echo $plugindirurl;?>silk/accept.png" /></a>\r
+<a href="javascript:doDelete()" onclick=""><img alt="Delete" border="0" src="<?php echo $plugindirurl?>silk/cross.png" /></a>\r
+</div>\r
+\r
+<!--\r
+<textarea id='tb_grid_debugmsgs' rows='5' cols='80' style='font-size:smaller;'></textarea>\r
+-->\r
+\r
+<script type="text/javascript">\r
+//<![CDATA[\r
+       Rico.loadModule('LiveGridAjax');\r
+       Rico.loadModule('LiveGridMenu');\r
+       Rico.include('translations/livegrid_ja.js');\r
+       Rico.include('ricoAjaxEngine.js');\r
+       \r
+       Rico.onLoad( function() {\r
+               var params = [\r
+                       'action=ajax',\r
+                       'type=blocked',\r
+                       'ticket=<?php echo $ticket ;?>'\r
+               ]; \r
+               \r
+               var cb = new Rico.TableColumn.checkbox('1','0');\r
+               var colspec = [\r
+                       {canHide:false, type:'control', control:cb, ClassName:'aligncenter'},\r
+                       {type:'raw'},\r
+                       {type:'raw'},\r
+                       ,\r
+                       ,\r
+                       ,\r
+               ];\r
+               \r
+               var opts = {\r
+                       saveColumnInfo   : {width:true, filter:false, sort:false}, \r
+                       menuEvent       : 'none',\r
+                       frozenColumns   : 2,\r
+                       canSortDefault  : false,\r
+                       canHideDefault  : true,\r
+                       allowColResize  : true,\r
+                       canFilterDefault: false,\r
+                       highlightElem   : 'none',\r
+                       columnSpecs     : colspec\r
+               };\r
+               \r
+               buffer = new Rico.Buffer.AjaxSQL('<?php echo $CONF['PluginURL'].'trackback/';?>grid.php',\r
+                               {TimeOut:10, requestParameters:params, sortParmFmt: 'displayName'}\r
+               );\r
+               orderGrid=new Rico.LiveGrid ('tb_grid', buffer, opts);\r
+               orderGrid.menu=new Rico.GridMenu({});\r
+               \r
+               // ajaxEngine\r
+               ajaxEngine = new Rico.AjaxEngine;\r
+               ajaxEngine.registerRequest('updateData', '<?php echo $CONF['PluginURL'].'trackback/';?>grid.php' );\r
+               ajaxEngine.registerAjaxElement('message');\r
+       });\r
+\r
+       function checkUpdateIds(){\r
+               var updateIds = [];\r
+               Rico.writeDebugMsg('check updated rows');\r
+               for(var i = 0; i < buffer.size; i++){\r
+                       row = buffer.rows[i];\r
+                       if( row[0].content && row[0].content == '1' ){\r
+                               updateIds.push(row[1].content);\r
+                               Rico.writeDebugMsg('id: '+row[1].content+' updated');\r
+                       }\r
+               }\r
+               return updateIds;\r
+       }\r
+       \r
+       function doUnBlock(){\r
+               var ids = checkUpdateIds();\r
+               if( !(ids.length && ids.length > 0) ) return ;\r
+               var params = [\r
+                       'action=dounblock',\r
+                       'ticket=<?php echo $ticket ;?>',\r
+                       'ids='+ids.join(',')\r
+               ]; \r
+               ajaxEngine.sendRequest('updateData', {parameters: ajaxEngine._createQueryString(params, 0)});\r
+               orderGrid.resetContents('tb_grid');\r
+               buffer.fetch(-1);\r
+       }\r
+       \r
+       function doDelete(){\r
+               var ids = checkUpdateIds();\r
+               if( !(ids.length && ids.length > 0) ) return ;\r
+               if( !confirm('ËÜÅö¤Ëºï½ü¤·¤Þ¤¹¤«¡©') ) return ;\r
+               \r
+               var params = [\r
+                       'action=dodelete',\r
+                       'ticket=<?php echo $ticket ;?>',\r
+                       'ids='+ids.join(',')\r
+               ];\r
+               ajaxEngine.sendRequest('updateData', {parameters: ajaxEngine._createQueryString(params, 0)});\r
+               orderGrid.resetContents('tb_grid');\r
+               buffer.fetch(-1);\r
+       }\r
+//]]>\r
+</script>\r
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/form.html b/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/form.html
new file mode 100644 (file)
index 0000000..24ce702
--- /dev/null
@@ -0,0 +1,56 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\r
+<html>\r
+       <head>\r
+               <title>TrackBack¤Î¼êÆ°Á÷¿®</title>\r
+               <link rel="stylesheet" type="text/css" href="<?php echo $CONF['AdminURL']?>styles/bookmarklet.css" />\r
+       </head>\r
+       \r
+       <body>\r
+               <h1>TrackBack¤Î¼êÆ°Á÷¿®</h1>\r
+<?php if ($success): ?>\r
+               <p>\r
+                       <strong>¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤ÎÁ÷¿®¤¬´°Î»¤·¤Þ¤·¤¿</strong>\r
+               </p>\r
+<?php endif; ?>\r
+<?php if ($error): ?>\r
+               <p>\r
+                       <strong><?php echo $status; ?></strong>\r
+               </p>\r
+<?php endif; ?>\r
+<?php if ($form): ?>           \r
+               <form method="post" action="<?php echo $CONF['ActionURL'] ?>">\r
+               \r
+               <div>\r
+                       <input type="hidden" name="tb_id" value="<?php echo $itemid;?>" />\r
+                       <input type="hidden" name="action" value="plugin" />\r
+                       <input type="hidden" name="name" value="TrackBack" />\r
+                       <input type="hidden" name="type" value="ping" />\r
+                       \r
+                       <table>\r
+                               <tr>\r
+                                       <td>¤¢¤Ê¤¿¤Îµ­»ö¤Îurl</td>\r
+                                       <td><input type="text" value="" name="url" size="60" /></td>\r
+                               </tr>\r
+                               <tr>\r
+                                       <td>µ­»ö¤Î¥¿¥¤¥È¥ë</td>\r
+                                       <td><input type="text" value="" name="title" size="60" /></td>\r
+                               </tr>\r
+                               <tr>\r
+                                       <td>µ­»ö¤ÎÍ×Ìóʸ</td>\r
+                                       <td><textarea name="excerpt" cols="40" rows="5"></textarea></td>\r
+                               </tr>\r
+                               <tr>\r
+                                       <td>¤¢¤Ê¤¿¤Îblog¤Î̾Á°</td>\r
+                                       <td><input type="text" value="" name="blog_name" size="60" /></td>\r
+                               </tr>\r
+                               <tr>\r
+                                       <td>¥È¥é¥Ã¥¯¥Ð¥Ã¥¯Á÷¿®</td>\r
+                                       <td><input type="submit" value="¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤òÁ÷¿®¤¹¤ë" /></td>\r
+                               </tr>\r
+                       </table>\r
+               </div>\r
+               \r
+               </form>\r
+<?php endif; ?>\r
+       </body>\r
+</html>
\ No newline at end of file
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/index.html b/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/index.html
new file mode 100644 (file)
index 0000000..a2a00ed
--- /dev/null
@@ -0,0 +1,34 @@
+<?php global $manager; ?>\r
+<h2>Overview of all items</h2>\r
+\r
+<?php if(count($blogs)): ?>\r
+\r
+<table>\r
+<?php while (list(,$blog) = each ($blogs)): ?>\r
+<?php if(count($blog['items'])): ?>\r
+       <thead>\r
+               <tr>\r
+                       <th>Blog: <?php echo htmlspecialchars($blog['bname']);?></th>\r
+                       <th>Total</th>\r
+                       <th>Action</th>\r
+               </tr>\r
+       </thead>\r
+       <tbody>\r
+               <?php while (list(,$item) = each ($blog['items'])): ?>\r
+               <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>\r
+                       <td>\r
+                               <?php echo $item['ititle'];?>\r
+                       </td>\r
+                       <td>\r
+                               <?php echo htmlspecialchars($item['total']);?>\r
+                       </td>\r
+                       <td>\r
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=list&id='.$item['inumber']),ENT_QUOTES);?>">Trackbacks</a>\r
+                       </td>\r
+               </tr>\r
+               <?php endwhile; ?>\r
+       </tbody>\r
+<?php endif; ?>\r
+<?php endwhile; ?>\r
+</table>\r
+<?php endif; ?>
\ No newline at end of file
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/list.html b/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/list.html
new file mode 100644 (file)
index 0000000..05eeeb8
--- /dev/null
@@ -0,0 +1,107 @@
+<?php global $manager; ?>\r
+<h2>\r
+       All trackbacks for &quot;<?php echo $story['title'];?>&quot;\r
+       <?php if ($count > $amount): ?>\r
+               (Page <?php echo ceil($start / $amount) + 1;?> of <?php echo ceil($count / $amount);?>)\r
+       <?php endif; ?>\r
+</h2>\r
+\r
+<?php if(count($items)): ?>\r
+<?php if ($count > $amount): ?>\r
+<table class="navigation">\r
+       <tr>\r
+               <td style='padding: 0;'>\r
+                       <?php if ($start > 0): ?>\r
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">\r
+                               <div>\r
+                                       <input type="submit" value="&lt;&lt; Previous" />       \r
+                                       <input type="hidden" name="action" value="list" />\r
+                                       <input type="hidden" name="id" value="<?php echo $story['id'];?>" />\r
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />\r
+                                       <?php $manager->addTicketHidden(); ?>\r
+                               </div>\r
+                       </form>\r
+                       <?php endif; ?>\r
+               </td>\r
+               <td style='padding: 0; text-align: right;'>     \r
+                       <?php if ($start + $amount < $count): ?>\r
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">\r
+                               <div>\r
+                                       <input type="submit" value="Next &gt; &gt;" />\r
+                                       <input type="hidden" name="action" value="list" />\r
+                                       <input type="hidden" name="id" value="<?php echo $story['id'];?>" />\r
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />\r
+                                       <?php $manager->addTicketHidden(); ?>\r
+                               </div>\r
+                       </form>\r
+                       <?php endif; ?>\r
+               </td>\r
+       </tr>\r
+</table>\r
+<?php endif; ?>\r
+\r
+<table>\r
+       <thead>\r
+               <tr>\r
+                       <th>Date</th>\r
+                       <th>Title, Blog and Excerpt</th>\r
+                       <th colspan="2">Actions</th>\r
+               </tr>\r
+       </thead>\r
+       <tbody>\r
+               <?php while (list(,$item) = each ($items)): ?>\r
+               <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>\r
+                       <td>\r
+                               <?php echo str_replace(' ', '&nbsp;', date("Y-m-d @ H:i",$item['timestamp']));?>\r
+                       </td>\r
+                       <td>\r
+                               <a href="<?php echo $item['url'];?>"><img alt="Visit" border="0" src="<?php echo $plugindirurl?>silk/house_go.png" /></a>\r
+                               <strong><?php echo $item['title'];?></strong> \r
+                               <em>(<?php echo $item['blog_name'];?>)</em><br />\r
+                               <?php echo $item['excerpt'];?>\r
+                       </td>\r
+                       <td>\r
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=block&tb='.$item['id'].'&next=list&id='.$story['id'].'&start='.$start),ENT_QUOTES);?>"><img alt="Block" border="0" src="<?php echo $plugindirurl?>silk/delete.png" /></a>\r
+                       </td>\r
+                       <td>\r
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=delete&tb='.$item['id'].'&next=list&id='.$story['id'].'&start='.$start),ENT_QUOTES);?>"><img alt="Delete" border="0" src="<?php echo $plugindirurl?>silk/cross.png" /></a>\r
+                       </td>\r
+               </tr>\r
+               <?php endwhile; ?>\r
+       </tbody>\r
+</table>\r
+\r
+<?php if ($count > $amount): ?>\r
+<table class="navigation">\r
+       <tr>\r
+               <td style='padding: 0;'>\r
+                       <?php if ($start > 0): ?>\r
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">\r
+                               <div>\r
+                                       <input type="submit" value="&lt;&lt; Previous" />       \r
+                                       <input type="hidden" name="action" value="list" />\r
+                                       <input type="hidden" name="id" value="<?php echo $story['id'];?>" />\r
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />\r
+                                       <?php $manager->addTicketHidden(); ?>\r
+                               </div>\r
+                       </form>\r
+                       <?php endif; ?>\r
+               </td>\r
+               <td style='padding: 0; text-align: right;'>     \r
+                       <?php if ($start + $amount < $count): ?>\r
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">\r
+                               <div>\r
+                                       <input type="submit" value="Next &gt; &gt;" />\r
+                                       <input type="hidden" name="action" value="list" />\r
+                                       <input type="hidden" name="id" value="<?php echo $story['id'];?>" />\r
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />\r
+                                       <?php $manager->addTicketHidden(); ?>\r
+                               </div>\r
+                       </form>\r
+                       <?php endif; ?>\r
+               </td>\r
+       </tr>\r
+</table>\r
+<?php endif; ?>\r
+<?php endif; ?>\r
+\r
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/menu.html b/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/menu.html
new file mode 100644 (file)
index 0000000..e98b6af
--- /dev/null
@@ -0,0 +1,32 @@
+<?php global $manager?>\r
+<h2>Trackback</h2>\r
+\r
+<script type="text/javascript" src="<?php echo $CONF['PluginURL']?>trackback/js/prototype.js"></script>\r
+<script type="text/javascript" src="<?php echo $CONF['PluginURL']?>trackback/js/rico/rico.js"></script>\r
+\r
+<ul>\r
+       <li>\r
+               <img border="0" src="<?php echo $plugindirurl?>silk/application_view_list.png" />\r
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=index'),ENT_QUOTES);?>">Overview of all items</a>\r
+       </li>\r
+       <li>\r
+               <img border="0" src="<?php echo $plugindirurl?>silk/tick.png" />\r
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=all'),ENT_QUOTES);?>">¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤ÎÁ´¥Ç¡¼¥¿</a>\r
+       </li>\r
+       <li>\r
+               <img border="0" src="<?php echo $plugindirurl?>silk/exclamation.png" />\r
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=blocked'),ENT_QUOTES);?>">¥Ö¥í¥Ã¥¯¤µ¤ì¤¿¥È¥é¥Ã¥¯¥Ð¥Ã¥¯</a>\r
+       </li> \r
+       <li>\r
+               <img border="0" src="<?php echo $plugindirurl?>silk/transmit_go.png" />\r
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=ping'),ENT_QUOTES);?>">¼êÆ°ping</a>\r
+       </li>\r
+       <li>\r
+               <img border="0" src="<?php echo $plugindirurl?>silk/help.png" />\r
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=help'),ENT_QUOTES);?>">¥Ø¥ë¥×</a>\r
+       </li>\r
+    <li>\r
+               <img border="0" src="<?php echo $plugindirurl?>silk/plugin_edit.png" />\r
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['AdminURL'].'index.php?action=pluginoptions&plugid='.$plugid),ENT_QUOTES);?>">¥×¥é¥°¥¤¥ó¥ª¥×¥·¥ç¥óÀßÄê</a>\r
+       </li>\r
+</ul>\r
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/ping.html b/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/ping.html
new file mode 100644 (file)
index 0000000..0d993ec
--- /dev/null
@@ -0,0 +1,50 @@
+<?php global $manager; ?>\r
+<h2>¼êÆ°ping¥Õ¥©¡¼¥à</h2>\r
+\r
+<form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">\r
+\r
+    <input type="hidden" name="action" value="sendping" />\r
+    <input type="hidden" name="next" value="ping" />\r
+    <?php $manager->addTicketHidden(); ?>\r
+       \r
+    <table>\r
+        <tr>\r
+            <th colspan='2'>¼êÆ°ping</th>\r
+        </tr>\r
+        <tr>\r
+            <td>¤¢¤Ê¤¿¤Îurl</td>\r
+            <td>\r
+                <input type="text" name="url" size="60" value="<?php echo htmlspecialchars($item['url']);?>" />\r
+            </td>\r
+        </tr>\r
+        <tr>\r
+            <td>µ­»ö¤Î¥¿¥¤¥È¥ë</td>\r
+            <td>\r
+                <input type="text" name="title" size="60" value="<?php echo htmlspecialchars($item['title']);?>" />\r
+            </td>\r
+        </tr>\r
+        <tr>\r
+            <td>µ­»ö¤ÎÍ×Ìóʸ</td>\r
+            <td>\r
+                    <textarea name="excerpt" cols="40" rows="5"><?php echo $item['excerpt'];?></textarea>\r
+            </td>\r
+        </tr>\r
+        <tr>\r
+            <td>Blog̾</td>\r
+            <td>\r
+                <input type="text" name="blog_name" size="60" value="<?php echo htmlspecialchars($item['blogname']);?>" />\r
+            </td>\r
+        </tr>\r
+        <tr>\r
+            <td>pingÀèurl</td>\r
+            <td>\r
+                <input type="text" value="" name="ping_url" size="60" />\r
+            </td>\r
+        </tr>\r
+        <tr>\r
+            <td>Á÷¿®</td>\r
+            <td><input type="submit" value="Á÷¿®" /></td>\r
+        </tr>\r
+    </table>\r
+\r
+</form>
\ No newline at end of file
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/response_all.xml b/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/response_all.xml
new file mode 100644 (file)
index 0000000..4ae2445
--- /dev/null
@@ -0,0 +1,35 @@
+<?php echo '<'.'?xml version="1.0" encoding="UTF-8"?'.'>';?>\r
+<?php global $manager; ?>\r
+\r
+<ajax-response>\r
+       <response type="object" id="tb_grid_updater">\r
+               <rowcount><?php echo $count; ?></rowcount>\r
+               <rows update_ui="true" offset="<?php echo $start; ?>" >\r
+                       <?php while (list(,$item) = each ($items)): ?>\r
+                       <tr>\r
+                               <td>0</td>\r
+                               <td><?php echo $item['id'];?></td>\r
+                               <td>\r
+                                       <?php echo date("Y-m-d H:i:s",$item['timestamp']);?>\r
+                               </td>\r
+                               <td>\r
+                                       <![CDATA[\r
+                                       <a href="<?php echo $item['story_url']; ?>"><?php echo $item['story'];?></a>\r
+                                       ]]>\r
+                               </td>\r
+                               <td>\r
+                                       <![CDATA[\r
+                                       <a href="<?php echo $item['url'];?>">\r
+                                               <img alt="Visit" border="0" src="<?php echo $plugindirurl?>silk/house_go.png" />\r
+                                       </a>\r
+                                       <strong><?php echo $item['title'];?></strong>\r
+                                       <?php echo $item['excerpt'];?>\r
+                                       <em>(<?php echo $item['blog_name'];?>)</em>\r
+                                       ]]>\r
+                               </td>\r
+                               <td></td>\r
+                       </tr>\r
+                       <?php endwhile; ?>\r
+               </rows> \r
+       </response>\r
+</ajax-response>\r
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/response_blocked.xml b/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/response_blocked.xml
new file mode 100644 (file)
index 0000000..29faac9
--- /dev/null
@@ -0,0 +1,41 @@
+<?php echo '<'.'?xml version="1.0" encoding="UTF-8"?'.'>';?>\r
+<?php global $manager; ?>\r
+\r
+<ajax-response>\r
+       <response type="object" id='tb_grid_updater'>\r
+               <rowcount><?php echo $count; ?></rowcount>\r
+               <rows update_ui='true' >\r
+                       <?php while (list(,$item) = each ($items)): ?>\r
+                       <tr>\r
+                               <td>0</td>\r
+                               <td><?php echo $item['id'];?></td>\r
+                               <td>\r
+                                       <?php echo date("Y-m-d H:i:s",$item['timestamp']);?>\r
+                               </td>\r
+                               <td>\r
+                                       <![CDATA[\r
+                                       <a href="<?php echo $item['story_url']; ?>"><?php echo $item['story'];?></a>\r
+                                       ]]>\r
+                               </td>\r
+                               <td>\r
+                                       <![CDATA[\r
+                                       <a href="<?php echo $item['url'];?>">\r
+                                               <img alt="Visit" border="0" src="<?php echo $plugindirurl?>silk/house_go.png" />\r
+                                       </a>\r
+                                       <strong><?php echo $item['title'];?></strong>\r
+                                       <?php echo $item['spam'] ? \r
+                                               '<img alt="spam" border="0" src="' . $plugindirurl . 'silk/delete.png" />' : \r
+                                               '';?>\r
+                                       <?php echo $item['link'] ? \r
+                                               '' : \r
+                                               '<img alt="NOT Linked" border="0" src="' . $plugindirurl . 'silk/link_break.png" />';?>\r
+                                       <?php echo $item['excerpt'];?>\r
+                                       <em>(<?php echo $item['blog_name'];?>)</em>\r
+                                       ]]>\r
+                               </td>\r
+                               <td></td>\r
+                       </tr>\r
+                       <?php endwhile; ?>\r
+               </rows> \r
+       </response>\r
+</ajax-response>\r
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/response_doblock.xml b/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/response_doblock.xml
new file mode 100644 (file)
index 0000000..fdceb49
--- /dev/null
@@ -0,0 +1,8 @@
+<?echo '<'.'?xml version="1.0" encoding="UTF-8"?'.'>';?>\r
+<?php global $manager; ?>\r
+\r
+<ajax-response>\r
+   <response type="element" id="message">\r
+               <?php echo $message; ?>\r
+   </response>\r
+</ajax-response>
\ No newline at end of file
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/response_dodelete.xml b/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/response_dodelete.xml
new file mode 100644 (file)
index 0000000..fdceb49
--- /dev/null
@@ -0,0 +1,8 @@
+<?echo '<'.'?xml version="1.0" encoding="UTF-8"?'.'>';?>\r
+<?php global $manager; ?>\r
+\r
+<ajax-response>\r
+   <response type="element" id="message">\r
+               <?php echo $message; ?>\r
+   </response>\r
+</ajax-response>
\ No newline at end of file
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/response_dounblock.xml b/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/response_dounblock.xml
new file mode 100644 (file)
index 0000000..fdceb49
--- /dev/null
@@ -0,0 +1,8 @@
+<?echo '<'.'?xml version="1.0" encoding="UTF-8"?'.'>';?>\r
+<?php global $manager; ?>\r
+\r
+<ajax-response>\r
+   <response type="element" id="message">\r
+               <?php echo $message; ?>\r
+   </response>\r
+</ajax-response>
\ No newline at end of file
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/updatetable.html b/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/updatetable.html
new file mode 100644 (file)
index 0000000..7435a03
--- /dev/null
@@ -0,0 +1,13 @@
+<?php global $manager; ?>\r
+<blockquote style="color: red;border:1px solid red;padding:1em;"><b>¥¢¥Ã¥×¥Ç¡¼¥È¤¬É¬ÍפǤ¹:</b><br />\r
+¤³¤Î¥Ð¡¼¥¸¥ç¥ó¤Ç±¿ÍѤ¹¤ë¤¿¤á¤Ë¤ÏDBÆâ¤Î¥Æ¡¼¥Ö¥ë¤Î¥¢¥Ã¥×¥Ç¡¼¥È¤¬É¬ÍפǤ¹¡£<br />\r
+º£¤Þ¤Ç¤Î¥Ç¡¼¥¿¤¬ºï½ü¤µ¤ì¤ë¤³¤È¤Ï¤¢¤ê¤Þ¤»¤ó¡£\r
+²¼¤Î¥¢¥Ã¥×¥Ç¡¼¥È¥Ü¥¿¥ó¤ò²¡¤·¤Æ¤¯¤À¤µ¤¤¡£\r
+\r
+                       <form method="post"><div>\r
+                               <input type="hidden" name="action" value="tableUpgrade" />\r
+                               <input type="submit" tabindex="10" value="upgrade table" />\r
+                               <?php $manager->addTicketHidden(); ?>\r
+                       </div></form>\r
+</blockquote>\r
+       \r
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/updatetablefinished.html b/NP_TrackBack/branches/DOM-branch/trackback/japanese-euc.templates/updatetablefinished.html
new file mode 100644 (file)
index 0000000..a16df97
--- /dev/null
@@ -0,0 +1,5 @@
+<?php global $manager; ?>\r
+<blockquote style="color: red;border:1px solid red;padding:1em;">\r
+¥Æ¡¼¥Ö¥ë¤Î¥¢¥Ã¥×¥Ç¡¼¥È¤Ï´°Î»¤·¤Þ¤·¤¿¡£\r
+</blockquote>\r
+       \r
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.help.html b/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.help.html
new file mode 100644 (file)
index 0000000..752f956
--- /dev/null
@@ -0,0 +1,116 @@
+<h2>ヘルプ</h2>
+
+
+<h3>スキンへの記述例</h3>
+<p>
+Nucleus CMS Japan Wikiの<a href="http://japan.nucleuscms.org/wiki/plugins:trackback">NP_TrackBackのページ</a>を参照してください。
+</p>
+
+<h3>表示のカスタマイズ</h3>
+<p>
+       トラックバック関連の表示方法はテンプレートを編集することによってカスタマイズできますが、色や余白などのデザインはCSS側で指定する必要があります。テンプレート内に入力したクラス名のプロパティはcssで指定してください。デフォルトのテンプレートを飾るCSSプロパティの例をあげておきます。
+</p>
+
+<pre>div.tb {
+       border: 1px solid #000; background: #FFF;
+}
+div.tb div.head {
+       padding: 4px;
+       background: #000; color: #FFF;
+       font-weight: bold; text-transform: lowercase; letter-spacing: 0.6em;
+}
+div.tb div.empty {
+       padding: 4px;
+       font-size: 95%;
+}
+div.tb div.item {
+       padding: 4px;
+}
+div.tb div.item div.name {
+       margin-bottom: 8px;
+       font-size: 120%; font-weight: bold;
+}
+div.tb div.item div.body {
+       font-size: 95%;
+}
+div.tb div.item div.body a {
+       font-weight: bold;
+}
+div.tb div.item div.date {
+       margin-bottom: 8px;
+       color: #888;
+       font-size: 85%; text-align: right;
+}
+div.tb div.info {
+       padding: 4px;
+       color: #FFF; background: #888;
+       font-size: 85%; font-style: italic;
+}</pre>
+
+<h3>日本語版更新履歴</h3>
+
+<ul>
+       <li>Version 2.0.3jp13 : (2008/12/14)</li>
+       <li> [Fixed] Pingフォームへのリンクを開こうとするとInvalid or expired ticket.になる問題を修正</li>
+       <li> [Fixed] TrackBack送信時のエラーハンドリングを改良した</li>
+       <li> [Fixed] TrackBackの文字コードの検出が正しく行われない場合がある問題を修正</li>
+       
+       <li>Version 2.0.3jp12 : (2008/01/12)</li>
+       <li> [Fixed] エラーメッセージの文字コードが適切でなかった問題を修正</li>
+       <li> [Fixed] トラックバックの送信文字コードをUTF-8固定にした</li>
+       <li> [Fixed] トラックバック可/不可の判定がおかしかった問題を修正</li>
+       
+       <li>Version 2.0.3jp11 : (2007/09/30)</li>
+       <li> [Added] SuperAdmin以外でもTrackBackが管理できるようにした</li>
+       
+       <li>Version 2.0.3jp10 : (2007/06/30)</li>
+       <li> [Fixed] mysql_query()をsql_query()に変更</li>
+       <li> [Changed] 実体参照テーブルについてNucleus標準ものを使うようにした</li>
+       <li> [Changed] インストールできるバージョンを3.3以降した</li>
+       <li> [Changed] Ricoを2.0にアップデートしたのに伴い、管理画面の機能を増強</li>
+       <li> [Added] コメント部分でもテンプレート変数が使えるようにした</li>
+       <li> [Fixed] トラックバック送信部分の不具合を修正(FC2対策)</li>
+       <li> [Changed] UserAgentをオリジナル版にあわせて変更</li>
+       <li> [Changed] TrackBackのレスポンスの解析にXMLパーサーを使うようにした</li>
+       <li> [Fixed] 言及リンクチェックの不具合を修正</li>
+       <li> [Added] 保留にしているURLと同じURLのトラックバックを無視するようにした</li>
+       <!-- 10.1, 10.2, 10.3 -->
+       <li> [Fixed] 言及リンクチェックの用のURL生成ルーチンの不具合を修正</li>
+       <!-- 10.4 -->
+       <li> [Added] spamトラックバック一括消去時に確認が出るようにした</li>
+       
+       <li>Version 2.0.3jp9 : (2007/05/04)</li>
+       <li> [Added] doIf()を追加(Nucleus 3.3向け)</li>
+       <li> [Added] URLが無効なトラックバックを無視するようにした</li>
+       
+       <li>Version 2.0.3jp8 : (2007/03/18)</li>
+       <li> [Fixed] 管理画面でStoryのリンクの不具合を修正</li>
+       <li> [Changed] URL抽出ルーチンを改良</li>
+       <li> [Changed] 管理画面へのリンクを修正</li>
+       <li> [Changed] 管理画面のページングをAjax対応にした</li>
+       <li> [Changed] トラックバック通知アドレスをブログごとに設定できるようにした</li>
+       <li> [Fixed] phpのshort openタグを修正</li>
+       <li> [Fixed] 管理画面のトラックバック一覧で日付が正常に表示されない問題を修正</li>
+       
+       <li>Version 2.0.3jp7 : (2006/11/26)</li>
+       <li> [Changed] SpamChekについて微調整</li>
+       <li> [Added] Ticket処理を追加(CSRF対策)</li>
+       <li> [Fixed] URLに&amp;が入っているときの動作を変更</li>
+       <li> [Added] 管理画面にアイコンを追加</li>
+       
+       <li>Version 2.0.3jp6 : (2006/09/30)</li>
+       <li> [Fixed] セキュリティの向上</li>
+       
+       <li>Version 2.0.3jp5 : (2006/09/16)</li>
+       <li> [Fixed] getPermaLinksFromText()内のURL抽出ルーチンの不具合を修正</li>
+       <li> [Fixed] Auto-Discovery時にSQLエラーが出る不具合を修正</li>
+       <li> [Fixed] 管理画面でパースエラーが出る場合がある不具合を修正</li>
+       <li> [Changed] SQLのクォートを"から'に変更</li>
+       <li> [Fixed] mb_emulator環境にてエラーが出る問題を修正</li>
+       <li> [Fixed] curlが有効な環境でエラーが出る問題を修正</li>
+       
+       <li>Version 2.0.3jp4 : (2006/07/15)</li>
+       <li> [Added] AutoDiscoveryURL出力時にSpamCheckを行うようにした</li>
+       <li> [Added] メッセージ、デフォルト値を日本語化</li>
+       <li> [Added] 大手ASPの言及トラックバックチェックの際にURLのリダイレクトを解除するようにした</li>
+</ul>
\ No newline at end of file
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/all.html b/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/all.html
new file mode 100644 (file)
index 0000000..56c75b1
--- /dev/null
@@ -0,0 +1,106 @@
+<?php global $manager; ?>
+<h2>
+       All trackbacks
+       <?php if ($count > $amount): ?>
+               (Page <?php echo ceil($start / $amount) + 1;?> of <?php echo ceil($count / $amount);?>)
+       <?php endif; ?>
+</h2>
+
+<?php if(count($items)): ?>
+<?php if ($count > $amount): ?>
+<table class="navigation">
+       <tr>
+               <td style='padding: 0;'>
+                       <?php if ($start > 0): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="&lt;&lt; Previous" />       
+                                       <input type="hidden" name="action" value="all" />
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+               <td style='padding: 0; text-align: right;'>     
+                       <?php if ($start + $amount < $count): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="Next &gt; &gt;" />
+                                       <input type="hidden" name="action" value="all" />
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+       </tr>
+</table>
+<?php endif; ?>
+
+<table>
+       <thead>
+               <tr>
+                       <th>Date</th>
+                       <th>Story</th>
+                       <th>Title, Blog and Excerpt</th>
+                       <th colspan="2">Actions</th>
+               </tr>
+       </thead>
+       <tbody>
+               <?php while (list(,$item) = each ($items)): ?>
+               <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>
+                       <td>
+                               <?php echo str_replace(' ', '&nbsp;', date("Y-m-d @ H:i",$item['timestamp']));?>
+                       </td>
+                       <td>
+                               <a href="<?php echo $item['story_url']; ?>"><?php echo $item['story'];?></a>
+                       </td>
+                       <td>
+                               <a href="<?php echo $item['url'];?>"><img alt="Visit" border="0" src="<?php echo $plugindirurl?>silk/house_go.png" /></a>
+                               <strong><?php echo $item['title'];?></strong> 
+                               <em>(<?php echo $item['blog_name'];?>)</em><br />
+                               <?php echo $item['excerpt'];?>
+                       </td>
+                       <td>
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=block&tb='.$item['id'].'&next=all&start='.$start),ENT_QUOTES);?>"><img alt="Block" border="0" src="<?php echo $plugindirurl?>silk/delete.png" /></a>
+                       </td>
+                       <td>
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=delete&tb='.$item['id'].'&next=all&start='.$start),ENT_QUOTES);?>"><img alt="Delete" border="0" src="<?php echo $plugindirurl?>silk/cross.png" /></a>
+                       </td>
+               </tr>
+               <?php endwhile; ?>
+       </tbody>
+</table>
+
+<?php if ($count > $amount): ?>
+<table class="navigation">
+       <tr>
+               <td style='padding: 0;'>
+                       <?php if ($start > 0): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="&lt;&lt; Previous" />       
+                                       <input type="hidden" name="action" value="all" />
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+               <td style='padding: 0; text-align: right;'>     
+                       <?php if ($start + $amount < $count): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="Next &gt; &gt;" />
+                                       <input type="hidden" name="action" value="all" />
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+       </tr>
+</table>
+<?php endif; ?>
+<?php endif; ?>
\ No newline at end of file
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/all_ajax.html b/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/all_ajax.html
new file mode 100644 (file)
index 0000000..38788e4
--- /dev/null
@@ -0,0 +1,130 @@
+<?php global $manager; ?>
+<h2>
+       All trackbacks
+</h2>
+
+<div id="message" style="color: red;"></div>
+
+<div style="width: 95%">
+<span id="tb_grid_bookmark"></span>
+
+<table id="tb_grid" style="border:0; margin:0;">
+       <colgroup>
+               <col style="width:25px;" />
+               <col style="width:40px;" />
+               <col style="width:70px;" />
+               <col style="width:150px;" />
+               <col style="width:200px;"/>
+               <col style="width:25px;" />
+       </colgroup>
+       <thead>
+               <tr>
+                       <th>&#160;</th>
+                       <th>id</th>
+                       <th>Date</th>
+                       <th>Story</th>
+                       <th>Title, Blog and Excerpt</th>
+                       <th>&#160;</th>
+               </tr>
+       </thead>
+</table>
+
+上記で選択したトラックバックを一括して処理します
+<a href="javascript:doDelete()" onclick=""><img alt="Delete" border="0" src="<?php echo $plugindirurl?>silk/cross.png" /></a>
+<a href="javascript:doBlock()" onclick=""><img alt="Block" border="0" src="<?php echo $plugindirurl?>silk/delete.png" /></a>
+</div>
+
+<!--
+<textarea id='tb_grid_debugmsgs' rows='5' cols='80' style='font-size:smaller;'></textarea>
+-->
+
+<script type="text/javascript">
+//<![CDATA[
+       Rico.loadModule('LiveGridAjax');
+       Rico.loadModule('LiveGridMenu');
+       Rico.include('translations/livegrid_ja.js');
+       Rico.include('ricoAjaxEngine.js');
+       
+       Rico.onLoad( function() {
+               var params = [
+                       'action=ajax',
+                       'type=all',
+                       'ticket=<?php echo $ticket ;?>'
+               ]; 
+               
+               var cb = new Rico.TableColumn.checkbox('1','0');
+               var colspec = [
+                       {canHide:false, type:'control', control:cb, ClassName:'aligncenter'},
+                       {type:'raw'},
+                       {type:'raw'},
+                       ,
+                       ,
+                       ,
+               ];
+               
+               var opts = {
+                       saveColumnInfo   : {width:true, filter:false, sort:false}, 
+                       menuEvent       : 'none',
+                       frozenColumns   : 2,
+                       canSortDefault  : false,
+                       canHideDefault  : true,
+                       allowColResize  : true,
+                       canFilterDefault: false,
+                       highlightElem   : 'none',
+                       columnSpecs     : colspec
+               };
+               
+               buffer = new Rico.Buffer.AjaxSQL('<?php echo $CONF['PluginURL'].'trackback/';?>grid.php',
+                               {TimeOut:10, requestParameters:params, sortParmFmt: 'displayName'}
+               );
+               orderGrid=new Rico.LiveGrid ('tb_grid', buffer, opts);
+               orderGrid.menu=new Rico.GridMenu({});
+               
+               // ajaxEngine
+               ajaxEngine = new Rico.AjaxEngine;
+               ajaxEngine.registerRequest('updateData', '<?php echo $CONF['PluginURL'].'trackback/';?>grid.php' );
+               ajaxEngine.registerAjaxElement('message');
+       });
+
+       function checkUpdateIds(){
+               var updateIds = [];
+               Rico.writeDebugMsg('check updated rows');
+               for(var i = 0; i < buffer.size; i++){
+                       row = buffer.rows[i];
+                       if( row[0].content && row[0].content == '1' ){
+                               updateIds.push(row[1].content);
+                               Rico.writeDebugMsg('id: '+row[1].content+' updated');
+                       }
+               }
+               return updateIds;
+       }
+       
+       function doBlock(){
+               var ids = checkUpdateIds();
+               if( !(ids.length && ids.length > 0) ) return ;
+               var params = [
+                       'action=doblock',
+                       'ticket=<?php echo $ticket ;?>',
+                       'ids='+ids.join(',')
+               ]; 
+               ajaxEngine.sendRequest('updateData', {parameters: ajaxEngine._createQueryString(params, 0)});
+               orderGrid.resetContents('tb_grid');
+               buffer.fetch(-1);
+       }
+       
+       function doDelete(){
+               var ids = checkUpdateIds();
+               if( !(ids.length && ids.length > 0) ) return ;
+               if( !confirm('本当に削除しますか?') ) return ;
+               
+               var params = [
+                       'action=dodelete',
+                       'ticket=<?php echo $ticket ;?>',
+                       'ids='+ids.join(',')
+               ];
+               ajaxEngine.sendRequest('updateData', {parameters: ajaxEngine._createQueryString(params, 0)});
+               orderGrid.resetContents('tb_grid');
+               buffer.fetch(-1);
+       }
+//]]>
+</script>
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/blocked.html b/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/blocked.html
new file mode 100644 (file)
index 0000000..b6d553b
--- /dev/null
@@ -0,0 +1,119 @@
+<?php global $manager; ?>
+<h2>
+       ブロックされたトラックバック
+       <?php if ($count > $amount): ?>
+               (Page <?php echo ceil($start / $amount) + 1;?> of <?php echo ceil($count / $amount);?>)
+       <?php endif; ?>
+</h2>
+
+<ul>
+       <li><a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=blocked_clear&next=blocked'),ENT_QUOTES); ?>" onClick="return confirm('ブロックされたトラックバックをクリアしてもよろしいですか?');">ブロックされたトラックバックのクリア</a></li>
+       <li><a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=blocked_spamclear&next=blocked'),ENT_QUOTES); ?>" onClick="return confirm('spam判定されたトラックバックをクリアしてもよろしいですか?');">spam判定されたトラックバックのクリア</a></li> 
+</ul>
+
+<?php if(count($items)): ?>
+<?php if ($count > $amount): ?>
+<table class="navigation">
+       <tr>
+               <td style='padding: 0;'>
+                       <?php if ($start > 0): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="&lt;&lt; Previous" />       
+                                       <input type="hidden" name="action" value="blocked" />
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+               <td style='padding: 0; text-align: right;'>     
+                       <?php if ($start + $amount < $count): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="Next &gt; &gt;" />
+                                       <input type="hidden" name="action" value="blocked" />
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+       </tr>
+</table>
+<?php endif; ?>
+
+<table>
+       <thead>
+               <tr>
+                       <th>Date</th>
+                       <th>Story</th>
+                       <th>Title, Blog and Excerpt</th>
+                       <th colspan="2">Actions</th>
+               </tr>
+       </thead>
+       <tbody>
+               <?php while (list(,$item) = each ($items)): ?>
+               <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>
+                       <td>
+                               <?php echo str_replace(' ', '&nbsp;', date("Y-m-d @ H:i",$item['timestamp']));?>
+                       </td>
+                       <td>
+                               <a href="<?php echo $item['story_url']; ?>"><?php echo $item['story'];?></a>
+                       </td>
+                       <td>
+                               <a href="<?php echo $item['url'];?>"><img alt="Visit" border="0" src="<?php echo $plugindirurl;?>silk/house_go.png" /></a>
+                               <strong><?php echo $item['title'];?></strong> 
+                               <em>(<?php echo $item['blog_name'];?>)</em>
+                               <?php echo $item['spam'] ? 
+                                       '<img alt="spam" border="0" src="' . $plugindirurl . 'silk/delete.png" />' : 
+                                       '';?>
+                               <?php echo $item['link'] ? 
+                                       '' : 
+                                       '<img alt="NOT Linked" border="0" src="' . $plugindirurl . 'silk/link_break.png" />';?>
+                               <br />
+                               <?php echo $item['excerpt'];?>
+                       </td>
+                       <td>
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=unblock&tb='.$item['id'].'&next=blocked&start='.$start),ENT_QUOTES);?>"><img alt="Unblock" border="0" src="<?php echo $plugindirurl;?>silk/accept.png" /></a>
+                       </td>
+                       <td>
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=delete&tb='.$item['id'].'&next=blocked&start='.$start),ENT_QUOTES);?>"><img alt="Delete" border="0" src="<?php echo $plugindirurl;?>silk/cross.png" /></a>
+                       </td>
+               </tr>
+               <?php endwhile; ?>
+       </tbody>
+</table>
+
+<?php if ($count > $amount): ?>
+<table class="navigation">
+       <tr>
+               <td style='padding: 0;'>
+                       <?php if ($start > 0): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="&lt;&lt; Previous" />       
+                                       <input type="hidden" name="action" value="blocked" />
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+               <td style='padding: 0; text-align: right;'>     
+                       <?php if ($start + $amount < $count): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="Next &gt; &gt;" />
+                                       <input type="hidden" name="action" value="blocked" />
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+       </tr>
+</table>
+<?php endif; ?>
+<?php endif; ?>
+
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/blocked_ajax.html b/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/blocked_ajax.html
new file mode 100644 (file)
index 0000000..7474160
--- /dev/null
@@ -0,0 +1,134 @@
+<?php global $manager; ?>
+<h2>
+       ブロックされたトラックバック
+</h2>
+
+<ul>
+       <li><a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=blocked_clear&next=blocked'),ENT_QUOTES); ?>" onClick="return confirm('ブロックされたトラックバックをクリアしてもよろしいですか?');">ブロックされたトラックバックのクリア</a></li>
+       <li><a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=blocked_spamclear&next=blocked'),ENT_QUOTES); ?>" onClick="return confirm('spam判定されたトラックバックをクリアしてもよろしいですか?');">spam判定されたトラックバックのクリア</a></li> 
+</ul>
+
+<div id="message" style="color: red;"></div>
+
+<div style="width: 95%">
+<span id="tb_grid_bookmark"></span>
+
+<table id="tb_grid" style="border:0; margin:0;">
+       <colgroup>
+               <col style="width:25px;" />
+               <col style="width:40px;" />
+               <col style="width:70px;" />
+               <col style="width:150px;" />
+               <col style="width:200px;"/>
+               <col style="width:25px;" />
+       </colgroup>
+       <thead>
+               <tr>
+                       <th>&#160;</th>
+                       <th>id</th>
+                       <th>Date</th>
+                       <th>Story</th>
+                       <th>Title, Blog and Excerpt</th>
+                       <th>&#160;</th>
+               </tr>
+       </thead>
+</table>
+上記で選択したトラックバックを一括して処理します
+<a href="javascript:doUnBlock()" onclick=""><img alt="Unblock" border="0" src="<?php echo $plugindirurl;?>silk/accept.png" /></a>
+<a href="javascript:doDelete()" onclick=""><img alt="Delete" border="0" src="<?php echo $plugindirurl?>silk/cross.png" /></a>
+</div>
+
+<!--
+<textarea id='tb_grid_debugmsgs' rows='5' cols='80' style='font-size:smaller;'></textarea>
+-->
+
+<script type="text/javascript">
+//<![CDATA[
+       Rico.loadModule('LiveGridAjax');
+       Rico.loadModule('LiveGridMenu');
+       Rico.include('translations/livegrid_ja.js');
+       Rico.include('ricoAjaxEngine.js');
+       
+       Rico.onLoad( function() {
+               var params = [
+                       'action=ajax',
+                       'type=blocked',
+                       'ticket=<?php echo $ticket ;?>'
+               ]; 
+               
+               var cb = new Rico.TableColumn.checkbox('1','0');
+               var colspec = [
+                       {canHide:false, type:'control', control:cb, ClassName:'aligncenter'},
+                       {type:'raw'},
+                       {type:'raw'},
+                       ,
+                       ,
+                       ,
+               ];
+               
+               var opts = {
+                       saveColumnInfo   : {width:true, filter:false, sort:false}, 
+                       menuEvent       : 'none',
+                       frozenColumns   : 2,
+                       canSortDefault  : false,
+                       canHideDefault  : true,
+                       allowColResize  : true,
+                       canFilterDefault: false,
+                       highlightElem   : 'none',
+                       columnSpecs     : colspec
+               };
+               
+               buffer = new Rico.Buffer.AjaxSQL('<?php echo $CONF['PluginURL'].'trackback/';?>grid.php',
+                               {TimeOut:10, requestParameters:params, sortParmFmt: 'displayName'}
+               );
+               orderGrid=new Rico.LiveGrid ('tb_grid', buffer, opts);
+               orderGrid.menu=new Rico.GridMenu({});
+               
+               // ajaxEngine
+               ajaxEngine = new Rico.AjaxEngine;
+               ajaxEngine.registerRequest('updateData', '<?php echo $CONF['PluginURL'].'trackback/';?>grid.php' );
+               ajaxEngine.registerAjaxElement('message');
+       });
+
+       function checkUpdateIds(){
+               var updateIds = [];
+               Rico.writeDebugMsg('check updated rows');
+               for(var i = 0; i < buffer.size; i++){
+                       row = buffer.rows[i];
+                       if( row[0].content && row[0].content == '1' ){
+                               updateIds.push(row[1].content);
+                               Rico.writeDebugMsg('id: '+row[1].content+' updated');
+                       }
+               }
+               return updateIds;
+       }
+       
+       function doUnBlock(){
+               var ids = checkUpdateIds();
+               if( !(ids.length && ids.length > 0) ) return ;
+               var params = [
+                       'action=dounblock',
+                       'ticket=<?php echo $ticket ;?>',
+                       'ids='+ids.join(',')
+               ]; 
+               ajaxEngine.sendRequest('updateData', {parameters: ajaxEngine._createQueryString(params, 0)});
+               orderGrid.resetContents('tb_grid');
+               buffer.fetch(-1);
+       }
+       
+       function doDelete(){
+               var ids = checkUpdateIds();
+               if( !(ids.length && ids.length > 0) ) return ;
+               if( !confirm('本当に削除しますか?') ) return ;
+               
+               var params = [
+                       'action=dodelete',
+                       'ticket=<?php echo $ticket ;?>',
+                       'ids='+ids.join(',')
+               ];
+               ajaxEngine.sendRequest('updateData', {parameters: ajaxEngine._createQueryString(params, 0)});
+               orderGrid.resetContents('tb_grid');
+               buffer.fetch(-1);
+       }
+//]]>
+</script>
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/form.html b/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/form.html
new file mode 100644 (file)
index 0000000..add813a
--- /dev/null
@@ -0,0 +1,56 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+       <head>
+               <title>TrackBackの手動送信</title>
+               <link rel="stylesheet" type="text/css" href="<?php echo $CONF['AdminURL']?>styles/bookmarklet.css" />
+       </head>
+       
+       <body>
+               <h1>TrackBackの手動送信</h1>
+<?php if ($success): ?>
+               <p>
+                       <strong>トラックバックの送信が完了しました</strong>
+               </p>
+<?php endif; ?>
+<?php if ($error): ?>
+               <p>
+                       <strong><?php echo $status; ?></strong>
+               </p>
+<?php endif; ?>
+<?php if ($form): ?>           
+               <form method="post" action="<?php echo $CONF['ActionURL'] ?>">
+               
+               <div>
+                       <input type="hidden" name="tb_id" value="<?php echo $itemid;?>" />
+                       <input type="hidden" name="action" value="plugin" />
+                       <input type="hidden" name="name" value="TrackBack" />
+                       <input type="hidden" name="type" value="ping" />
+                       
+                       <table>
+                               <tr>
+                                       <td>あなたの記事のurl</td>
+                                       <td><input type="text" value="" name="url" size="60" /></td>
+                               </tr>
+                               <tr>
+                                       <td>記事のタイトル</td>
+                                       <td><input type="text" value="" name="title" size="60" /></td>
+                               </tr>
+                               <tr>
+                                       <td>記事の要約文</td>
+                                       <td><textarea name="excerpt" cols="40" rows="5"></textarea></td>
+                               </tr>
+                               <tr>
+                                       <td>あなたのblogの名前</td>
+                                       <td><input type="text" value="" name="blog_name" size="60" /></td>
+                               </tr>
+                               <tr>
+                                       <td>トラックバック送信</td>
+                                       <td><input type="submit" value="トラックバックを送信する" /></td>
+                               </tr>
+                       </table>
+               </div>
+               
+               </form>
+<?php endif; ?>
+       </body>
+</html>
\ No newline at end of file
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/index.html b/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/index.html
new file mode 100644 (file)
index 0000000..15a2ce4
--- /dev/null
@@ -0,0 +1,34 @@
+<?php global $manager; ?>
+<h2>Overview of all items</h2>
+
+<?php if(count($blogs)): ?>
+
+<table>
+<?php while (list(,$blog) = each ($blogs)): ?>
+<?php if(count($blog['items'])): ?>
+       <thead>
+               <tr>
+                       <th>Blog: <?php echo htmlspecialchars($blog['bname']);?></th>
+                       <th>Total</th>
+                       <th>Action</th>
+               </tr>
+       </thead>
+       <tbody>
+               <?php while (list(,$item) = each ($blog['items'])): ?>
+               <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>
+                       <td>
+                               <?php echo $item['ititle'];?>
+                       </td>
+                       <td>
+                               <?php echo htmlspecialchars($item['total']);?>
+                       </td>
+                       <td>
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=list&id='.$item['inumber']),ENT_QUOTES);?>">Trackbacks</a>
+                       </td>
+               </tr>
+               <?php endwhile; ?>
+       </tbody>
+<?php endif; ?>
+<?php endwhile; ?>
+</table>
+<?php endif; ?>
\ No newline at end of file
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/list.html b/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/list.html
new file mode 100644 (file)
index 0000000..0fcf3c3
--- /dev/null
@@ -0,0 +1,107 @@
+<?php global $manager; ?>
+<h2>
+       All trackbacks for &quot;<?php echo $story['title'];?>&quot;
+       <?php if ($count > $amount): ?>
+               (Page <?php echo ceil($start / $amount) + 1;?> of <?php echo ceil($count / $amount);?>)
+       <?php endif; ?>
+</h2>
+
+<?php if(count($items)): ?>
+<?php if ($count > $amount): ?>
+<table class="navigation">
+       <tr>
+               <td style='padding: 0;'>
+                       <?php if ($start > 0): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="&lt;&lt; Previous" />       
+                                       <input type="hidden" name="action" value="list" />
+                                       <input type="hidden" name="id" value="<?php echo $story['id'];?>" />
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+               <td style='padding: 0; text-align: right;'>     
+                       <?php if ($start + $amount < $count): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="Next &gt; &gt;" />
+                                       <input type="hidden" name="action" value="list" />
+                                       <input type="hidden" name="id" value="<?php echo $story['id'];?>" />
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+       </tr>
+</table>
+<?php endif; ?>
+
+<table>
+       <thead>
+               <tr>
+                       <th>Date</th>
+                       <th>Title, Blog and Excerpt</th>
+                       <th colspan="2">Actions</th>
+               </tr>
+       </thead>
+       <tbody>
+               <?php while (list(,$item) = each ($items)): ?>
+               <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>
+                       <td>
+                               <?php echo str_replace(' ', '&nbsp;', date("Y-m-d @ H:i",$item['timestamp']));?>
+                       </td>
+                       <td>
+                               <a href="<?php echo $item['url'];?>"><img alt="Visit" border="0" src="<?php echo $plugindirurl?>silk/house_go.png" /></a>
+                               <strong><?php echo $item['title'];?></strong> 
+                               <em>(<?php echo $item['blog_name'];?>)</em><br />
+                               <?php echo $item['excerpt'];?>
+                       </td>
+                       <td>
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=block&tb='.$item['id'].'&next=list&id='.$story['id'].'&start='.$start),ENT_QUOTES);?>"><img alt="Block" border="0" src="<?php echo $plugindirurl?>silk/delete.png" /></a>
+                       </td>
+                       <td>
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=delete&tb='.$item['id'].'&next=list&id='.$story['id'].'&start='.$start),ENT_QUOTES);?>"><img alt="Delete" border="0" src="<?php echo $plugindirurl?>silk/cross.png" /></a>
+                       </td>
+               </tr>
+               <?php endwhile; ?>
+       </tbody>
+</table>
+
+<?php if ($count > $amount): ?>
+<table class="navigation">
+       <tr>
+               <td style='padding: 0;'>
+                       <?php if ($start > 0): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="&lt;&lt; Previous" />       
+                                       <input type="hidden" name="action" value="list" />
+                                       <input type="hidden" name="id" value="<?php echo $story['id'];?>" />
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+               <td style='padding: 0; text-align: right;'>     
+                       <?php if ($start + $amount < $count): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="Next &gt; &gt;" />
+                                       <input type="hidden" name="action" value="list" />
+                                       <input type="hidden" name="id" value="<?php echo $story['id'];?>" />
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+       </tr>
+</table>
+<?php endif; ?>
+<?php endif; ?>
+
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/menu.html b/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/menu.html
new file mode 100644 (file)
index 0000000..ac37024
--- /dev/null
@@ -0,0 +1,32 @@
+<?php global $manager?>
+<h2>Trackback</h2>
+
+<script type="text/javascript" src="<?php echo $CONF['PluginURL']?>trackback/js/prototype.js"></script>
+<script type="text/javascript" src="<?php echo $CONF['PluginURL']?>trackback/js/rico/rico.js"></script>
+
+<ul>
+       <li>
+               <img border="0" src="<?php echo $plugindirurl?>silk/application_view_list.png" />
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=index'),ENT_QUOTES);?>">Overview of all items</a>
+       </li>
+       <li>
+               <img border="0" src="<?php echo $plugindirurl?>silk/tick.png" />
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=all'),ENT_QUOTES);?>">トラックバックの全データ</a>
+       </li>
+       <li>
+               <img border="0" src="<?php echo $plugindirurl?>silk/exclamation.png" />
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=blocked'),ENT_QUOTES);?>">ブロックされたトラックバック</a>
+       </li> 
+       <li>
+               <img border="0" src="<?php echo $plugindirurl?>silk/transmit_go.png" />
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=ping'),ENT_QUOTES);?>">手動ping</a>
+       </li>
+       <li>
+               <img border="0" src="<?php echo $plugindirurl?>silk/help.png" />
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=help'),ENT_QUOTES);?>">ヘルプ</a>
+       </li>
+    <li>
+               <img border="0" src="<?php echo $plugindirurl?>silk/plugin_edit.png" />
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['AdminURL'].'index.php?action=pluginoptions&plugid='.$plugid),ENT_QUOTES);?>">プラグインオプション設定</a>
+       </li>
+</ul>
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/ping.html b/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/ping.html
new file mode 100644 (file)
index 0000000..84a882c
--- /dev/null
@@ -0,0 +1,50 @@
+<?php global $manager; ?>
+<h2>手動pingフォーム</h2>
+
+<form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+
+    <input type="hidden" name="action" value="sendping" />
+    <input type="hidden" name="next" value="ping" />
+    <?php $manager->addTicketHidden(); ?>
+       
+    <table>
+        <tr>
+            <th colspan='2'>手動ping</th>
+        </tr>
+        <tr>
+            <td>あなたのurl</td>
+            <td>
+                <input type="text" name="url" size="60" value="<?php echo htmlspecialchars($item['url']);?>" />
+            </td>
+        </tr>
+        <tr>
+            <td>記事のタイトル</td>
+            <td>
+                <input type="text" name="title" size="60" value="<?php echo htmlspecialchars($item['title']);?>" />
+            </td>
+        </tr>
+        <tr>
+            <td>記事の要約文</td>
+            <td>
+                    <textarea name="excerpt" cols="40" rows="5"><?php echo $item['excerpt'];?></textarea>
+            </td>
+        </tr>
+        <tr>
+            <td>Blog名</td>
+            <td>
+                <input type="text" name="blog_name" size="60" value="<?php echo htmlspecialchars($item['blogname']);?>" />
+            </td>
+        </tr>
+        <tr>
+            <td>ping先url</td>
+            <td>
+                <input type="text" value="" name="ping_url" size="60" />
+            </td>
+        </tr>
+        <tr>
+            <td>送信</td>
+            <td><input type="submit" value="送信" /></td>
+        </tr>
+    </table>
+
+</form>
\ No newline at end of file
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/response_all.xml b/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/response_all.xml
new file mode 100644 (file)
index 0000000..4ae2445
--- /dev/null
@@ -0,0 +1,35 @@
+<?php echo '<'.'?xml version="1.0" encoding="UTF-8"?'.'>';?>\r
+<?php global $manager; ?>\r
+\r
+<ajax-response>\r
+       <response type="object" id="tb_grid_updater">\r
+               <rowcount><?php echo $count; ?></rowcount>\r
+               <rows update_ui="true" offset="<?php echo $start; ?>" >\r
+                       <?php while (list(,$item) = each ($items)): ?>\r
+                       <tr>\r
+                               <td>0</td>\r
+                               <td><?php echo $item['id'];?></td>\r
+                               <td>\r
+                                       <?php echo date("Y-m-d H:i:s",$item['timestamp']);?>\r
+                               </td>\r
+                               <td>\r
+                                       <![CDATA[\r
+                                       <a href="<?php echo $item['story_url']; ?>"><?php echo $item['story'];?></a>\r
+                                       ]]>\r
+                               </td>\r
+                               <td>\r
+                                       <![CDATA[\r
+                                       <a href="<?php echo $item['url'];?>">\r
+                                               <img alt="Visit" border="0" src="<?php echo $plugindirurl?>silk/house_go.png" />\r
+                                       </a>\r
+                                       <strong><?php echo $item['title'];?></strong>\r
+                                       <?php echo $item['excerpt'];?>\r
+                                       <em>(<?php echo $item['blog_name'];?>)</em>\r
+                                       ]]>\r
+                               </td>\r
+                               <td></td>\r
+                       </tr>\r
+                       <?php endwhile; ?>\r
+               </rows> \r
+       </response>\r
+</ajax-response>\r
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/response_blocked.xml b/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/response_blocked.xml
new file mode 100644 (file)
index 0000000..29faac9
--- /dev/null
@@ -0,0 +1,41 @@
+<?php echo '<'.'?xml version="1.0" encoding="UTF-8"?'.'>';?>\r
+<?php global $manager; ?>\r
+\r
+<ajax-response>\r
+       <response type="object" id='tb_grid_updater'>\r
+               <rowcount><?php echo $count; ?></rowcount>\r
+               <rows update_ui='true' >\r
+                       <?php while (list(,$item) = each ($items)): ?>\r
+                       <tr>\r
+                               <td>0</td>\r
+                               <td><?php echo $item['id'];?></td>\r
+                               <td>\r
+                                       <?php echo date("Y-m-d H:i:s",$item['timestamp']);?>\r
+                               </td>\r
+                               <td>\r
+                                       <![CDATA[\r
+                                       <a href="<?php echo $item['story_url']; ?>"><?php echo $item['story'];?></a>\r
+                                       ]]>\r
+                               </td>\r
+                               <td>\r
+                                       <![CDATA[\r
+                                       <a href="<?php echo $item['url'];?>">\r
+                                               <img alt="Visit" border="0" src="<?php echo $plugindirurl?>silk/house_go.png" />\r
+                                       </a>\r
+                                       <strong><?php echo $item['title'];?></strong>\r
+                                       <?php echo $item['spam'] ? \r
+                                               '<img alt="spam" border="0" src="' . $plugindirurl . 'silk/delete.png" />' : \r
+                                               '';?>\r
+                                       <?php echo $item['link'] ? \r
+                                               '' : \r
+                                               '<img alt="NOT Linked" border="0" src="' . $plugindirurl . 'silk/link_break.png" />';?>\r
+                                       <?php echo $item['excerpt'];?>\r
+                                       <em>(<?php echo $item['blog_name'];?>)</em>\r
+                                       ]]>\r
+                               </td>\r
+                               <td></td>\r
+                       </tr>\r
+                       <?php endwhile; ?>\r
+               </rows> \r
+       </response>\r
+</ajax-response>\r
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/response_doblock.xml b/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/response_doblock.xml
new file mode 100644 (file)
index 0000000..a53f37c
--- /dev/null
@@ -0,0 +1,8 @@
+<?echo '<'.'?xml version="1.0" encoding="UTF-8"?'.'>';?>
+<?php global $manager; ?>
+
+<ajax-response>
+   <response type="element" id="message">
+               <?php echo $message; ?>
+   </response>
+</ajax-response>
\ No newline at end of file
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/response_dodelete.xml b/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/response_dodelete.xml
new file mode 100644 (file)
index 0000000..a53f37c
--- /dev/null
@@ -0,0 +1,8 @@
+<?echo '<'.'?xml version="1.0" encoding="UTF-8"?'.'>';?>
+<?php global $manager; ?>
+
+<ajax-response>
+   <response type="element" id="message">
+               <?php echo $message; ?>
+   </response>
+</ajax-response>
\ No newline at end of file
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/response_dounblock.xml b/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/response_dounblock.xml
new file mode 100644 (file)
index 0000000..a53f37c
--- /dev/null
@@ -0,0 +1,8 @@
+<?echo '<'.'?xml version="1.0" encoding="UTF-8"?'.'>';?>
+<?php global $manager; ?>
+
+<ajax-response>
+   <response type="element" id="message">
+               <?php echo $message; ?>
+   </response>
+</ajax-response>
\ No newline at end of file
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/updatetable.html b/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/updatetable.html
new file mode 100644 (file)
index 0000000..a426a8a
--- /dev/null
@@ -0,0 +1,13 @@
+<?php global $manager; ?>
+<blockquote style="color: red;border:1px solid red;padding:1em;"><b>アップデートが必要です:</b><br />
+このバージョンで運用するためにはDB内のテーブルのアップデートが必要です。<br />
+今までのデータが削除されることはありません。
+下のアップデートボタンを押してください。
+
+                       <form method="post"><div>
+                               <input type="hidden" name="action" value="tableUpgrade" />
+                               <input type="submit" tabindex="10" value="upgrade table" />
+                               <?php $manager->addTicketHidden(); ?>
+                       </div></form>
+</blockquote>
+       
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/updatetablefinished.html b/NP_TrackBack/branches/DOM-branch/trackback/japanese-utf8.templates/updatetablefinished.html
new file mode 100644 (file)
index 0000000..6b1ea94
--- /dev/null
@@ -0,0 +1,5 @@
+<?php global $manager; ?>
+<blockquote style="color: red;border:1px solid red;padding:1em;">
+テーブルのアップデートは完了しました。
+</blockquote>
+       
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/prototype.js b/NP_TrackBack/branches/DOM-branch/trackback/js/prototype.js
new file mode 100644 (file)
index 0000000..5d2100f
--- /dev/null
@@ -0,0 +1,3271 @@
+/*  Prototype JavaScript framework, version 1.5.1
+ *  (c) 2005-2007 Sam Stephenson
+ *
+ *  Prototype is freely distributable under the terms of an MIT-style license.
+ *  For details, see the Prototype web site: http://www.prototypejs.org/
+ *
+/*--------------------------------------------------------------------------*/
+
+var Prototype = {
+  Version: '1.5.1',
+
+  Browser: {
+    IE:     !!(window.attachEvent && !window.opera),
+    Opera:  !!window.opera,
+    WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
+    Gecko:  navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1
+  },
+
+  BrowserFeatures: {
+    XPath: !!document.evaluate,
+    ElementExtensions: !!window.HTMLElement,
+    SpecificElementExtensions:
+      (document.createElement('div').__proto__ !==
+       document.createElement('form').__proto__)
+  },
+
+  ScriptFragment: '<script[^>]*>([\u0001-\uFFFF]*?)</script>',
+  JSONFilter: /^\/\*-secure-\s*(.*)\s*\*\/\s*$/,
+
+  emptyFunction: function() { },
+  K: function(x) { return x }
+}
+
+var Class = {
+  create: function() {
+    return function() {
+      this.initialize.apply(this, arguments);
+    }
+  }
+}
+
+var Abstract = new Object();
+
+Object.extend = function(destination, source) {
+  for (var property in source) {
+    destination[property] = source[property];
+  }
+  return destination;
+}
+
+Object.extend(Object, {
+  inspect: function(object) {
+    try {
+      if (object === undefined) return 'undefined';
+      if (object === null) return 'null';
+      return object.inspect ? object.inspect() : object.toString();
+    } catch (e) {
+      if (e instanceof RangeError) return '...';
+      throw e;
+    }
+  },
+
+  toJSON: function(object) {
+    var type = typeof object;
+    switch(type) {
+      case 'undefined':
+      case 'function':
+      case 'unknown': return;
+      case 'boolean': return object.toString();
+    }
+    if (object === null) return 'null';
+    if (object.toJSON) return object.toJSON();
+    if (object.ownerDocument === document) return;
+    var results = [];
+    for (var property in object) {
+      var value = Object.toJSON(object[property]);
+      if (value !== undefined)
+        results.push(property.toJSON() + ': ' + value);
+    }
+    return '{' + results.join(', ') + '}';
+  },
+
+  keys: function(object) {
+    var keys = [];
+    for (var property in object)
+      keys.push(property);
+    return keys;
+  },
+
+  values: function(object) {
+    var values = [];
+    for (var property in object)
+      values.push(object[property]);
+    return values;
+  },
+
+  clone: function(object) {
+    return Object.extend({}, object);
+  }
+});
+
+Function.prototype.bind = function() {
+  var __method = this, args = $A(arguments), object = args.shift();
+  return function() {
+    return __method.apply(object, args.concat($A(arguments)));
+  }
+}
+
+Function.prototype.bindAsEventListener = function(object) {
+  var __method = this, args = $A(arguments), object = args.shift();
+  return function(event) {
+    return __method.apply(object, [event || window.event].concat(args));
+  }
+}
+
+Object.extend(Number.prototype, {
+  toColorPart: function() {
+    return this.toPaddedString(2, 16);
+  },
+
+  succ: function() {
+    return this + 1;
+  },
+
+  times: function(iterator) {
+    $R(0, this, true).each(iterator);
+    return this;
+  },
+
+  toPaddedString: function(length, radix) {
+    var string = this.toString(radix || 10);
+    return '0'.times(length - string.length) + string;
+  },
+
+  toJSON: function() {
+    return isFinite(this) ? this.toString() : 'null';
+  }
+});
+
+Date.prototype.toJSON = function() {
+  return '"' + this.getFullYear() + '-' +
+    (this.getMonth() + 1).toPaddedString(2) + '-' +
+    this.getDate().toPaddedString(2) + 'T' +
+    this.getHours().toPaddedString(2) + ':' +
+    this.getMinutes().toPaddedString(2) + ':' +
+    this.getSeconds().toPaddedString(2) + '"';
+};
+
+var Try = {
+  these: function() {
+    var returnValue;
+
+    for (var i = 0, length = arguments.length; i < length; i++) {
+      var lambda = arguments[i];
+      try {
+        returnValue = lambda();
+        break;
+      } catch (e) {}
+    }
+
+    return returnValue;
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var PeriodicalExecuter = Class.create();
+PeriodicalExecuter.prototype = {
+  initialize: function(callback, frequency) {
+    this.callback = callback;
+    this.frequency = frequency;
+    this.currentlyExecuting = false;
+
+    this.registerCallback();
+  },
+
+  registerCallback: function() {
+    this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+  },
+
+  stop: function() {
+    if (!this.timer) return;
+    clearInterval(this.timer);
+    this.timer = null;
+  },
+
+  onTimerEvent: function() {
+    if (!this.currentlyExecuting) {
+      try {
+        this.currentlyExecuting = true;
+        this.callback(this);
+      } finally {
+        this.currentlyExecuting = false;
+      }
+    }
+  }
+}
+Object.extend(String, {
+  interpret: function(value) {
+    return value == null ? '' : String(value);
+  },
+  specialChar: {
+    '\b': '\\b',
+    '\t': '\\t',
+    '\n': '\\n',
+    '\f': '\\f',
+    '\r': '\\r',
+    '\\': '\\\\'
+  }
+});
+
+Object.extend(String.prototype, {
+  gsub: function(pattern, replacement) {
+    var result = '', source = this, match;
+    replacement = arguments.callee.prepareReplacement(replacement);
+
+    while (source.length > 0) {
+      if (match = source.match(pattern)) {
+        result += source.slice(0, match.index);
+        result += String.interpret(replacement(match));
+        source  = source.slice(match.index + match[0].length);
+      } else {
+        result += source, source = '';
+      }
+    }
+    return result;
+  },
+
+  sub: function(pattern, replacement, count) {
+    replacement = this.gsub.prepareReplacement(replacement);
+    count = count === undefined ? 1 : count;
+
+    return this.gsub(pattern, function(match) {
+      if (--count < 0) return match[0];
+      return replacement(match);
+    });
+  },
+
+  scan: function(pattern, iterator) {
+    this.gsub(pattern, iterator);
+    return this;
+  },
+
+  truncate: function(length, truncation) {
+    length = length || 30;
+    truncation = truncation === undefined ? '...' : truncation;
+    return this.length > length ?
+      this.slice(0, length - truncation.length) + truncation : this;
+  },
+
+  strip: function() {
+    return this.replace(/^\s+/, '').replace(/\s+$/, '');
+  },
+
+  stripTags: function() {
+    return this.replace(/<\/?[^>]+>/gi, '');
+  },
+
+  stripScripts: function() {
+    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
+  },
+
+  extractScripts: function() {
+    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
+    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+    return (this.match(matchAll) || []).map(function(scriptTag) {
+      return (scriptTag.match(matchOne) || ['', ''])[1];
+    });
+  },
+
+  evalScripts: function() {
+    return this.extractScripts().map(function(script) { return eval(script) });
+  },
+
+  escapeHTML: function() {
+    var self = arguments.callee;
+    self.text.data = this;
+    return self.div.innerHTML;
+  },
+
+  unescapeHTML: function() {
+    var div = document.createElement('div');
+    div.innerHTML = this.stripTags();
+    return div.childNodes[0] ? (div.childNodes.length > 1 ?
+      $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
+      div.childNodes[0].nodeValue) : '';
+  },
+
+  toQueryParams: function(separator) {
+    var match = this.strip().match(/([^?#]*)(#.*)?$/);
+    if (!match) return {};
+
+    return match[1].split(separator || '&').inject({}, function(hash, pair) {
+      if ((pair = pair.split('='))[0]) {
+        var key = decodeURIComponent(pair.shift());
+        var value = pair.length > 1 ? pair.join('=') : pair[0];
+        if (value != undefined) value = decodeURIComponent(value);
+
+        if (key in hash) {
+          if (hash[key].constructor != Array) hash[key] = [hash[key]];
+          hash[key].push(value);
+        }
+        else hash[key] = value;
+      }
+      return hash;
+    });
+  },
+
+  toArray: function() {
+    return this.split('');
+  },
+
+  succ: function() {
+    return this.slice(0, this.length - 1) +
+      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
+  },
+
+  times: function(count) {
+    var result = '';
+    for (var i = 0; i < count; i++) result += this;
+    return result;
+  },
+
+  camelize: function() {
+    var parts = this.split('-'), len = parts.length;
+    if (len == 1) return parts[0];
+
+    var camelized = this.charAt(0) == '-'
+      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
+      : parts[0];
+
+    for (var i = 1; i < len; i++)
+      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
+
+    return camelized;
+  },
+
+  capitalize: function() {
+    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
+  },
+
+  underscore: function() {
+    return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
+  },
+
+  dasherize: function() {
+    return this.gsub(/_/,'-');
+  },
+
+  inspect: function(useDoubleQuotes) {
+    var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
+      var character = String.specialChar[match[0]];
+      return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
+    });
+    if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
+    return "'" + escapedString.replace(/'/g, '\\\'') + "'";
+  },
+
+  toJSON: function() {
+    return this.inspect(true);
+  },
+
+  unfilterJSON: function(filter) {
+    return this.sub(filter || Prototype.JSONFilter, '#{1}');
+  },
+
+  evalJSON: function(sanitize) {
+    var json = this.unfilterJSON();
+    try {
+      if (!sanitize || (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(json)))
+        return eval('(' + json + ')');
+    } catch (e) { }
+    throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
+  },
+
+  include: function(pattern) {
+    return this.indexOf(pattern) > -1;
+  },
+
+  startsWith: function(pattern) {
+    return this.indexOf(pattern) === 0;
+  },
+
+  endsWith: function(pattern) {
+    var d = this.length - pattern.length;
+    return d >= 0 && this.lastIndexOf(pattern) === d;
+  },
+
+  empty: function() {
+    return this == '';
+  },
+
+  blank: function() {
+    return /^\s*$/.test(this);
+  }
+});
+
+if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
+  escapeHTML: function() {
+    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
+  },
+  unescapeHTML: function() {
+    return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
+  }
+});
+
+String.prototype.gsub.prepareReplacement = function(replacement) {
+  if (typeof replacement == 'function') return replacement;
+  var template = new Template(replacement);
+  return function(match) { return template.evaluate(match) };
+}
+
+String.prototype.parseQuery = String.prototype.toQueryParams;
+
+Object.extend(String.prototype.escapeHTML, {
+  div:  document.createElement('div'),
+  text: document.createTextNode('')
+});
+
+with (String.prototype.escapeHTML) div.appendChild(text);
+
+var Template = Class.create();
+Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
+Template.prototype = {
+  initialize: function(template, pattern) {
+    this.template = template.toString();
+    this.pattern  = pattern || Template.Pattern;
+  },
+
+  evaluate: function(object) {
+    return this.template.gsub(this.pattern, function(match) {
+      var before = match[1];
+      if (before == '\\') return match[2];
+      return before + String.interpret(object[match[3]]);
+    });
+  }
+}
+
+var $break = {}, $continue = new Error('"throw $continue" is deprecated, use "return" instead');
+
+var Enumerable = {
+  each: function(iterator) {
+    var index = 0;
+    try {
+      this._each(function(value) {
+        iterator(value, index++);
+      });
+    } catch (e) {
+      if (e != $break) throw e;
+    }
+    return this;
+  },
+
+  eachSlice: function(number, iterator) {
+    var index = -number, slices = [], array = this.toArray();
+    while ((index += number) < array.length)
+      slices.push(array.slice(index, index+number));
+    return slices.map(iterator);
+  },
+
+  all: function(iterator) {
+    var result = true;
+    this.each(function(value, index) {
+      result = result && !!(iterator || Prototype.K)(value, index);
+      if (!result) throw $break;
+    });
+    return result;
+  },
+
+  any: function(iterator) {
+    var result = false;
+    this.each(function(value, index) {
+      if (result = !!(iterator || Prototype.K)(value, index))
+        throw $break;
+    });
+    return result;
+  },
+
+  collect: function(iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      results.push((iterator || Prototype.K)(value, index));
+    });
+    return results;
+  },
+
+  detect: function(iterator) {
+    var result;
+    this.each(function(value, index) {
+      if (iterator(value, index)) {
+        result = value;
+        throw $break;
+      }
+    });
+    return result;
+  },
+
+  findAll: function(iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      if (iterator(value, index))
+        results.push(value);
+    });
+    return results;
+  },
+
+  grep: function(pattern, iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      var stringValue = value.toString();
+      if (stringValue.match(pattern))
+        results.push((iterator || Prototype.K)(value, index));
+    })
+    return results;
+  },
+
+  include: function(object) {
+    var found = false;
+    this.each(function(value) {
+      if (value == object) {
+        found = true;
+        throw $break;
+      }
+    });
+    return found;
+  },
+
+  inGroupsOf: function(number, fillWith) {
+    fillWith = fillWith === undefined ? null : fillWith;
+    return this.eachSlice(number, function(slice) {
+      while(slice.length < number) slice.push(fillWith);
+      return slice;
+    });
+  },
+
+  inject: function(memo, iterator) {
+    this.each(function(value, index) {
+      memo = iterator(memo, value, index);
+    });
+    return memo;
+  },
+
+  invoke: function(method) {
+    var args = $A(arguments).slice(1);
+    return this.map(function(value) {
+      return value[method].apply(value, args);
+    });
+  },
+
+  max: function(iterator) {
+    var result;
+    this.each(function(value, index) {
+      value = (iterator || Prototype.K)(value, index);
+      if (result == undefined || value >= result)
+        result = value;
+    });
+    return result;
+  },
+
+  min: function(iterator) {
+    var result;
+    this.each(function(value, index) {
+      value = (iterator || Prototype.K)(value, index);
+      if (result == undefined || value < result)
+        result = value;
+    });
+    return result;
+  },
+
+  partition: function(iterator) {
+    var trues = [], falses = [];
+    this.each(function(value, index) {
+      ((iterator || Prototype.K)(value, index) ?
+        trues : falses).push(value);
+    });
+    return [trues, falses];
+  },
+
+  pluck: function(property) {
+    var results = [];
+    this.each(function(value, index) {
+      results.push(value[property]);
+    });
+    return results;
+  },
+
+  reject: function(iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      if (!iterator(value, index))
+        results.push(value);
+    });
+    return results;
+  },
+
+  sortBy: function(iterator) {
+    return this.map(function(value, index) {
+      return {value: value, criteria: iterator(value, index)};
+    }).sort(function(left, right) {
+      var a = left.criteria, b = right.criteria;
+      return a < b ? -1 : a > b ? 1 : 0;
+    }).pluck('value');
+  },
+
+  toArray: function() {
+    return this.map();
+  },
+
+  zip: function() {
+    var iterator = Prototype.K, args = $A(arguments);
+    if (typeof args.last() == 'function')
+      iterator = args.pop();
+
+    var collections = [this].concat(args).map($A);
+    return this.map(function(value, index) {
+      return iterator(collections.pluck(index));
+    });
+  },
+
+  size: function() {
+    return this.toArray().length;
+  },
+
+  inspect: function() {
+    return '#<Enumerable:' + this.toArray().inspect() + '>';
+  }
+}
+
+Object.extend(Enumerable, {
+  map:     Enumerable.collect,
+  find:    Enumerable.detect,
+  select:  Enumerable.findAll,
+  member:  Enumerable.include,
+  entries: Enumerable.toArray
+});
+var $A = Array.from = function(iterable) {
+  if (!iterable) return [];
+  if (iterable.toArray) {
+    return iterable.toArray();
+  } else {
+    var results = [];
+    for (var i = 0, length = iterable.length; i < length; i++)
+      results.push(iterable[i]);
+    return results;
+  }
+}
+
+if (Prototype.Browser.WebKit) {
+  $A = Array.from = function(iterable) {
+    if (!iterable) return [];
+    if (!(typeof iterable == 'function' && iterable == '[object NodeList]') &&
+      iterable.toArray) {
+      return iterable.toArray();
+    } else {
+      var results = [];
+      for (var i = 0, length = iterable.length; i < length; i++)
+        results.push(iterable[i]);
+      return results;
+    }
+  }
+}
+
+Object.extend(Array.prototype, Enumerable);
+
+if (!Array.prototype._reverse)
+  Array.prototype._reverse = Array.prototype.reverse;
+
+Object.extend(Array.prototype, {
+  _each: function(iterator) {
+    for (var i = 0, length = this.length; i < length; i++)
+      iterator(this[i]);
+  },
+
+  clear: function() {
+    this.length = 0;
+    return this;
+  },
+
+  first: function() {
+    return this[0];
+  },
+
+  last: function() {
+    return this[this.length - 1];
+  },
+
+  compact: function() {
+    return this.select(function(value) {
+      return value != null;
+    });
+  },
+
+  flatten: function() {
+    return this.inject([], function(array, value) {
+      return array.concat(value && value.constructor == Array ?
+        value.flatten() : [value]);
+    });
+  },
+
+  without: function() {
+    var values = $A(arguments);
+    return this.select(function(value) {
+      return !values.include(value);
+    });
+  },
+
+  indexOf: function(object) {
+    for (var i = 0, length = this.length; i < length; i++)
+      if (this[i] == object) return i;
+    return -1;
+  },
+
+  reverse: function(inline) {
+    return (inline !== false ? this : this.toArray())._reverse();
+  },
+
+  reduce: function() {
+    return this.length > 1 ? this : this[0];
+  },
+
+  uniq: function(sorted) {
+    return this.inject([], function(array, value, index) {
+      if (0 == index || (sorted ? array.last() != value : !array.include(value)))
+        array.push(value);
+      return array;
+    });
+  },
+
+  clone: function() {
+    return [].concat(this);
+  },
+
+  size: function() {
+    return this.length;
+  },
+
+  inspect: function() {
+    return '[' + this.map(Object.inspect).join(', ') + ']';
+  },
+
+  toJSON: function() {
+    var results = [];
+    this.each(function(object) {
+      var value = Object.toJSON(object);
+      if (value !== undefined) results.push(value);
+    });
+    return '[' + results.join(', ') + ']';
+  }
+});
+
+Array.prototype.toArray = Array.prototype.clone;
+
+function $w(string) {
+  string = string.strip();
+  return string ? string.split(/\s+/) : [];
+}
+
+if (Prototype.Browser.Opera){
+  Array.prototype.concat = function() {
+    var array = [];
+    for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
+    for (var i = 0, length = arguments.length; i < length; i++) {
+      if (arguments[i].constructor == Array) {
+        for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
+          array.push(arguments[i][j]);
+      } else {
+        array.push(arguments[i]);
+      }
+    }
+    return array;
+  }
+}
+var Hash = function(object) {
+  if (object instanceof Hash) this.merge(object);
+  else Object.extend(this, object || {});
+};
+
+Object.extend(Hash, {
+  toQueryString: function(obj) {
+    var parts = [];
+    parts.add = arguments.callee.addPair;
+
+    this.prototype._each.call(obj, function(pair) {
+      if (!pair.key) return;
+      var value = pair.value;
+
+      if (value && typeof value == 'object') {
+        if (value.constructor == Array) value.each(function(value) {
+          parts.add(pair.key, value);
+        });
+        return;
+      }
+      parts.add(pair.key, value);
+    });
+
+    return parts.join('&');
+  },
+
+  toJSON: function(object) {
+    var results = [];
+    this.prototype._each.call(object, function(pair) {
+      var value = Object.toJSON(pair.value);
+      if (value !== undefined) results.push(pair.key.toJSON() + ': ' + value);
+    });
+    return '{' + results.join(', ') + '}';
+  }
+});
+
+Hash.toQueryString.addPair = function(key, value, prefix) {
+  key = encodeURIComponent(key);
+  if (value === undefined) this.push(key);
+  else this.push(key + '=' + (value == null ? '' : encodeURIComponent(value)));
+}
+
+Object.extend(Hash.prototype, Enumerable);
+Object.extend(Hash.prototype, {
+  _each: function(iterator) {
+    for (var key in this) {
+      var value = this[key];
+      if (value && value == Hash.prototype[key]) continue;
+
+      var pair = [key, value];
+      pair.key = key;
+      pair.value = value;
+      iterator(pair);
+    }
+  },
+
+  keys: function() {
+    return this.pluck('key');
+  },
+
+  values: function() {
+    return this.pluck('value');
+  },
+
+  merge: function(hash) {
+    return $H(hash).inject(this, function(mergedHash, pair) {
+      mergedHash[pair.key] = pair.value;
+      return mergedHash;
+    });
+  },
+
+  remove: function() {
+    var result;
+    for(var i = 0, length = arguments.length; i < length; i++) {
+      var value = this[arguments[i]];
+      if (value !== undefined){
+        if (result === undefined) result = value;
+        else {
+          if (result.constructor != Array) result = [result];
+          result.push(value)
+        }
+      }
+      delete this[arguments[i]];
+    }
+    return result;
+  },
+
+  toQueryString: function() {
+    return Hash.toQueryString(this);
+  },
+
+  inspect: function() {
+    return '#<Hash:{' + this.map(function(pair) {
+      return pair.map(Object.inspect).join(': ');
+    }).join(', ') + '}>';
+  },
+
+  toJSON: function() {
+    return Hash.toJSON(this);
+  }
+});
+
+function $H(object) {
+  if (object instanceof Hash) return object;
+  return new Hash(object);
+};
+
+// Safari iterates over shadowed properties
+if (function() {
+  var i = 0, Test = function(value) { this.key = value };
+  Test.prototype.key = 'foo';
+  for (var property in new Test('bar')) i++;
+  return i > 1;
+}()) Hash.prototype._each = function(iterator) {
+  var cache = [];
+  for (var key in this) {
+    var value = this[key];
+    if ((value && value == Hash.prototype[key]) || cache.include(key)) continue;
+    cache.push(key);
+    var pair = [key, value];
+    pair.key = key;
+    pair.value = value;
+    iterator(pair);
+  }
+};
+ObjectRange = Class.create();
+Object.extend(ObjectRange.prototype, Enumerable);
+Object.extend(ObjectRange.prototype, {
+  initialize: function(start, end, exclusive) {
+    this.start = start;
+    this.end = end;
+    this.exclusive = exclusive;
+  },
+
+  _each: function(iterator) {
+    var value = this.start;
+    while (this.include(value)) {
+      iterator(value);
+      value = value.succ();
+    }
+  },
+
+  include: function(value) {
+    if (value < this.start)
+      return false;
+    if (this.exclusive)
+      return value < this.end;
+    return value <= this.end;
+  }
+});
+
+var $R = function(start, end, exclusive) {
+  return new ObjectRange(start, end, exclusive);
+}
+
+var Ajax = {
+  getTransport: function() {
+    return Try.these(
+      function() {return new XMLHttpRequest()},
+      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
+    ) || false;
+  },
+
+  activeRequestCount: 0
+}
+
+Ajax.Responders = {
+  responders: [],
+
+  _each: function(iterator) {
+    this.responders._each(iterator);
+  },
+
+  register: function(responder) {
+    if (!this.include(responder))
+      this.responders.push(responder);
+  },
+
+  unregister: function(responder) {
+    this.responders = this.responders.without(responder);
+  },
+
+  dispatch: function(callback, request, transport, json) {
+    this.each(function(responder) {
+      if (typeof responder[callback] == 'function') {
+        try {
+          responder[callback].apply(responder, [request, transport, json]);
+        } catch (e) {}
+      }
+    });
+  }
+};
+
+Object.extend(Ajax.Responders, Enumerable);
+
+Ajax.Responders.register({
+  onCreate: function() {
+    Ajax.activeRequestCount++;
+  },
+  onComplete: function() {
+    Ajax.activeRequestCount--;
+  }
+});
+
+Ajax.Base = function() {};
+Ajax.Base.prototype = {
+  setOptions: function(options) {
+    this.options = {
+      method:       'post',
+      asynchronous: true,
+      contentType:  'application/x-www-form-urlencoded',
+      encoding:     'UTF-8',
+      parameters:   ''
+    }
+    Object.extend(this.options, options || {});
+
+    this.options.method = this.options.method.toLowerCase();
+    if (typeof this.options.parameters == 'string')
+      this.options.parameters = this.options.parameters.toQueryParams();
+  }
+}
+
+Ajax.Request = Class.create();
+Ajax.Request.Events =
+  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
+  _complete: false,
+
+  initialize: function(url, options) {
+    this.transport = Ajax.getTransport();
+    this.setOptions(options);
+    this.request(url);
+  },
+
+  request: function(url) {
+    this.url = url;
+    this.method = this.options.method;
+    var params = Object.clone(this.options.parameters);
+
+    if (!['get', 'post'].include(this.method)) {
+      // simulate other verbs over post
+      params['_method'] = this.method;
+      this.method = 'post';
+    }
+
+    this.parameters = params;
+
+    if (params = Hash.toQueryString(params)) {
+      // when GET, append parameters to URL
+      if (this.method == 'get')
+        this.url += (this.url.include('?') ? '&' : '?') + params;
+      else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
+        params += '&_=';
+    }
+
+    try {
+      if (this.options.onCreate) this.options.onCreate(this.transport);
+      Ajax.Responders.dispatch('onCreate', this, this.transport);
+
+      this.transport.open(this.method.toUpperCase(), this.url,
+        this.options.asynchronous);
+
+      if (this.options.asynchronous)
+        setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
+
+      this.transport.onreadystatechange = this.onStateChange.bind(this);
+      this.setRequestHeaders();
+
+      this.body = this.method == 'post' ? (this.options.postBody || params) : null;
+      this.transport.send(this.body);
+
+      /* Force Firefox to handle ready state 4 for synchronous requests */
+      if (!this.options.asynchronous && this.transport.overrideMimeType)
+        this.onStateChange();
+
+    }
+    catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
+  onStateChange: function() {
+    var readyState = this.transport.readyState;
+    if (readyState > 1 && !((readyState == 4) && this._complete))
+      this.respondToReadyState(this.transport.readyState);
+  },
+
+  setRequestHeaders: function() {
+    var headers = {
+      'X-Requested-With': 'XMLHttpRequest',
+      'X-Prototype-Version': Prototype.Version,
+      'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
+    };
+
+    if (this.method == 'post') {
+      headers['Content-type'] = this.options.contentType +
+        (this.options.encoding ? '; charset=' + this.options.encoding : '');
+
+      /* Force "Connection: close" for older Mozilla browsers to work
+       * around a bug where XMLHttpRequest sends an incorrect
+       * Content-length header. See Mozilla Bugzilla #246651.
+       */
+      if (this.transport.overrideMimeType &&
+          (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
+            headers['Connection'] = 'close';
+    }
+
+    // user-defined headers
+    if (typeof this.options.requestHeaders == 'object') {
+      var extras = this.options.requestHeaders;
+
+      if (typeof extras.push == 'function')
+        for (var i = 0, length = extras.length; i < length; i += 2)
+          headers[extras[i]] = extras[i+1];
+      else
+        $H(extras).each(function(pair) { headers[pair.key] = pair.value });
+    }
+
+    for (var name in headers)
+      this.transport.setRequestHeader(name, headers[name]);
+  },
+
+  success: function() {
+    return !this.transport.status
+        || (this.transport.status >= 200 && this.transport.status < 300);
+  },
+
+  respondToReadyState: function(readyState) {
+    var state = Ajax.Request.Events[readyState];
+    var transport = this.transport, json = this.evalJSON();
+
+    if (state == 'Complete') {
+      try {
+        this._complete = true;
+        (this.options['on' + this.transport.status]
+         || this.options['on' + (this.success() ? 'Success' : 'Failure')]
+         || Prototype.emptyFunction)(transport, json);
+      } catch (e) {
+        this.dispatchException(e);
+      }
+
+      var contentType = this.getHeader('Content-type');
+      if (contentType && contentType.strip().
+        match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
+          this.evalResponse();
+    }
+
+    try {
+      (this.options['on' + state] || Prototype.emptyFunction)(transport, json);
+      Ajax.Responders.dispatch('on' + state, this, transport, json);
+    } catch (e) {
+      this.dispatchException(e);
+    }
+
+    if (state == 'Complete') {
+      // avoid memory leak in MSIE: clean up
+      this.transport.onreadystatechange = Prototype.emptyFunction;
+    }
+  },
+
+  getHeader: function(name) {
+    try {
+      return this.transport.getResponseHeader(name);
+    } catch (e) { return null }
+  },
+
+  evalJSON: function() {
+    try {
+      var json = this.getHeader('X-JSON');
+      return json ? json.evalJSON() : null;
+    } catch (e) { return null }
+  },
+
+  evalResponse: function() {
+    try {
+      return eval((this.transport.responseText || '').unfilterJSON());
+    } catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
+  dispatchException: function(exception) {
+    (this.options.onException || Prototype.emptyFunction)(this, exception);
+    Ajax.Responders.dispatch('onException', this, exception);
+  }
+});
+
+Ajax.Updater = Class.create();
+
+Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
+  initialize: function(container, url, options) {
+    this.container = {
+      success: (container.success || container),
+      failure: (container.failure || (container.success ? null : container))
+    }
+
+    this.transport = Ajax.getTransport();
+    this.setOptions(options);
+
+    var onComplete = this.options.onComplete || Prototype.emptyFunction;
+    this.options.onComplete = (function(transport, param) {
+      this.updateContent();
+      onComplete(transport, param);
+    }).bind(this);
+
+    this.request(url);
+  },
+
+  updateContent: function() {
+    var receiver = this.container[this.success() ? 'success' : 'failure'];
+    var response = this.transport.responseText;
+
+    if (!this.options.evalScripts) response = response.stripScripts();
+
+    if (receiver = $(receiver)) {
+      if (this.options.insertion)
+        new this.options.insertion(receiver, response);
+      else
+        receiver.update(response);
+    }
+
+    if (this.success()) {
+      if (this.onComplete)
+        setTimeout(this.onComplete.bind(this), 10);
+    }
+  }
+});
+
+Ajax.PeriodicalUpdater = Class.create();
+Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
+  initialize: function(container, url, options) {
+    this.setOptions(options);
+    this.onComplete = this.options.onComplete;
+
+    this.frequency = (this.options.frequency || 2);
+    this.decay = (this.options.decay || 1);
+
+    this.updater = {};
+    this.container = container;
+    this.url = url;
+
+    this.start();
+  },
+
+  start: function() {
+    this.options.onComplete = this.updateComplete.bind(this);
+    this.onTimerEvent();
+  },
+
+  stop: function() {
+    this.updater.options.onComplete = undefined;
+    clearTimeout(this.timer);
+    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
+  },
+
+  updateComplete: function(request) {
+    if (this.options.decay) {
+      this.decay = (request.responseText == this.lastText ?
+        this.decay * this.options.decay : 1);
+
+      this.lastText = request.responseText;
+    }
+    this.timer = setTimeout(this.onTimerEvent.bind(this),
+      this.decay * this.frequency * 1000);
+  },
+
+  onTimerEvent: function() {
+    this.updater = new Ajax.Updater(this.container, this.url, this.options);
+  }
+});
+function $(element) {
+  if (arguments.length > 1) {
+    for (var i = 0, elements = [], length = arguments.length; i < length; i++)
+      elements.push($(arguments[i]));
+    return elements;
+  }
+  if (typeof element == 'string')
+    element = document.getElementById(element);
+  return Element.extend(element);
+}
+
+if (Prototype.BrowserFeatures.XPath) {
+  document._getElementsByXPath = function(expression, parentElement) {
+    var results = [];
+    var query = document.evaluate(expression, $(parentElement) || document,
+      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+    for (var i = 0, length = query.snapshotLength; i < length; i++)
+      results.push(query.snapshotItem(i));
+    return results;
+  };
+
+  document.getElementsByClassName = function(className, parentElement) {
+    var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
+    return document._getElementsByXPath(q, parentElement);
+  }
+
+} else document.getElementsByClassName = function(className, parentElement) {
+  var children = ($(parentElement) || document.body).getElementsByTagName('*');
+  var elements = [], child;
+  for (var i = 0, length = children.length; i < length; i++) {
+    child = children[i];
+    if (Element.hasClassName(child, className))
+      elements.push(Element.extend(child));
+  }
+  return elements;
+};
+
+/*--------------------------------------------------------------------------*/
+
+if (!window.Element) var Element = {};
+
+Element.extend = function(element) {
+  var F = Prototype.BrowserFeatures;
+  if (!element || !element.tagName || element.nodeType == 3 ||
+   element._extended || F.SpecificElementExtensions || element == window)
+    return element;
+
+  var methods = {}, tagName = element.tagName, cache = Element.extend.cache,
+   T = Element.Methods.ByTag;
+
+  // extend methods for all tags (Safari doesn't need this)
+  if (!F.ElementExtensions) {
+    Object.extend(methods, Element.Methods),
+    Object.extend(methods, Element.Methods.Simulated);
+  }
+
+  // extend methods for specific tags
+  if (T[tagName]) Object.extend(methods, T[tagName]);
+
+  for (var property in methods) {
+    var value = methods[property];
+    if (typeof value == 'function' && !(property in element))
+      element[property] = cache.findOrStore(value);
+  }
+
+  element._extended = Prototype.emptyFunction;
+  return element;
+};
+
+Element.extend.cache = {
+  findOrStore: function(value) {
+    return this[value] = this[value] || function() {
+      return value.apply(null, [this].concat($A(arguments)));
+    }
+  }
+};
+
+Element.Methods = {
+  visible: function(element) {
+    return $(element).style.display != 'none';
+  },
+
+  toggle: function(element) {
+    element = $(element);
+    Element[Element.visible(element) ? 'hide' : 'show'](element);
+    return element;
+  },
+
+  hide: function(element) {
+    $(element).style.display = 'none';
+    return element;
+  },
+
+  show: function(element) {
+    $(element).style.display = '';
+    return element;
+  },
+
+  remove: function(element) {
+    element = $(element);
+    element.parentNode.removeChild(element);
+    return element;
+  },
+
+  update: function(element, html) {
+    html = typeof html == 'undefined' ? '' : html.toString();
+    $(element).innerHTML = html.stripScripts();
+    setTimeout(function() {html.evalScripts()}, 10);
+    return element;
+  },
+
+  replace: function(element, html) {
+    element = $(element);
+    html = typeof html == 'undefined' ? '' : html.toString();
+    if (element.outerHTML) {
+      element.outerHTML = html.stripScripts();
+    } else {
+      var range = element.ownerDocument.createRange();
+      range.selectNodeContents(element);
+      element.parentNode.replaceChild(
+        range.createContextualFragment(html.stripScripts()), element);
+    }
+    setTimeout(function() {html.evalScripts()}, 10);
+    return element;
+  },
+
+  inspect: function(element) {
+    element = $(element);
+    var result = '<' + element.tagName.toLowerCase();
+    $H({'id': 'id', 'className': 'class'}).each(function(pair) {
+      var property = pair.first(), attribute = pair.last();
+      var value = (element[property] || '').toString();
+      if (value) result += ' ' + attribute + '=' + value.inspect(true);
+    });
+    return result + '>';
+  },
+
+  recursivelyCollect: function(element, property) {
+    element = $(element);
+    var elements = [];
+    while (element = element[property])
+      if (element.nodeType == 1)
+        elements.push(Element.extend(element));
+    return elements;
+  },
+
+  ancestors: function(element) {
+    return $(element).recursivelyCollect('parentNode');
+  },
+
+  descendants: function(element) {
+    return $A($(element).getElementsByTagName('*')).each(Element.extend);
+  },
+
+  firstDescendant: function(element) {
+    element = $(element).firstChild;
+    while (element && element.nodeType != 1) element = element.nextSibling;
+    return $(element);
+  },
+
+  immediateDescendants: function(element) {
+    if (!(element = $(element).firstChild)) return [];
+    while (element && element.nodeType != 1) element = element.nextSibling;
+    if (element) return [element].concat($(element).nextSiblings());
+    return [];
+  },
+
+  previousSiblings: function(element) {
+    return $(element).recursivelyCollect('previousSibling');
+  },
+
+  nextSiblings: function(element) {
+    return $(element).recursivelyCollect('nextSibling');
+  },
+
+  siblings: function(element) {
+    element = $(element);
+    return element.previousSiblings().reverse().concat(element.nextSiblings());
+  },
+
+  match: function(element, selector) {
+    if (typeof selector == 'string')
+      selector = new Selector(selector);
+    return selector.match($(element));
+  },
+
+  up: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return $(element.parentNode);
+    var ancestors = element.ancestors();
+    return expression ? Selector.findElement(ancestors, expression, index) :
+      ancestors[index || 0];
+  },
+
+  down: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return element.firstDescendant();
+    var descendants = element.descendants();
+    return expression ? Selector.findElement(descendants, expression, index) :
+      descendants[index || 0];
+  },
+
+  previous: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
+    var previousSiblings = element.previousSiblings();
+    return expression ? Selector.findElement(previousSiblings, expression, index) :
+      previousSiblings[index || 0];
+  },
+
+  next: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
+    var nextSiblings = element.nextSiblings();
+    return expression ? Selector.findElement(nextSiblings, expression, index) :
+      nextSiblings[index || 0];
+  },
+
+  getElementsBySelector: function() {
+    var args = $A(arguments), element = $(args.shift());
+    return Selector.findChildElements(element, args);
+  },
+
+  getElementsByClassName: function(element, className) {
+    return document.getElementsByClassName(className, element);
+  },
+
+  readAttribute: function(element, name) {
+    element = $(element);
+    if (Prototype.Browser.IE) {
+      if (!element.attributes) return null;
+      var t = Element._attributeTranslations;
+      if (t.values[name]) return t.values[name](element, name);
+      if (t.names[name])  name = t.names[name];
+      var attribute = element.attributes[name];
+      return attribute ? attribute.nodeValue : null;
+    }
+    return element.getAttribute(name);
+  },
+
+  getHeight: function(element) {
+    return $(element).getDimensions().height;
+  },
+
+  getWidth: function(element) {
+    return $(element).getDimensions().width;
+  },
+
+  classNames: function(element) {
+    return new Element.ClassNames(element);
+  },
+
+  hasClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    var elementClassName = element.className;
+    if (elementClassName.length == 0) return false;
+    if (elementClassName == className ||
+        elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
+      return true;
+    return false;
+  },
+
+  addClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    Element.classNames(element).add(className);
+    return element;
+  },
+
+  removeClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    Element.classNames(element).remove(className);
+    return element;
+  },
+
+  toggleClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
+    return element;
+  },
+
+  observe: function() {
+    Event.observe.apply(Event, arguments);
+    return $A(arguments).first();
+  },
+
+  stopObserving: function() {
+    Event.stopObserving.apply(Event, arguments);
+    return $A(arguments).first();
+  },
+
+  // removes whitespace-only text node children
+  cleanWhitespace: function(element) {
+    element = $(element);
+    var node = element.firstChild;
+    while (node) {
+      var nextNode = node.nextSibling;
+      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
+        element.removeChild(node);
+      node = nextNode;
+    }
+    return element;
+  },
+
+  empty: function(element) {
+    return $(element).innerHTML.blank();
+  },
+
+  descendantOf: function(element, ancestor) {
+    element = $(element), ancestor = $(ancestor);
+    while (element = element.parentNode)
+      if (element == ancestor) return true;
+    return false;
+  },
+
+  scrollTo: function(element) {
+    element = $(element);
+    var pos = Position.cumulativeOffset(element);
+    window.scrollTo(pos[0], pos[1]);
+    return element;
+  },
+
+  getStyle: function(element, style) {
+    element = $(element);
+    style = style == 'float' ? 'cssFloat' : style.camelize();
+    var value = element.style[style];
+    if (!value) {
+      var css = document.defaultView.getComputedStyle(element, null);
+      value = css ? css[style] : null;
+    }
+    if (style == 'opacity') return value ? parseFloat(value) : 1.0;
+    return value == 'auto' ? null : value;
+  },
+
+  getOpacity: function(element) {
+    return $(element).getStyle('opacity');
+  },
+
+  setStyle: function(element, styles, camelized) {
+    element = $(element);
+    var elementStyle = element.style;
+
+    for (var property in styles)
+      if (property == 'opacity') element.setOpacity(styles[property])
+      else
+        elementStyle[(property == 'float' || property == 'cssFloat') ?
+          (elementStyle.styleFloat === undefined ? 'cssFloat' : 'styleFloat') :
+          (camelized ? property : property.camelize())] = styles[property];
+
+    return element;
+  },
+
+  setOpacity: function(element, value) {
+    element = $(element);
+    element.style.opacity = (value == 1 || value === '') ? '' :
+      (value < 0.00001) ? 0 : value;
+    return element;
+  },
+
+  getDimensions: function(element) {
+    element = $(element);
+    var display = $(element).getStyle('display');
+    if (display != 'none' && display != null) // Safari bug
+      return {width: element.offsetWidth, height: element.offsetHeight};
+
+    // All *Width and *Height properties give 0 on elements with display none,
+    // so enable the element temporarily
+    var els = element.style;
+    var originalVisibility = els.visibility;
+    var originalPosition = els.position;
+    var originalDisplay = els.display;
+    els.visibility = 'hidden';
+    els.position = 'absolute';
+    els.display = 'block';
+    var originalWidth = element.clientWidth;
+    var originalHeight = element.clientHeight;
+    els.display = originalDisplay;
+    els.position = originalPosition;
+    els.visibility = originalVisibility;
+    return {width: originalWidth, height: originalHeight};
+  },
+
+  makePositioned: function(element) {
+    element = $(element);
+    var pos = Element.getStyle(element, 'position');
+    if (pos == 'static' || !pos) {
+      element._madePositioned = true;
+      element.style.position = 'relative';
+      // Opera returns the offset relative to the positioning context, when an
+      // element is position relative but top and left have not been defined
+      if (window.opera) {
+        element.style.top = 0;
+        element.style.left = 0;
+      }
+    }
+    return element;
+  },
+
+  undoPositioned: function(element) {
+    element = $(element);
+    if (element._madePositioned) {
+      element._madePositioned = undefined;
+      element.style.position =
+        element.style.top =
+        element.style.left =
+        element.style.bottom =
+        element.style.right = '';
+    }
+    return element;
+  },
+
+  makeClipping: function(element) {
+    element = $(element);
+    if (element._overflow) return element;
+    element._overflow = element.style.overflow || 'auto';
+    if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
+      element.style.overflow = 'hidden';
+    return element;
+  },
+
+  undoClipping: function(element) {
+    element = $(element);
+    if (!element._overflow) return element;
+    element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
+    element._overflow = null;
+    return element;
+  }
+};
+
+Object.extend(Element.Methods, {
+  childOf: Element.Methods.descendantOf,
+  childElements: Element.Methods.immediateDescendants
+});
+
+if (Prototype.Browser.Opera) {
+  Element.Methods._getStyle = Element.Methods.getStyle;
+  Element.Methods.getStyle = function(element, style) {
+    switch(style) {
+      case 'left':
+      case 'top':
+      case 'right':
+      case 'bottom':
+        if (Element._getStyle(element, 'position') == 'static') return null;
+      default: return Element._getStyle(element, style);
+    }
+  };
+}
+else if (Prototype.Browser.IE) {
+  Element.Methods.getStyle = function(element, style) {
+    element = $(element);
+    style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
+    var value = element.style[style];
+    if (!value && element.currentStyle) value = element.currentStyle[style];
+
+    if (style == 'opacity') {
+      if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
+        if (value[1]) return parseFloat(value[1]) / 100;
+      return 1.0;
+    }
+
+    if (value == 'auto') {
+      if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
+        return element['offset'+style.capitalize()] + 'px';
+      return null;
+    }
+    return value;
+  };
+
+  Element.Methods.setOpacity = function(element, value) {
+    element = $(element);
+    var filter = element.getStyle('filter'), style = element.style;
+    if (value == 1 || value === '') {
+      style.filter = filter.replace(/alpha\([^\)]*\)/gi,'');
+      return element;
+    } else if (value < 0.00001) value = 0;
+    style.filter = filter.replace(/alpha\([^\)]*\)/gi, '') +
+      'alpha(opacity=' + (value * 100) + ')';
+    return element;
+  };
+
+  // IE is missing .innerHTML support for TABLE-related elements
+  Element.Methods.update = function(element, html) {
+    element = $(element);
+    html = typeof html == 'undefined' ? '' : html.toString();
+    var tagName = element.tagName.toUpperCase();
+    if (['THEAD','TBODY','TR','TD'].include(tagName)) {
+      var div = document.createElement('div');
+      switch (tagName) {
+        case 'THEAD':
+        case 'TBODY':
+          div.innerHTML = '<table><tbody>' +  html.stripScripts() + '</tbody></table>';
+          depth = 2;
+          break;
+        case 'TR':
+          div.innerHTML = '<table><tbody><tr>' +  html.stripScripts() + '</tr></tbody></table>';
+          depth = 3;
+          break;
+        case 'TD':
+          div.innerHTML = '<table><tbody><tr><td>' +  html.stripScripts() + '</td></tr></tbody></table>';
+          depth = 4;
+      }
+      $A(element.childNodes).each(function(node) { element.removeChild(node) });
+      depth.times(function() { div = div.firstChild });
+      $A(div.childNodes).each(function(node) { element.appendChild(node) });
+    } else {
+      element.innerHTML = html.stripScripts();
+    }
+    setTimeout(function() { html.evalScripts() }, 10);
+    return element;
+  }
+}
+else if (Prototype.Browser.Gecko) {
+  Element.Methods.setOpacity = function(element, value) {
+    element = $(element);
+    element.style.opacity = (value == 1) ? 0.999999 :
+      (value === '') ? '' : (value < 0.00001) ? 0 : value;
+    return element;
+  };
+}
+
+Element._attributeTranslations = {
+  names: {
+    colspan:   "colSpan",
+    rowspan:   "rowSpan",
+    valign:    "vAlign",
+    datetime:  "dateTime",
+    accesskey: "accessKey",
+    tabindex:  "tabIndex",
+    enctype:   "encType",
+    maxlength: "maxLength",
+    readonly:  "readOnly",
+    longdesc:  "longDesc"
+  },
+  values: {
+    _getAttr: function(element, attribute) {
+      return element.getAttribute(attribute, 2);
+    },
+    _flag: function(element, attribute) {
+      return $(element).hasAttribute(attribute) ? attribute : null;
+    },
+    style: function(element) {
+      return element.style.cssText.toLowerCase();
+    },
+    title: function(element) {
+      var node = element.getAttributeNode('title');
+      return node.specified ? node.nodeValue : null;
+    }
+  }
+};
+
+(function() {
+  Object.extend(this, {
+    href: this._getAttr,
+    src:  this._getAttr,
+    type: this._getAttr,
+    disabled: this._flag,
+    checked:  this._flag,
+    readonly: this._flag,
+    multiple: this._flag
+  });
+}).call(Element._attributeTranslations.values);
+
+Element.Methods.Simulated = {
+  hasAttribute: function(element, attribute) {
+    var t = Element._attributeTranslations, node;
+    attribute = t.names[attribute] || attribute;
+    node = $(element).getAttributeNode(attribute);
+    return node && node.specified;
+  }
+};
+
+Element.Methods.ByTag = {};
+
+Object.extend(Element, Element.Methods);
+
+if (!Prototype.BrowserFeatures.ElementExtensions &&
+ document.createElement('div').__proto__) {
+  window.HTMLElement = {};
+  window.HTMLElement.prototype = document.createElement('div').__proto__;
+  Prototype.BrowserFeatures.ElementExtensions = true;
+}
+
+Element.hasAttribute = function(element, attribute) {
+  if (element.hasAttribute) return element.hasAttribute(attribute);
+  return Element.Methods.Simulated.hasAttribute(element, attribute);
+};
+
+Element.addMethods = function(methods) {
+  var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
+
+  if (!methods) {
+    Object.extend(Form, Form.Methods);
+    Object.extend(Form.Element, Form.Element.Methods);
+    Object.extend(Element.Methods.ByTag, {
+      "FORM":     Object.clone(Form.Methods),
+      "INPUT":    Object.clone(Form.Element.Methods),
+      "SELECT":   Object.clone(Form.Element.Methods),
+      "TEXTAREA": Object.clone(Form.Element.Methods)
+    });
+  }
+
+  if (arguments.length == 2) {
+    var tagName = methods;
+    methods = arguments[1];
+  }
+
+  if (!tagName) Object.extend(Element.Methods, methods || {});
+  else {
+    if (tagName.constructor == Array) tagName.each(extend);
+    else extend(tagName);
+  }
+
+  function extend(tagName) {
+    tagName = tagName.toUpperCase();
+    if (!Element.Methods.ByTag[tagName])
+      Element.Methods.ByTag[tagName] = {};
+    Object.extend(Element.Methods.ByTag[tagName], methods);
+  }
+
+  function copy(methods, destination, onlyIfAbsent) {
+    onlyIfAbsent = onlyIfAbsent || false;
+    var cache = Element.extend.cache;
+    for (var property in methods) {
+      var value = methods[property];
+      if (!onlyIfAbsent || !(property in destination))
+        destination[property] = cache.findOrStore(value);
+    }
+  }
+
+  function findDOMClass(tagName) {
+    var klass;
+    var trans = {
+      "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
+      "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
+      "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
+      "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
+      "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
+      "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
+      "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
+      "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
+      "FrameSet", "IFRAME": "IFrame"
+    };
+    if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
+    if (window[klass]) return window[klass];
+    klass = 'HTML' + tagName + 'Element';
+    if (window[klass]) return window[klass];
+    klass = 'HTML' + tagName.capitalize() + 'Element';
+    if (window[klass]) return window[klass];
+
+    window[klass] = {};
+    window[klass].prototype = document.createElement(tagName).__proto__;
+    return window[klass];
+  }
+
+  if (F.ElementExtensions) {
+    copy(Element.Methods, HTMLElement.prototype);
+    copy(Element.Methods.Simulated, HTMLElement.prototype, true);
+  }
+
+  if (F.SpecificElementExtensions) {
+    for (var tag in Element.Methods.ByTag) {
+      var klass = findDOMClass(tag);
+      if (typeof klass == "undefined") continue;
+      copy(T[tag], klass.prototype);
+    }
+  }
+
+  Object.extend(Element, Element.Methods);
+  delete Element.ByTag;
+};
+
+var Toggle = { display: Element.toggle };
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.Insertion = function(adjacency) {
+  this.adjacency = adjacency;
+}
+
+Abstract.Insertion.prototype = {
+  initialize: function(element, content) {
+    this.element = $(element);
+    this.content = content.stripScripts();
+
+    if (this.adjacency && this.element.insertAdjacentHTML) {
+      try {
+        this.element.insertAdjacentHTML(this.adjacency, this.content);
+      } catch (e) {
+        var tagName = this.element.tagName.toUpperCase();
+        if (['TBODY', 'TR'].include(tagName)) {
+          this.insertContent(this.contentFromAnonymousTable());
+        } else {
+          throw e;
+        }
+      }
+    } else {
+      this.range = this.element.ownerDocument.createRange();
+      if (this.initializeRange) this.initializeRange();
+      this.insertContent([this.range.createContextualFragment(this.content)]);
+    }
+
+    setTimeout(function() {content.evalScripts()}, 10);
+  },
+
+  contentFromAnonymousTable: function() {
+    var div = document.createElement('div');
+    div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
+    return $A(div.childNodes[0].childNodes[0].childNodes);
+  }
+}
+
+var Insertion = new Object();
+
+Insertion.Before = Class.create();
+Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
+  initializeRange: function() {
+    this.range.setStartBefore(this.element);
+  },
+
+  insertContent: function(fragments) {
+    fragments.each((function(fragment) {
+      this.element.parentNode.insertBefore(fragment, this.element);
+    }).bind(this));
+  }
+});
+
+Insertion.Top = Class.create();
+Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
+  initializeRange: function() {
+    this.range.selectNodeContents(this.element);
+    this.range.collapse(true);
+  },
+
+  insertContent: function(fragments) {
+    fragments.reverse(false).each((function(fragment) {
+      this.element.insertBefore(fragment, this.element.firstChild);
+    }).bind(this));
+  }
+});
+
+Insertion.Bottom = Class.create();
+Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
+  initializeRange: function() {
+    this.range.selectNodeContents(this.element);
+    this.range.collapse(this.element);
+  },
+
+  insertContent: function(fragments) {
+    fragments.each((function(fragment) {
+      this.element.appendChild(fragment);
+    }).bind(this));
+  }
+});
+
+Insertion.After = Class.create();
+Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
+  initializeRange: function() {
+    this.range.setStartAfter(this.element);
+  },
+
+  insertContent: function(fragments) {
+    fragments.each((function(fragment) {
+      this.element.parentNode.insertBefore(fragment,
+        this.element.nextSibling);
+    }).bind(this));
+  }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Element.ClassNames = Class.create();
+Element.ClassNames.prototype = {
+  initialize: function(element) {
+    this.element = $(element);
+  },
+
+  _each: function(iterator) {
+    this.element.className.split(/\s+/).select(function(name) {
+      return name.length > 0;
+    })._each(iterator);
+  },
+
+  set: function(className) {
+    this.element.className = className;
+  },
+
+  add: function(classNameToAdd) {
+    if (this.include(classNameToAdd)) return;
+    this.set($A(this).concat(classNameToAdd).join(' '));
+  },
+
+  remove: function(classNameToRemove) {
+    if (!this.include(classNameToRemove)) return;
+    this.set($A(this).without(classNameToRemove).join(' '));
+  },
+
+  toString: function() {
+    return $A(this).join(' ');
+  }
+};
+
+Object.extend(Element.ClassNames.prototype, Enumerable);
+/* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
+ * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
+ * license.  Please see http://www.yui-ext.com/ for more information. */
+
+var Selector = Class.create();
+
+Selector.prototype = {
+  initialize: function(expression) {
+    this.expression = expression.strip();
+    this.compileMatcher();
+  },
+
+  compileMatcher: function() {
+    // Selectors with namespaced attributes can't use the XPath version
+    if (Prototype.BrowserFeatures.XPath && !(/\[[\w-]*?:/).test(this.expression))
+      return this.compileXPathMatcher();
+
+    var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
+        c = Selector.criteria, le, p, m;
+
+    if (Selector._cache[e]) {
+      this.matcher = Selector._cache[e]; return;
+    }
+    this.matcher = ["this.matcher = function(root) {",
+                    "var r = root, h = Selector.handlers, c = false, n;"];
+
+    while (e && le != e && (/\S/).test(e)) {
+      le = e;
+      for (var i in ps) {
+        p = ps[i];
+        if (m = e.match(p)) {
+          this.matcher.push(typeof c[i] == 'function' ? c[i](m) :
+             new Template(c[i]).evaluate(m));
+          e = e.replace(m[0], '');
+          break;
+        }
+      }
+    }
+
+    this.matcher.push("return h.unique(n);\n}");
+    eval(this.matcher.join('\n'));
+    Selector._cache[this.expression] = this.matcher;
+  },
+
+  compileXPathMatcher: function() {
+    var e = this.expression, ps = Selector.patterns,
+        x = Selector.xpath, le,  m;
+
+    if (Selector._cache[e]) {
+      this.xpath = Selector._cache[e]; return;
+    }
+
+    this.matcher = ['.//*'];
+    while (e && le != e && (/\S/).test(e)) {
+      le = e;
+      for (var i in ps) {
+        if (m = e.match(ps[i])) {
+          this.matcher.push(typeof x[i] == 'function' ? x[i](m) :
+            new Template(x[i]).evaluate(m));
+          e = e.replace(m[0], '');
+          break;
+        }
+      }
+    }
+
+    this.xpath = this.matcher.join('');
+    Selector._cache[this.expression] = this.xpath;
+  },
+
+  findElements: function(root) {
+    root = root || document;
+    if (this.xpath) return document._getElementsByXPath(this.xpath, root);
+    return this.matcher(root);
+  },
+
+  match: function(element) {
+    return this.findElements(document).include(element);
+  },
+
+  toString: function() {
+    return this.expression;
+  },
+
+  inspect: function() {
+    return "#<Selector:" + this.expression.inspect() + ">";
+  }
+};
+
+Object.extend(Selector, {
+  _cache: {},
+
+  xpath: {
+    descendant:   "//*",
+    child:        "/*",
+    adjacent:     "/following-sibling::*[1]",
+    laterSibling: '/following-sibling::*',
+    tagName:      function(m) {
+      if (m[1] == '*') return '';
+      return "[local-name()='" + m[1].toLowerCase() +
+             "' or local-name()='" + m[1].toUpperCase() + "']";
+    },
+    className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]",
+    id:           "[@id='#{1}']",
+    attrPresence: "[@#{1}]",
+    attr: function(m) {
+      m[3] = m[5] || m[6];
+      return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
+    },
+    pseudo: function(m) {
+      var h = Selector.xpath.pseudos[m[1]];
+      if (!h) return '';
+      if (typeof h === 'function') return h(m);
+      return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
+    },
+    operators: {
+      '=':  "[@#{1}='#{3}']",
+      '!=': "[@#{1}!='#{3}']",
+      '^=': "[starts-with(@#{1}, '#{3}')]",
+      '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
+      '*=': "[contains(@#{1}, '#{3}')]",
+      '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
+      '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
+    },
+    pseudos: {
+      'first-child': '[not(preceding-sibling::*)]',
+      'last-child':  '[not(following-sibling::*)]',
+      'only-child':  '[not(preceding-sibling::* or following-sibling::*)]',
+      'empty':       "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
+      'checked':     "[@checked]",
+      'disabled':    "[@disabled]",
+      'enabled':     "[not(@disabled)]",
+      'not': function(m) {
+        var e = m[6], p = Selector.patterns,
+            x = Selector.xpath, le, m, v;
+
+        var exclusion = [];
+        while (e && le != e && (/\S/).test(e)) {
+          le = e;
+          for (var i in p) {
+            if (m = e.match(p[i])) {
+              v = typeof x[i] == 'function' ? x[i](m) : new Template(x[i]).evaluate(m);
+              exclusion.push("(" + v.substring(1, v.length - 1) + ")");
+              e = e.replace(m[0], '');
+              break;
+            }
+          }
+        }
+        return "[not(" + exclusion.join(" and ") + ")]";
+      },
+      'nth-child':      function(m) {
+        return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
+      },
+      'nth-last-child': function(m) {
+        return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
+      },
+      'nth-of-type':    function(m) {
+        return Selector.xpath.pseudos.nth("position() ", m);
+      },
+      'nth-last-of-type': function(m) {
+        return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
+      },
+      'first-of-type':  function(m) {
+        m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
+      },
+      'last-of-type':   function(m) {
+        m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
+      },
+      'only-of-type':   function(m) {
+        var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
+      },
+      nth: function(fragment, m) {
+        var mm, formula = m[6], predicate;
+        if (formula == 'even') formula = '2n+0';
+        if (formula == 'odd')  formula = '2n+1';
+        if (mm = formula.match(/^(\d+)$/)) // digit only
+          return '[' + fragment + "= " + mm[1] + ']';
+        if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+          if (mm[1] == "-") mm[1] = -1;
+          var a = mm[1] ? Number(mm[1]) : 1;
+          var b = mm[2] ? Number(mm[2]) : 0;
+          predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
+          "((#{fragment} - #{b}) div #{a} >= 0)]";
+          return new Template(predicate).evaluate({
+            fragment: fragment, a: a, b: b });
+        }
+      }
+    }
+  },
+
+  criteria: {
+    tagName:      'n = h.tagName(n, r, "#{1}", c);   c = false;',
+    className:    'n = h.className(n, r, "#{1}", c); c = false;',
+    id:           'n = h.id(n, r, "#{1}", c);        c = false;',
+    attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;',
+    attr: function(m) {
+      m[3] = (m[5] || m[6]);
+      return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m);
+    },
+    pseudo:       function(m) {
+      if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
+      return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
+    },
+    descendant:   'c = "descendant";',
+    child:        'c = "child";',
+    adjacent:     'c = "adjacent";',
+    laterSibling: 'c = "laterSibling";'
+  },
+
+  patterns: {
+    // combinators must be listed first
+    // (and descendant needs to be last combinator)
+    laterSibling: /^\s*~\s*/,
+    child:        /^\s*>\s*/,
+    adjacent:     /^\s*\+\s*/,
+    descendant:   /^\s/,
+
+    // selectors follow
+    tagName:      /^\s*(\*|[\w\-]+)(\b|$)?/,
+    id:           /^#([\w\-\*]+)(\b|$)/,
+    className:    /^\.([\w\-\*]+)(\b|$)/,
+    pseudo:       /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|\s|(?=:))/,
+    attrPresence: /^\[([\w]+)\]/,
+    attr:         /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\]]*?)\4|([^'"][^\]]*?)))?\]/
+  },
+
+  handlers: {
+    // UTILITY FUNCTIONS
+    // joins two collections
+    concat: function(a, b) {
+      for (var i = 0, node; node = b[i]; i++)
+        a.push(node);
+      return a;
+    },
+
+    // marks an array of nodes for counting
+    mark: function(nodes) {
+      for (var i = 0, node; node = nodes[i]; i++)
+        node._counted = true;
+      return nodes;
+    },
+
+    unmark: function(nodes) {
+      for (var i = 0, node; node = nodes[i]; i++)
+        node._counted = undefined;
+      return nodes;
+    },
+
+    // mark each child node with its position (for nth calls)
+    // "ofType" flag indicates whether we're indexing for nth-of-type
+    // rather than nth-child
+    index: function(parentNode, reverse, ofType) {
+      parentNode._counted = true;
+      if (reverse) {
+        for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
+          node = nodes[i];
+          if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
+        }
+      } else {
+        for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
+          if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
+      }
+    },
+
+    // filters out duplicates and extends all nodes
+    unique: function(nodes) {
+      if (nodes.length == 0) return nodes;
+      var results = [], n;
+      for (var i = 0, l = nodes.length; i < l; i++)
+        if (!(n = nodes[i])._counted) {
+          n._counted = true;
+          results.push(Element.extend(n));
+        }
+      return Selector.handlers.unmark(results);
+    },
+
+    // COMBINATOR FUNCTIONS
+    descendant: function(nodes) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        h.concat(results, node.getElementsByTagName('*'));
+      return results;
+    },
+
+    child: function(nodes) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        for (var j = 0, children = [], child; child = node.childNodes[j]; j++)
+          if (child.nodeType == 1 && child.tagName != '!') results.push(child);
+      }
+      return results;
+    },
+
+    adjacent: function(nodes) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        var next = this.nextElementSibling(node);
+        if (next) results.push(next);
+      }
+      return results;
+    },
+
+    laterSibling: function(nodes) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        h.concat(results, Element.nextSiblings(node));
+      return results;
+    },
+
+    nextElementSibling: function(node) {
+      while (node = node.nextSibling)
+             if (node.nodeType == 1) return node;
+      return null;
+    },
+
+    previousElementSibling: function(node) {
+      while (node = node.previousSibling)
+        if (node.nodeType == 1) return node;
+      return null;
+    },
+
+    // TOKEN FUNCTIONS
+    tagName: function(nodes, root, tagName, combinator) {
+      tagName = tagName.toUpperCase();
+      var results = [], h = Selector.handlers;
+      if (nodes) {
+        if (combinator) {
+          // fastlane for ordinary descendant combinators
+          if (combinator == "descendant") {
+            for (var i = 0, node; node = nodes[i]; i++)
+              h.concat(results, node.getElementsByTagName(tagName));
+            return results;
+          } else nodes = this[combinator](nodes);
+          if (tagName == "*") return nodes;
+        }
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node.tagName.toUpperCase() == tagName) results.push(node);
+        return results;
+      } else return root.getElementsByTagName(tagName);
+    },
+
+    id: function(nodes, root, id, combinator) {
+      var targetNode = $(id), h = Selector.handlers;
+      if (!nodes && root == document) return targetNode ? [targetNode] : [];
+      if (nodes) {
+        if (combinator) {
+          if (combinator == 'child') {
+            for (var i = 0, node; node = nodes[i]; i++)
+              if (targetNode.parentNode == node) return [targetNode];
+          } else if (combinator == 'descendant') {
+            for (var i = 0, node; node = nodes[i]; i++)
+              if (Element.descendantOf(targetNode, node)) return [targetNode];
+          } else if (combinator == 'adjacent') {
+            for (var i = 0, node; node = nodes[i]; i++)
+              if (Selector.handlers.previousElementSibling(targetNode) == node)
+                return [targetNode];
+          } else nodes = h[combinator](nodes);
+        }
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node == targetNode) return [targetNode];
+        return [];
+      }
+      return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
+    },
+
+    className: function(nodes, root, className, combinator) {
+      if (nodes && combinator) nodes = this[combinator](nodes);
+      return Selector.handlers.byClassName(nodes, root, className);
+    },
+
+    byClassName: function(nodes, root, className) {
+      if (!nodes) nodes = Selector.handlers.descendant([root]);
+      var needle = ' ' + className + ' ';
+      for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
+        nodeClassName = node.className;
+        if (nodeClassName.length == 0) continue;
+        if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
+          results.push(node);
+      }
+      return results;
+    },
+
+    attrPresence: function(nodes, root, attr) {
+      var results = [];
+      for (var i = 0, node; node = nodes[i]; i++)
+        if (Element.hasAttribute(node, attr)) results.push(node);
+      return results;
+    },
+
+    attr: function(nodes, root, attr, value, operator) {
+      if (!nodes) nodes = root.getElementsByTagName("*");
+      var handler = Selector.operators[operator], results = [];
+      for (var i = 0, node; node = nodes[i]; i++) {
+        var nodeValue = Element.readAttribute(node, attr);
+        if (nodeValue === null) continue;
+        if (handler(nodeValue, value)) results.push(node);
+      }
+      return results;
+    },
+
+    pseudo: function(nodes, name, value, root, combinator) {
+      if (nodes && combinator) nodes = this[combinator](nodes);
+      if (!nodes) nodes = root.getElementsByTagName("*");
+      return Selector.pseudos[name](nodes, value, root);
+    }
+  },
+
+  pseudos: {
+    'first-child': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        if (Selector.handlers.previousElementSibling(node)) continue;
+          results.push(node);
+      }
+      return results;
+    },
+    'last-child': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        if (Selector.handlers.nextElementSibling(node)) continue;
+          results.push(node);
+      }
+      return results;
+    },
+    'only-child': function(nodes, value, root) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
+          results.push(node);
+      return results;
+    },
+    'nth-child':        function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root);
+    },
+    'nth-last-child':   function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root, true);
+    },
+    'nth-of-type':      function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root, false, true);
+    },
+    'nth-last-of-type': function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root, true, true);
+    },
+    'first-of-type':    function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, "1", root, false, true);
+    },
+    'last-of-type':     function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, "1", root, true, true);
+    },
+    'only-of-type':     function(nodes, formula, root) {
+      var p = Selector.pseudos;
+      return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
+    },
+
+    // handles the an+b logic
+    getIndices: function(a, b, total) {
+      if (a == 0) return b > 0 ? [b] : [];
+      return $R(1, total).inject([], function(memo, i) {
+        if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
+        return memo;
+      });
+    },
+
+    // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
+    nth: function(nodes, formula, root, reverse, ofType) {
+      if (nodes.length == 0) return [];
+      if (formula == 'even') formula = '2n+0';
+      if (formula == 'odd')  formula = '2n+1';
+      var h = Selector.handlers, results = [], indexed = [], m;
+      h.mark(nodes);
+      for (var i = 0, node; node = nodes[i]; i++) {
+        if (!node.parentNode._counted) {
+          h.index(node.parentNode, reverse, ofType);
+          indexed.push(node.parentNode);
+        }
+      }
+      if (formula.match(/^\d+$/)) { // just a number
+        formula = Number(formula);
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node.nodeIndex == formula) results.push(node);
+      } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+        if (m[1] == "-") m[1] = -1;
+        var a = m[1] ? Number(m[1]) : 1;
+        var b = m[2] ? Number(m[2]) : 0;
+        var indices = Selector.pseudos.getIndices(a, b, nodes.length);
+        for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
+          for (var j = 0; j < l; j++)
+            if (node.nodeIndex == indices[j]) results.push(node);
+        }
+      }
+      h.unmark(nodes);
+      h.unmark(indexed);
+      return results;
+    },
+
+    'empty': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        // IE treats comments as element nodes
+        if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
+        results.push(node);
+      }
+      return results;
+    },
+
+    'not': function(nodes, selector, root) {
+      var h = Selector.handlers, selectorType, m;
+      var exclusions = new Selector(selector).findElements(root);
+      h.mark(exclusions);
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (!node._counted) results.push(node);
+      h.unmark(exclusions);
+      return results;
+    },
+
+    'enabled': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (!node.disabled) results.push(node);
+      return results;
+    },
+
+    'disabled': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (node.disabled) results.push(node);
+      return results;
+    },
+
+    'checked': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (node.checked) results.push(node);
+      return results;
+    }
+  },
+
+  operators: {
+    '=':  function(nv, v) { return nv == v; },
+    '!=': function(nv, v) { return nv != v; },
+    '^=': function(nv, v) { return nv.startsWith(v); },
+    '$=': function(nv, v) { return nv.endsWith(v); },
+    '*=': function(nv, v) { return nv.include(v); },
+    '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
+    '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
+  },
+
+  matchElements: function(elements, expression) {
+    var matches = new Selector(expression).findElements(), h = Selector.handlers;
+    h.mark(matches);
+    for (var i = 0, results = [], element; element = elements[i]; i++)
+      if (element._counted) results.push(element);
+    h.unmark(matches);
+    return results;
+  },
+
+  findElement: function(elements, expression, index) {
+    if (typeof expression == 'number') {
+      index = expression; expression = false;
+    }
+    return Selector.matchElements(elements, expression || '*')[index || 0];
+  },
+
+  findChildElements: function(element, expressions) {
+    var exprs = expressions.join(','), expressions = [];
+    exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
+      expressions.push(m[1].strip());
+    });
+    var results = [], h = Selector.handlers;
+    for (var i = 0, l = expressions.length, selector; i < l; i++) {
+      selector = new Selector(expressions[i].strip());
+      h.concat(results, selector.findElements(element));
+    }
+    return (l > 1) ? h.unique(results) : results;
+  }
+});
+
+function $$() {
+  return Selector.findChildElements(document, $A(arguments));
+}
+var Form = {
+  reset: function(form) {
+    $(form).reset();
+    return form;
+  },
+
+  serializeElements: function(elements, getHash) {
+    var data = elements.inject({}, function(result, element) {
+      if (!element.disabled && element.name) {
+        var key = element.name, value = $(element).getValue();
+        if (value != null) {
+               if (key in result) {
+            if (result[key].constructor != Array) result[key] = [result[key]];
+            result[key].push(value);
+          }
+          else result[key] = value;
+        }
+      }
+      return result;
+    });
+
+    return getHash ? data : Hash.toQueryString(data);
+  }
+};
+
+Form.Methods = {
+  serialize: function(form, getHash) {
+    return Form.serializeElements(Form.getElements(form), getHash);
+  },
+
+  getElements: function(form) {
+    return $A($(form).getElementsByTagName('*')).inject([],
+      function(elements, child) {
+        if (Form.Element.Serializers[child.tagName.toLowerCase()])
+          elements.push(Element.extend(child));
+        return elements;
+      }
+    );
+  },
+
+  getInputs: function(form, typeName, name) {
+    form = $(form);
+    var inputs = form.getElementsByTagName('input');
+
+    if (!typeName && !name) return $A(inputs).map(Element.extend);
+
+    for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
+      var input = inputs[i];
+      if ((typeName && input.type != typeName) || (name && input.name != name))
+        continue;
+      matchingInputs.push(Element.extend(input));
+    }
+
+    return matchingInputs;
+  },
+
+  disable: function(form) {
+    form = $(form);
+    Form.getElements(form).invoke('disable');
+    return form;
+  },
+
+  enable: function(form) {
+    form = $(form);
+    Form.getElements(form).invoke('enable');
+    return form;
+  },
+
+  findFirstElement: function(form) {
+    return $(form).getElements().find(function(element) {
+      return element.type != 'hidden' && !element.disabled &&
+        ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
+    });
+  },
+
+  focusFirstElement: function(form) {
+    form = $(form);
+    form.findFirstElement().activate();
+    return form;
+  },
+
+  request: function(form, options) {
+    form = $(form), options = Object.clone(options || {});
+
+    var params = options.parameters;
+    options.parameters = form.serialize(true);
+
+    if (params) {
+      if (typeof params == 'string') params = params.toQueryParams();
+      Object.extend(options.parameters, params);
+    }
+
+    if (form.hasAttribute('method') && !options.method)
+      options.method = form.method;
+
+    return new Ajax.Request(form.readAttribute('action'), options);
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element = {
+  focus: function(element) {
+    $(element).focus();
+    return element;
+  },
+
+  select: function(element) {
+    $(element).select();
+    return element;
+  }
+}
+
+Form.Element.Methods = {
+  serialize: function(element) {
+    element = $(element);
+    if (!element.disabled && element.name) {
+      var value = element.getValue();
+      if (value != undefined) {
+        var pair = {};
+        pair[element.name] = value;
+        return Hash.toQueryString(pair);
+      }
+    }
+    return '';
+  },
+
+  getValue: function(element) {
+    element = $(element);
+    var method = element.tagName.toLowerCase();
+    return Form.Element.Serializers[method](element);
+  },
+
+  clear: function(element) {
+    $(element).value = '';
+    return element;
+  },
+
+  present: function(element) {
+    return $(element).value != '';
+  },
+
+  activate: function(element) {
+    element = $(element);
+    try {
+      element.focus();
+      if (element.select && (element.tagName.toLowerCase() != 'input' ||
+        !['button', 'reset', 'submit'].include(element.type)))
+        element.select();
+    } catch (e) {}
+    return element;
+  },
+
+  disable: function(element) {
+    element = $(element);
+    element.blur();
+    element.disabled = true;
+    return element;
+  },
+
+  enable: function(element) {
+    element = $(element);
+    element.disabled = false;
+    return element;
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Field = Form.Element;
+var $F = Form.Element.Methods.getValue;
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element.Serializers = {
+  input: function(element) {
+    switch (element.type.toLowerCase()) {
+      case 'checkbox':
+      case 'radio':
+        return Form.Element.Serializers.inputSelector(element);
+      default:
+        return Form.Element.Serializers.textarea(element);
+    }
+  },
+
+  inputSelector: function(element) {
+    return element.checked ? element.value : null;
+  },
+
+  textarea: function(element) {
+    return element.value;
+  },
+
+  select: function(element) {
+    return this[element.type == 'select-one' ?
+      'selectOne' : 'selectMany'](element);
+  },
+
+  selectOne: function(element) {
+    var index = element.selectedIndex;
+    return index >= 0 ? this.optionValue(element.options[index]) : null;
+  },
+
+  selectMany: function(element) {
+    var values, length = element.length;
+    if (!length) return null;
+
+    for (var i = 0, values = []; i < length; i++) {
+      var opt = element.options[i];
+      if (opt.selected) values.push(this.optionValue(opt));
+    }
+    return values;
+  },
+
+  optionValue: function(opt) {
+    // extend element because hasAttribute may not be native
+    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.TimedObserver = function() {}
+Abstract.TimedObserver.prototype = {
+  initialize: function(element, frequency, callback) {
+    this.frequency = frequency;
+    this.element   = $(element);
+    this.callback  = callback;
+
+    this.lastValue = this.getValue();
+    this.registerCallback();
+  },
+
+  registerCallback: function() {
+    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+  },
+
+  onTimerEvent: function() {
+    var value = this.getValue();
+    var changed = ('string' == typeof this.lastValue && 'string' == typeof value
+      ? this.lastValue != value : String(this.lastValue) != String(value));
+    if (changed) {
+      this.callback(this.element, value);
+      this.lastValue = value;
+    }
+  }
+}
+
+Form.Element.Observer = Class.create();
+Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+  getValue: function() {
+    return Form.Element.getValue(this.element);
+  }
+});
+
+Form.Observer = Class.create();
+Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+  getValue: function() {
+    return Form.serialize(this.element);
+  }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.EventObserver = function() {}
+Abstract.EventObserver.prototype = {
+  initialize: function(element, callback) {
+    this.element  = $(element);
+    this.callback = callback;
+
+    this.lastValue = this.getValue();
+    if (this.element.tagName.toLowerCase() == 'form')
+      this.registerFormCallbacks();
+    else
+      this.registerCallback(this.element);
+  },
+
+  onElementEvent: function() {
+    var value = this.getValue();
+    if (this.lastValue != value) {
+      this.callback(this.element, value);
+      this.lastValue = value;
+    }
+  },
+
+  registerFormCallbacks: function() {
+    Form.getElements(this.element).each(this.registerCallback.bind(this));
+  },
+
+  registerCallback: function(element) {
+    if (element.type) {
+      switch (element.type.toLowerCase()) {
+        case 'checkbox':
+        case 'radio':
+          Event.observe(element, 'click', this.onElementEvent.bind(this));
+          break;
+        default:
+          Event.observe(element, 'change', this.onElementEvent.bind(this));
+          break;
+      }
+    }
+  }
+}
+
+Form.Element.EventObserver = Class.create();
+Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+  getValue: function() {
+    return Form.Element.getValue(this.element);
+  }
+});
+
+Form.EventObserver = Class.create();
+Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+  getValue: function() {
+    return Form.serialize(this.element);
+  }
+});
+if (!window.Event) {
+  var Event = new Object();
+}
+
+Object.extend(Event, {
+  KEY_BACKSPACE: 8,
+  KEY_TAB:       9,
+  KEY_RETURN:   13,
+  KEY_ESC:      27,
+  KEY_LEFT:     37,
+  KEY_UP:       38,
+  KEY_RIGHT:    39,
+  KEY_DOWN:     40,
+  KEY_DELETE:   46,
+  KEY_HOME:     36,
+  KEY_END:      35,
+  KEY_PAGEUP:   33,
+  KEY_PAGEDOWN: 34,
+
+  element: function(event) {
+    return $(event.target || event.srcElement);
+  },
+
+  isLeftClick: function(event) {
+    return (((event.which) && (event.which == 1)) ||
+            ((event.button) && (event.button == 1)));
+  },
+
+  pointerX: function(event) {
+    return event.pageX || (event.clientX +
+      (document.documentElement.scrollLeft || document.body.scrollLeft));
+  },
+
+  pointerY: function(event) {
+    return event.pageY || (event.clientY +
+      (document.documentElement.scrollTop || document.body.scrollTop));
+  },
+
+  stop: function(event) {
+    if (event.preventDefault) {
+      event.preventDefault();
+      event.stopPropagation();
+    } else {
+      event.returnValue = false;
+      event.cancelBubble = true;
+    }
+  },
+
+  // find the first node with the given tagName, starting from the
+  // node the event was triggered on; traverses the DOM upwards
+  findElement: function(event, tagName) {
+    var element = Event.element(event);
+    while (element.parentNode && (!element.tagName ||
+        (element.tagName.toUpperCase() != tagName.toUpperCase())))
+      element = element.parentNode;
+    return element;
+  },
+
+  observers: false,
+
+  _observeAndCache: function(element, name, observer, useCapture) {
+    if (!this.observers) this.observers = [];
+    if (element.addEventListener) {
+      this.observers.push([element, name, observer, useCapture]);
+      element.addEventListener(name, observer, useCapture);
+    } else if (element.attachEvent) {
+      this.observers.push([element, name, observer, useCapture]);
+      element.attachEvent('on' + name, observer);
+    }
+  },
+
+  unloadCache: function() {
+    if (!Event.observers) return;
+    for (var i = 0, length = Event.observers.length; i < length; i++) {
+      Event.stopObserving.apply(this, Event.observers[i]);
+      Event.observers[i][0] = null;
+    }
+    Event.observers = false;
+  },
+
+  observe: function(element, name, observer, useCapture) {
+    element = $(element);
+    useCapture = useCapture || false;
+
+    if (name == 'keypress' &&
+      (Prototype.Browser.WebKit || element.attachEvent))
+      name = 'keydown';
+
+    Event._observeAndCache(element, name, observer, useCapture);
+  },
+
+  stopObserving: function(element, name, observer, useCapture) {
+    element = $(element);
+    useCapture = useCapture || false;
+
+    if (name == 'keypress' &&
+        (Prototype.Browser.WebKit || element.attachEvent))
+      name = 'keydown';
+
+    if (element.removeEventListener) {
+      element.removeEventListener(name, observer, useCapture);
+    } else if (element.detachEvent) {
+      try {
+        element.detachEvent('on' + name, observer);
+      } catch (e) {}
+    }
+  }
+});
+
+/* prevent memory leaks in IE */
+if (Prototype.Browser.IE)
+  Event.observe(window, 'unload', Event.unloadCache, false);
+var Position = {
+  // set to true if needed, warning: firefox performance problems
+  // NOT neeeded for page scrolling, only if draggable contained in
+  // scrollable elements
+  includeScrollOffsets: false,
+
+  // must be called before calling withinIncludingScrolloffset, every time the
+  // page is scrolled
+  prepare: function() {
+    this.deltaX =  window.pageXOffset
+                || document.documentElement.scrollLeft
+                || document.body.scrollLeft
+                || 0;
+    this.deltaY =  window.pageYOffset
+                || document.documentElement.scrollTop
+                || document.body.scrollTop
+                || 0;
+  },
+
+  realOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.scrollTop  || 0;
+      valueL += element.scrollLeft || 0;
+      element = element.parentNode;
+    } while (element);
+    return [valueL, valueT];
+  },
+
+  cumulativeOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+    } while (element);
+    return [valueL, valueT];
+  },
+
+  positionedOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+      if (element) {
+        if(element.tagName=='BODY') break;
+        var p = Element.getStyle(element, 'position');
+        if (p == 'relative' || p == 'absolute') break;
+      }
+    } while (element);
+    return [valueL, valueT];
+  },
+
+  offsetParent: function(element) {
+    if (element.offsetParent) return element.offsetParent;
+    if (element == document.body) return element;
+
+    while ((element = element.parentNode) && element != document.body)
+      if (Element.getStyle(element, 'position') != 'static')
+        return element;
+
+    return document.body;
+  },
+
+  // caches x/y coordinate pair to use with overlap
+  within: function(element, x, y) {
+    if (this.includeScrollOffsets)
+      return this.withinIncludingScrolloffsets(element, x, y);
+    this.xcomp = x;
+    this.ycomp = y;
+    this.offset = this.cumulativeOffset(element);
+
+    return (y >= this.offset[1] &&
+            y <  this.offset[1] + element.offsetHeight &&
+            x >= this.offset[0] &&
+            x <  this.offset[0] + element.offsetWidth);
+  },
+
+  withinIncludingScrolloffsets: function(element, x, y) {
+    var offsetcache = this.realOffset(element);
+
+    this.xcomp = x + offsetcache[0] - this.deltaX;
+    this.ycomp = y + offsetcache[1] - this.deltaY;
+    this.offset = this.cumulativeOffset(element);
+
+    return (this.ycomp >= this.offset[1] &&
+            this.ycomp <  this.offset[1] + element.offsetHeight &&
+            this.xcomp >= this.offset[0] &&
+            this.xcomp <  this.offset[0] + element.offsetWidth);
+  },
+
+  // within must be called directly before
+  overlap: function(mode, element) {
+    if (!mode) return 0;
+    if (mode == 'vertical')
+      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
+        element.offsetHeight;
+    if (mode == 'horizontal')
+      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
+        element.offsetWidth;
+  },
+
+  page: function(forElement) {
+    var valueT = 0, valueL = 0;
+
+    var element = forElement;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+
+      // Safari fix
+      if (element.offsetParent == document.body)
+        if (Element.getStyle(element,'position')=='absolute') break;
+
+    } while (element = element.offsetParent);
+
+    element = forElement;
+    do {
+      if (!window.opera || element.tagName=='BODY') {
+        valueT -= element.scrollTop  || 0;
+        valueL -= element.scrollLeft || 0;
+      }
+    } while (element = element.parentNode);
+
+    return [valueL, valueT];
+  },
+
+  clone: function(source, target) {
+    var options = Object.extend({
+      setLeft:    true,
+      setTop:     true,
+      setWidth:   true,
+      setHeight:  true,
+      offsetTop:  0,
+      offsetLeft: 0
+    }, arguments[2] || {})
+
+    // find page position of source
+    source = $(source);
+    var p = Position.page(source);
+
+    // find coordinate system to use
+    target = $(target);
+    var delta = [0, 0];
+    var parent = null;
+    // delta [0,0] will do fine with position: fixed elements,
+    // position:absolute needs offsetParent deltas
+    if (Element.getStyle(target,'position') == 'absolute') {
+      parent = Position.offsetParent(target);
+      delta = Position.page(parent);
+    }
+
+    // correct by body offsets (fixes Safari)
+    if (parent == document.body) {
+      delta[0] -= document.body.offsetLeft;
+      delta[1] -= document.body.offsetTop;
+    }
+
+    // set position
+    if(options.setLeft)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
+    if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
+    if(options.setWidth)  target.style.width = source.offsetWidth + 'px';
+    if(options.setHeight) target.style.height = source.offsetHeight + 'px';
+  },
+
+  absolutize: function(element) {
+    element = $(element);
+    if (element.style.position == 'absolute') return;
+    Position.prepare();
+
+    var offsets = Position.positionedOffset(element);
+    var top     = offsets[1];
+    var left    = offsets[0];
+    var width   = element.clientWidth;
+    var height  = element.clientHeight;
+
+    element._originalLeft   = left - parseFloat(element.style.left  || 0);
+    element._originalTop    = top  - parseFloat(element.style.top || 0);
+    element._originalWidth  = element.style.width;
+    element._originalHeight = element.style.height;
+
+    element.style.position = 'absolute';
+    element.style.top    = top + 'px';
+    element.style.left   = left + 'px';
+    element.style.width  = width + 'px';
+    element.style.height = height + 'px';
+  },
+
+  relativize: function(element) {
+    element = $(element);
+    if (element.style.position == 'relative') return;
+    Position.prepare();
+
+    element.style.position = 'relative';
+    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
+    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
+
+    element.style.top    = top + 'px';
+    element.style.left   = left + 'px';
+    element.style.height = element._originalHeight;
+    element.style.width  = element._originalWidth;
+  }
+}
+
+// Safari returns margins on body which is incorrect if the child is absolutely
+// positioned.  For performance reasons, redefine Position.cumulativeOffset for
+// KHTML/WebKit only.
+if (Prototype.Browser.WebKit) {
+  Position.cumulativeOffset = function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      if (element.offsetParent == document.body)
+        if (Element.getStyle(element, 'position') == 'absolute') break;
+
+      element = element.offsetParent;
+    } while (element);
+
+    return [valueL, valueT];
+  }
+}
+
+Element.addMethods();
\ No newline at end of file
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/css/coffee-with-milk.css b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/css/coffee-with-milk.css
new file mode 100644 (file)
index 0000000..86a1998
--- /dev/null
@@ -0,0 +1,73 @@
+/*\r
+Based on "Coffee with milk" table design by Roger Johansson, 456 Berea Street\r
+www.456bereastreet.com\r
+================================================*/\r
+\r
+.ricoLG_table {\r
+       border-top:1px solid #523A0B !important;\r
+       border-right:none;\r
+       font:normal 76%/150% "Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif;\r
+       color:#000;\r
+}\r
+tr.ricoLG_hdg .ricoLG_cell, tr.ricoLG_hdg th, tr.ricoLG_hdg td {  /* td/th required for IE */\r
+       background:#EBE5D9 !important;\r
+       line-height:normal;\r
+       text-align:left;\r
+}\r
+\r
+tr.ricoLG_hdg th, tr.ricoLG_hdg td {\r
+       border-bottom:1px solid #523A0B;\r
+       background:#EBE5D9;\r
+       }\r
+\r
+tr.ricoLG_hdg th, tr.ricoLG_hdg td {\r
+  border-left: 1px solid #E0D8CD !important;\r
+}\r
+\r
+.ricoLG_bottom th, .ricoLG_bottom td {\r
+  border-left: 1px solid #FFF;\r
+}\r
+\r
+tr.ricoLG_hdg div.ricoLG_cell {\r
+       background:#EBE5D9;\r
+       font-weight:bold;\r
+       padding:0.5em 0 0.5em 0.5em;\r
+}\r
+div.ricoLG_outerDiv table a {\r
+       color:#523A0B;\r
+       text-decoration:none;\r
+       border-bottom:1px dotted;\r
+       }\r
+div.ricoLG_outerDiv tbody a:visited {\r
+       color:#444;\r
+       font-weight:normal;\r
+       }\r
+div.ricoLG_outerDiv table a:hover {\r
+       border-bottom-style:solid;\r
+       }\r
+\r
+.ricoLG_bottom div.ricoLG_oddRow {\r
+       background-color:#F7F4EE;\r
+       border-top: 1px solid #EBE5D9;\r
+       border-bottom: 1px solid #EBE5D9;\r
+}\r
+.ricoLG_bottom div.ricoLG_evenRow {\r
+       border-top: 1px solid #FFF;\r
+       border-bottom: 1px solid #FFF;\r
+}\r
+.ricoLG_selection {\r
+       background-color:#ffffee !important;\r
+       border-color:#523A0B !important;\r
+}\r
+.ricoLG_table {\r
+  border-style:none;\r
+}\r
+\r
+caption {\r
+       font-family:Georgia,Times,serif;\r
+       font-weight:normal;\r
+       font-size:1.4em;\r
+       text-align:left;\r
+       margin:0;\r
+       padding:0.5em 0.25em;\r
+       }
\ No newline at end of file
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/css/grayedout.css b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/css/grayedout.css
new file mode 100644 (file)
index 0000000..d5a0dd4
--- /dev/null
@@ -0,0 +1,68 @@
+/* -------------------------------------------------------\r
+Based on Grayed Out table design\r
+Author: Terence Ordona\r
+URL: http://www.imaputz.com/\r
+ ------------------------------------------------------- */\r
+div.ricoLG_outerDiv *, div.ricoLG_cell {\r
+  font-size: 11px;\r
+  font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;\r
+}\r
+\r
+.ricoLG_table {\r
+       border-top: 1px solid #CCC;\r
+       border-right: 1px solid #CCC;\r
+}\r
+\r
+tr.ricoLG_hdg th, tr.ricoLG_hdg td {\r
+       background-color: #FFF !important;\r
+  background: url(../images/grayedout.gif) #FFF repeat-x scroll center left;\r
+  border-bottom: 1px solid #CCC;\r
+}\r
+\r
+.ricoLG_table th, .ricoLG_table td {\r
+       border-left: 1px solid #CCC;\r
+}\r
+\r
+.ricoLG_bottom th, .ricoLG_bottom td {\r
+       border-bottom: 1px solid #CCC;\r
+}\r
+\r
+.ricoLG_bottom div.ricoLG_cell {\r
+  border-bottom: none;\r
+  padding: 5px;\r
+}\r
+\r
+tr.ricoLG_hdg .ricoLG_cell {\r
+  font-weight: normal;\r
+}\r
+\r
+div.ricoLG_outerDiv a:visited, div.ricoLG_outerDiv a:link {\r
+       color: #009;\r
+       text-decoration: none;\r
+}\r
+\r
+div.ricoLG_outerDiv a:hover {\r
+       color: #009;\r
+       text-decoration: underline;\r
+}\r
+\r
+.ricoLG_oddRow {\r
+       background-color: #EEE;\r
+}\r
+\r
+div.ricoLG_selection {\r
+       background-color: #999;\r
+       color: #FFF;\r
+}\r
+\r
+div.ricoLG_highlightDiv {\r
+       border-color: #999;\r
+}\r
+       \r
+caption {\r
+       text-align: left;\r
+       font-size: 100%;\r
+       padding: .75em;\r
+       color: #000;\r
+}\r
+\r
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/css/greenHdg.css b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/css/greenHdg.css
new file mode 100644 (file)
index 0000000..f46dfb1
--- /dev/null
@@ -0,0 +1,8 @@
+/* display grid headings with a green background */\r
+\r
+tr.ricoLG_hdg .ricoLG_cell, tr.ricoLG_hdg th, tr.ricoLG_hdg td {  /* td/th required for IE */\r
+  background-color : #cedebd !important;\r
+  color            : #000000;\r
+  font-weight      : bold;\r
+}\r
+div.ricoLG_selection { background-color: #cedebd; }\r
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/css/iegradient.css b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/css/iegradient.css
new file mode 100644 (file)
index 0000000..6d4b80e
--- /dev/null
@@ -0,0 +1,3 @@
+tr.ricoLG_hdg .ricoLG_cell, tr.ricoLG_hdg th, tr.ricoLG_hdg td {  /* td/th required for IE */\r
+  Filter: progid:DXImageTransform.Microsoft.Gradient(gradientType=0,startColorStr=white,endColorStr=Gainsboro); \r
+}\r
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/css/ricoCalendar.css b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/css/ricoCalendar.css
new file mode 100644 (file)
index 0000000..18c3ad4
--- /dev/null
@@ -0,0 +1,112 @@
+/* ricoCalendar */\r
+\r
+div.ricoCalContainer, div.ricoTreeContainer {\r
+  position:absolute;\r
+  z-index:9999;\r
+  font-size:8pt;\r
+  left:0px;\r
+  top:0px;\r
+}\r
+\r
+table.ricoCalTab {\r
+  border:1px solid #666666;\r
+}\r
+\r
+table.ricoCalTab thead a {\r
+  border:1px solid #D4D0C8;\r
+  text-decoration: none;\r
+  color:black;\r
+}\r
+\r
+table.ricoCalTab thead img {\r
+  border:none;\r
+  padding-left: 0.3em;\r
+  padding-right: 0.3em;\r
+}\r
+\r
+table.ricoCalTab thead a:hover {\r
+  border:1px solid #666666;\r
+  cursor:pointer;\r
+}\r
+\r
+table.ricoCalTab thead td {\r
+  background-color: #D4D0C8;\r
+  font-weight: bold;\r
+  text-align:center;\r
+  padding: 2px;\r
+}\r
+\r
+table.ricoCalTab tfoot td {\r
+  color:#FFF;\r
+  text-align:center;\r
+  background-color: #666666;\r
+  padding: 2px;\r
+}\r
+\r
+table.ricoCalTab tfoot span {\r
+  text-decoration: underline;\r
+  cursor:pointer;\r
+}\r
+\r
+table.ricoCalTab tbody {\r
+  background-color: white;\r
+}\r
+\r
+tr.ricoCalDayNames td {\r
+  font-weight: bold;\r
+  padding: 0px 2px 0px 2px;\r
+  text-align:right;\r
+}\r
+\r
+td.ricoCal0, td.ricoCal1, td.ricoCal2, td.ricoCal3, td.ricoCal4, td.ricoCal5, td.ricoCal6, td.ricoCalToday, td.ricoCalEmpty {\r
+  text-decoration:none;\r
+  text-align:right;\r
+  width:3em;\r
+}\r
+\r
+/* Monday-Friday */\r
+td.ricoCal1, td.ricoCal2, td.ricoCal3, td.ricoCal4, td.ricoCal5 {\r
+  cursor:pointer;\r
+  color:black;\r
+}\r
+\r
+/* Sunday, Saturday */\r
+td.ricoCal0, td.ricoCal6 {\r
+  cursor:pointer;\r
+  color:#999;\r
+}\r
+\r
+td.ricoCalToday {\r
+  cursor:pointer;\r
+  color:red;\r
+  font-weight:bold;\r
+}\r
+\r
+td.ricoCalWeekNum {\r
+  background-color: #D4D0C8;\r
+  color:black;\r
+  text-align:center;\r
+}\r
+\r
+.ricoCalMenu {\r
+  position:absolute;\r
+  background-color: #FEE;\r
+  border-bottom:1px solid #666666;\r
+  border-right:1px solid #666666;\r
+}\r
+\r
+.ricoCalMenu td {\r
+  border-top:1px solid #666666;\r
+  border-left:1px solid #666666;\r
+}\r
+\r
+.ricoCalMenu a {\r
+  display:block;\r
+  text-decoration:none;\r
+  color:black;\r
+  cursor:pointer;\r
+}\r
+\r
+.ricoCalMenu a:hover {\r
+  background-color: #FCC;\r
+}\r
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/css/ricoGrid.css b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/css/ricoGrid.css
new file mode 100644 (file)
index 0000000..6760ce6
--- /dev/null
@@ -0,0 +1,125 @@
+div.ricoLG_outerDiv {\r
+  position:relative;\r
+  /*border:thin solid blue;  /* for debugging */\r
+}\r
+\r
+div.ricoLG_innerDiv, div.ricoLG_frozenTabsDiv {\r
+  overflow:hidden;\r
+  margin:0px;\r
+  padding:0px;\r
+  position:absolute;\r
+  top:0px;\r
+}\r
+\r
+div.ricoLG_scrollDiv {\r
+  overflow:scroll;\r
+  position:relative;\r
+}\r
+\r
+div.ricoLG_scrollTabsDiv {\r
+  position:absolute;\r
+  top:0px;\r
+}\r
+\r
+div.ricoLG_resizeDiv {\r
+  position:absolute;\r
+  top:0px;\r
+  width:1px;\r
+  z-index:2;\r
+  background-color:blue;\r
+}\r
+\r
+div.ricoLG_highlightDiv {\r
+  position:absolute;\r
+  border: 2px solid black;\r
+}\r
+\r
+.ricoLG_table {\r
+  margin: 0px;\r
+  padding: 0px;\r
+  border-right: 1px solid silver;\r
+  border-top: 1px solid silver;\r
+}\r
+\r
+.ricoLG_table th, .ricoLG_table td {\r
+  border-left: 1px solid silver;\r
+}\r
+\r
+table.ricoLG_bottom {\r
+  border-top-style: none;\r
+}\r
+\r
+.ricoLG_evenRow   { }\r
+.ricoLG_oddRow    { background-color: #EEE; }\r
+.ricoLG_selection { background-color: #cedebd; }\r
+\r
+div.ricoLG_col {\r
+  overflow:hidden;\r
+  width:100px;\r
+}\r
+\r
+.ricoLG_top div.ricoLG_col {\r
+  position:relative;\r
+}\r
+\r
+.ricoLG_top div.ricoLG_Resize {\r
+  position:absolute;\r
+  width:5px;\r
+  height:100%;\r
+  top:0px;\r
+  cursor:e-resize;\r
+}\r
+\r
+.ricoLG_HdrIcon {\r
+  padding-left:2px;\r
+  padding-right:2px;\r
+}\r
+\r
+.ricoLG_bottom div.ricoLG_cell, .ricoLG_top th, .ricoLG_top td {\r
+  border-bottom: 1px solid silver;\r
+}\r
+\r
+div.ricoLG_cell {\r
+  overflow:hidden;\r
+  height:1.2em;\r
+  padding-left: 3px;\r
+  margin: 0px;\r
+  font-size: 10pt;\r
+       padding-top:3px;\r
+       padding-bottom:3px;\r
+}\r
+\r
+div.ricoLG_messageDiv {\r
+  position:absolute;\r
+  z-index:200;\r
+  border:1px solid green;\r
+  background-color:white;\r
+  font-weight:bold;\r
+  font-size:larger;\r
+  color:navy;\r
+  text-align:center;\r
+  padding:4px;\r
+}\r
+\r
+p.ricoBookmark {\r
+  margin-bottom: 3px;\r
+  font-size: 10pt;\r
+}\r
+\r
+div.alignleft {\r
+  text-align: left;\r
+}\r
+\r
+div.aligncenter {\r
+  text-align: center;\r
+}\r
+\r
+div.alignright {\r
+  text-align: right;\r
+}\r
+\r
+span.ricoSessionTimer {\r
+  background-color:black;\r
+  color:white;\r
+}\r
+\r
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/css/ricoLiveGridForms.css b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/css/ricoLiveGridForms.css
new file mode 100644 (file)
index 0000000..e43e729
--- /dev/null
@@ -0,0 +1,81 @@
+/* ricoLiveGridForms */\r
+\r
+span.ricoSaveMsg {\r
+  background-color:yellow;\r
+}\r
+\r
+span.ricoSessionTimer {\r
+  background-color:black;\r
+  color:white;\r
+}\r
+\r
+div.ricoLG_editDiv, div.ricoLG_editResponseDiv {\r
+  color:#000; background:#E8ECF3;\r
+  overflow:auto;\r
+  padding:8px;\r
+  border: 1px solid navy;\r
+  position:absolute;\r
+  font-size: 10pt;\r
+  z-index:300;\r
+  top:0px;\r
+  left:0px;\r
+}\r
+\r
+form .ricoEditLabel, form .ricoEditLabelWithHelp {\r
+  font-weight: bold;\r
+  text-align: left;\r
+  padding-right: 1em;\r
+}\r
+\r
+form .ricoEditLabelWithHelp {\r
+  color: navy;\r
+}\r
+\r
+form {\r
+  margin:0px;\r
+}\r
+\r
+.tabHeader {\r
+  height: 1.8em;
+       color : #AAA;\r
+       background: #D8E0F2;
+       font-weight : bold;
+  float: left;
+  display: inline;
+  margin-left: 2px;
+  margin-right: 2px;\r
+  text-align: center;
+  white-space:nowrap;
+  overflow:hidden;
+}\r
+\r
+.tabHover {
+       color : #666;
+  cursor: pointer;
+}
+
+.tabSelected {
+       color : #444;
+       background: #CFD4E6;
+  cursor: auto;
+}
+
+.tabContentContainer {
+  clear:both;
+}\r
+\r
+div.ricoLG_editDiv .tabContent, div.ricoLG_editDiv .noTabContent {\r
+  color:#000; background:#CFD4E6;\r
+  overflow: hidden;\r
+  padding: 4px;\r
+  white-space:nowrap;\r
+}\r
+\r
+div.ricoLG_editDiv .noTabContent {\r
+  float:left;  /* required by IE7 */\r
+}\r
+\r
+span.ricoLookup {\r
+  display:none;\r
+}\r
+\r
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/css/ricoMenu.css b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/css/ricoMenu.css
new file mode 100644 (file)
index 0000000..b694c79
--- /dev/null
@@ -0,0 +1,95 @@
+/* ricoMenu */\r
+\r
+div.ricoMenu, div.ricoMenuSafari {\r
+position: absolute;\r
+z-index: 100;\r
+border:1px solid #666;\r
+padding:2px;\r
+cursor:default;\r
+visibility: hidden;\r
+}\r
+\r
+div.ricoMenu, div.ricoMenu div.ricoMenuHeading, div.ricoMenu a {\r
+background-color:menu;\r
+color: menutext;\r
+text-decoration: none;\r
+font-family:tahoma,arial,helvetica,sans-serif;\r
+font-size: 8pt;\r
+display:block;\r
+}\r
+\r
+div.ricoMenuSafari, div.ricoMenuSafari div.ricoMenuHeading, div.ricoMenuSafari a {\r
+background-color:#EDEDED;\r
+text-decoration: none;\r
+font-family:tahoma,arial,helvetica,sans-serif;\r
+font-size: 8pt;\r
+display:block;\r
+}\r
+\r
+div.ricoMenu div.ricoMenuHeading{\r
+padding: 1px 0px;\r
+font-weight:bold;\r
+}\r
+\r
+div.ricoMenuSafari div.ricoMenuHeading{\r
+padding: 1px 0px;\r
+color: black;\r
+display: block;\r
+font-weight:bold;\r
+}\r
+\r
+div.ricoMenu .enabled {\r
+position: relative;\r
+}\r
+\r
+div.ricoMenuSafari .enabled {\r
+color: black;\r
+}\r
+\r
+div.ricoMenu .enabled, div.ricoMenu .enabled-hover, div.ricoMenuSafari .enabled, div.ricoMenuSafari .enabled-hover, div.ricoMenu .disabled, div.ricoMenuSafari .disabled {\r
+padding-left: 1em;\r
+padding-top:0.1em;\r
+padding-bottom:0.1em;\r
+z-index: 101;\r
+}\r
+\r
+div.ricoMenu .disabled, div.ricoMenuSafari .disabled {\r
+color: #999;\r
+}\r
+\r
+div.ricoMenu hr{\r
+height:1px;\r
+margin:1px;\r
+border:0;\r
+color: menu;\r
+background-color: menu;\r
+}\r
+\r
+div.ricoMenu .enabled-hover, div.ricoMenu .ricoSubMenuOpen {\r
+   background-color: Highlight;\r
+   color:            HighlightText;\r
+}\r
+\r
+div.ricoMenuSafari .enabled-hover, div.ricoMenuSafari .ricoSubMenuOpen {\r
+   background-color: #1657B8;\r
+   color:            white;\r
+}\r
+\r
+div.ricoMenu .ricoSubMenu, div.ricoMenu .ricoSubMenuOpen, div.ricoMenuSafari .ricoSubMenu, div.ricoMenuSafari .ricoSubMenuOpen {\r
+padding: 1px 0px;\r
+display: block;\r
+font-weight:bold;\r
+z-index: 101;\r
+position: relative;\r
+}\r
+\r
+div.ricoMenu div.ricoMenuBreak, div.ricoMenuSafari div.ricoMenuBreak {\r
+height:1px;\r
+margin:3px 0 3px 0;\r
+padding:0;\r
+background-color: #AAA;\r
+width:100%;\r
+line-height:5px;\r
+overflow:hidden;\r
+}\r
+\r
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/css/ricoTree.css b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/css/ricoTree.css
new file mode 100644 (file)
index 0000000..33a457c
--- /dev/null
@@ -0,0 +1,42 @@
+/* ricoTree */\r
+\r
+div.ricoTreeContainer {\r
+  background-color:#cedebd;\r
+  padding:4px;\r
+  border:1px solid black;\r
+  top:0px;\r
+  left:0px;\r
+  position:absolute;\r
+  z-index:9999;\r
+}\r
+\r
+div.ricoTree {\r
+  border:thin inset;\r
+  overflow:auto;\r
+  background-color:#FFF;\r
+}\r
+\r
+div.ricoTree p, div.ricoTree a {\r
+  margin:0px;\r
+  padding-left:0.3em;\r
+  white-space:nowrap;\r
+}\r
+\r
+div.ricoTree a {\r
+  cursor:pointer;\r
+  text-decoration:none;\r
+}\r
+\r
+div.ricoTree a:hover {\r
+  background-color:#EEE;\r
+}\r
+\r
+div.ricoTree img {\r
+  margin:0px;\r
+  padding:0px;\r
+  display:block;\r
+}\r
+\r
+div.ricoTree * {\r
+  font-size:8pt;\r
+}\r
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/css/tanChisel.css b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/css/tanChisel.css
new file mode 100644 (file)
index 0000000..706437e
--- /dev/null
@@ -0,0 +1,36 @@
+tr.ricoLG_hdg .ricoLG_cell, tr.ricoLG_hdg th, tr.ricoLG_hdg td {  /* td/th required for IE */\r
+  background-color:#e0e0c0 !important;\r
+       vertical-align:middle;\r
+}\r
+\r
+tr.ricoLG_hdg div.ricoLG_cell {\r
+       border-top: 1px solid #F0F0E8;\r
+}\r
+\r
+.ricoLG_bottom div.ricoLG_cell, .ricoLG_top th, .ricoLG_top td {\r
+       border-bottom: 1px solid #D8d0c0;;\r
+}\r
+\r
+.ricoLG_table th, .ricoLG_table td {\r
+       border-left: 1px solid #F0F0E8;\r
+       border-right: 1px solid #D8d0c0;\r
+}\r
+\r
+div.ricoMenu, div.ricoMenu div.ricoMenuHeading, div.ricoMenu .ricoSubMenu, div.ricoMenuSafari, div.ricoMenuSafari div.ricoMenuHeading, div.ricoMenuSafari .ricoSubMenu {\r
+  background-color:#f0f0e0;\r
+}\r
+\r
+div.ricoMenu, div.ricoMenu div.ricoMenuHeading, div.ricoMenu .ricoSubMenu, div.ricoMenu .ricoSubMenuOpen, div.ricoMenuSafari, div.ricoMenuSafari div.ricoMenuHeading, div.ricoMenuSafari .ricoSubMenu, div.ricoMenuSafari .ricoSubMenuOpen {\r
+       border-top: 1px solid #F0F0E8;\r
+       border-left: 1px solid #F0F0E8;\r
+       border-bottom: 1px solid #D8d0c0;;\r
+       border-right: 1px solid #D8d0c0;\r
+}\r
+\r
+.ricoLG_table {\r
+  border-style:none;\r
+}\r
+\r
+div.ricoLG_selection {\r
+  background-color:#e0e0c0;\r
+}\r
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/css/warmfall.css b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/css/warmfall.css
new file mode 100644 (file)
index 0000000..1af5155
--- /dev/null
@@ -0,0 +1,63 @@
+/* -------------------------------------------------------\r
+Based on warm fall table design\r
+Author: Mya Leigh\r
+Theme: A Warm, Fall Table - Easy to Read\r
+URL: http://www.myaleigh.com \r
+ ------------------------------------------------------- */\r
+.ricoLG_table {\r
+       border-top: 1px solid #84785e;\r
+       border-right: 1px solid #84785e;\r
+}\r
+\r
+tr.ricoLG_hdg .ricoLG_cell, tr.ricoLG_hdg th, tr.ricoLG_hdg td {  /* td/th required for IE */\r
+       background-color: #a24116 !important;\r
+  color: #ffffff !important;  \r
+}\r
+\r
+.ricoLG_table th, .ricoLG_table td {\r
+       border-left: 1px solid #84785e;\r
+}\r
+\r
+.ricoLG_bottom div.ricoLG_cell, .ricoLG_top th, .ricoLG_top td {\r
+  border-bottom: 1px solid #84785e;\r
+}\r
+\r
+tr.ricoLG_hdg .ricoLG_cell {\r
+       background-color: #a24116;\r
+  border: 0;\r
+  color: #ffffff;  \r
+       padding: .75em;\r
+  font: "Verdana", Arial, Helvetica, sans-serif;\r
+  font-weight: bold;\r
+}\r
+\r
+div.ricoLG_outerDiv a:visited, div.ricoLG_outerDiv a:link, div.ricoLG_outerDiv a:active {\r
+       color: #101011;\r
+       text-decoration: none;\r
+}\r
+\r
+div.ricoLG_outerDiv a:hover {\r
+       text-decoration: underline;\r
+}\r
+\r
+div.ricoLG_outerDiv tbody a:visited {\r
+       color:#444;\r
+}\r
+\r
+.ricoLG_oddRow {\r
+       background-color: #fffce1;\r
+       color: #101011;\r
+}\r
+\r
+.ricoLG_selection {\r
+       background-color: #a24116;\r
+       color: #ffffff;\r
+}\r
+       \r
+caption {\r
+       text-align: left;\r
+       font-size: 100%;\r
+       padding: .75em;\r
+       color: #000;\r
+}\r
+\r
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/export-owc.html b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/export-owc.html
new file mode 100644 (file)
index 0000000..8819eca
--- /dev/null
@@ -0,0 +1,34 @@
+<html>\r
+<head>\r
+<title>Export</title>\r
+<SCRIPT TYPE="text/javascript">\r
+function getdata() {\r
+  if (!window.opener || window.opener.closed) {\r
+    alert('Error! Parent window is closed');\r
+    return;\r
+  }\r
+  var divID=window.location.search;\r
+  if (divID.length<2) {\r
+    alert('Error! Invalid id');\r
+    return;\r
+  }\r
+  divID=divID.substring(1);\r
+  var oDiv=window.opener.document.getElementById(divID);\r
+  if (!oDiv) {\r
+    alert('Error! Can not find \"'+divID+'\"');\r
+    return;\r
+  }\r
+  var oSS=document.getElementById('ss')\r
+  if (!oSS) {\r
+    alert('Error! Can not find spreadsheet');\r
+    return;\r
+  }\r
+  oSS.HTMLData=oDiv.innerHTML;\r
+}\r
+window.onload=getdata;\r
+</SCRIPT>\r
+</head>\r
+<body>\r
+<object id="ss" classid="CLSID:0002E559-0000-0000-C000-000000000046" style="width:100%;height:100%"></object>\r
+</body>\r
+</html>\r
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/export-plain.html b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/export-plain.html
new file mode 100644 (file)
index 0000000..b158c58
--- /dev/null
@@ -0,0 +1,28 @@
+<html>\r
+<head>\r
+<title>Export</title>\r
+<SCRIPT TYPE="text/javascript">\r
+function getdata() {\r
+  if (!window.opener || window.opener.closed) {\r
+    alert('Error! Parent window is closed');\r
+    return;\r
+  }\r
+  var divID=window.location.search;\r
+  if (divID.length<2) {\r
+    alert('Error! Invalid id');\r
+    return;\r
+  }\r
+  divID=divID.substring(1);\r
+  var oDiv=window.opener.document.getElementById(divID);\r
+  if (!oDiv) {\r
+    alert('Error! Can not find \"'+divID+'\"');\r
+    return;\r
+  }\r
+  document.body.innerHTML=oDiv.innerHTML;\r
+}\r
+window.onload=getdata;\r
+</SCRIPT>\r
+</head>\r
+<body>\r
+</body>\r
+</html>\r
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/aline.gif b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/aline.gif
new file mode 100644 (file)
index 0000000..7f5cb43
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/aline.gif differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/calarrow.png b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/calarrow.png
new file mode 100644 (file)
index 0000000..acdcfed
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/calarrow.png differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/calendaricon.gif b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/calendaricon.gif
new file mode 100644 (file)
index 0000000..abdf6ac
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/calendaricon.gif differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/close.gif b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/close.gif
new file mode 100644 (file)
index 0000000..65c5f16
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/close.gif differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/divider.gif b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/divider.gif
new file mode 100644 (file)
index 0000000..d9863d4
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/divider.gif differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/doc.gif b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/doc.gif
new file mode 100644 (file)
index 0000000..231036d
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/doc.gif differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/dotbutton.gif b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/dotbutton.gif
new file mode 100644 (file)
index 0000000..20c85cc
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/dotbutton.gif differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/drop.gif b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/drop.gif
new file mode 100644 (file)
index 0000000..afce892
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/drop.gif differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/filtercol.gif b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/filtercol.gif
new file mode 100644 (file)
index 0000000..2e93828
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/filtercol.gif differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/folderclosed.gif b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/folderclosed.gif
new file mode 100644 (file)
index 0000000..7c61d6f
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/folderclosed.gif differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/folderopen.gif b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/folderopen.gif
new file mode 100644 (file)
index 0000000..c16e11b
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/folderopen.gif differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/grayedout.gif b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/grayedout.gif
new file mode 100644 (file)
index 0000000..ead27af
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/grayedout.gif differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/left.gif b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/left.gif
new file mode 100644 (file)
index 0000000..f652075
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/left.gif differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/link.gif b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/link.gif
new file mode 100644 (file)
index 0000000..a679f4d
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/link.gif differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/node.gif b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/node.gif
new file mode 100644 (file)
index 0000000..d2dfe4b
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/node.gif differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/nodeblank.gif b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/nodeblank.gif
new file mode 100644 (file)
index 0000000..4f1f916
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/nodeblank.gif differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/nodelast.gif b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/nodelast.gif
new file mode 100644 (file)
index 0000000..3fe0a7e
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/nodelast.gif differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/nodeline.gif b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/nodeline.gif
new file mode 100644 (file)
index 0000000..5767a1f
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/nodeline.gif differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/nodem.gif b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/nodem.gif
new file mode 100644 (file)
index 0000000..fcc2d37
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/nodem.gif differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/nodemlast.gif b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/nodemlast.gif
new file mode 100644 (file)
index 0000000..11ae43a
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/nodemlast.gif differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/nodep.gif b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/nodep.gif
new file mode 100644 (file)
index 0000000..5b68013
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/nodep.gif differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/nodeplast.gif b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/nodeplast.gif
new file mode 100644 (file)
index 0000000..b87f003
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/nodeplast.gif differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/resize.gif b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/resize.gif
new file mode 100644 (file)
index 0000000..8efd1b5
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/resize.gif differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/ricologo.gif b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/ricologo.gif
new file mode 100644 (file)
index 0000000..9b14203
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/ricologo.gif differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/right.gif b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/right.gif
new file mode 100644 (file)
index 0000000..5517f2b
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/right.gif differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/shadow.png b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/shadow.png
new file mode 100644 (file)
index 0000000..7862c9b
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/shadow.png differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/shadow_ll.png b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/shadow_ll.png
new file mode 100644 (file)
index 0000000..cc0665b
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/shadow_ll.png differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/shadow_ur.png b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/shadow_ur.png
new file mode 100644 (file)
index 0000000..cad0f2c
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/shadow_ur.png differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/sort_asc.gif b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/sort_asc.gif
new file mode 100644 (file)
index 0000000..6330232
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/sort_asc.gif differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/sort_desc.gif b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/sort_desc.gif
new file mode 100644 (file)
index 0000000..b3a681c
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/images/sort_desc.gif differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/prototype.js b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/prototype.js
new file mode 100644 (file)
index 0000000..5906575
--- /dev/null
@@ -0,0 +1,3269 @@
+/*  Prototype JavaScript framework, version 1.5.1_rc3
+ *  (c) 2005-2007 Sam Stephenson
+ *
+ *  Prototype is freely distributable under the terms of an MIT-style license.
+ *  For details, see the Prototype web site: http://www.prototypejs.org/
+ *
+/*--------------------------------------------------------------------------*/
+
+var Prototype = {
+  Version: '1.5.1_rc3',
+
+  Browser: {
+    IE:     !!(window.attachEvent && !window.opera),
+    Opera:  !!window.opera,
+    WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
+    Gecko:  navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1
+  },
+
+  BrowserFeatures: {
+    XPath: !!document.evaluate,
+    ElementExtensions: !!window.HTMLElement,
+    SpecificElementExtensions:
+      (document.createElement('div').__proto__ !==
+       document.createElement('form').__proto__)
+  },
+
+  ScriptFragment: '<script[^>]*>([\u0001-\uFFFF]*?)</script>',
+  JSONFilter: /^\/\*-secure-\s*(.*)\s*\*\/\s*$/,
+
+  emptyFunction: function() { },
+  K: function(x) { return x }
+}
+
+var Class = {
+  create: function() {
+    return function() {
+      this.initialize.apply(this, arguments);
+    }
+  }
+}
+
+var Abstract = new Object();
+
+Object.extend = function(destination, source) {
+  for (var property in source) {
+    destination[property] = source[property];
+  }
+  return destination;
+}
+
+Object.extend(Object, {
+  inspect: function(object) {
+    try {
+      if (object === undefined) return 'undefined';
+      if (object === null) return 'null';
+      return object.inspect ? object.inspect() : object.toString();
+    } catch (e) {
+      if (e instanceof RangeError) return '...';
+      throw e;
+    }
+  },
+
+  toJSON: function(object) {
+    var type = typeof object;
+    switch(type) {
+      case 'undefined':
+      case 'function':
+      case 'unknown': return;
+      case 'boolean': return object.toString();
+    }
+    if (object === null) return 'null';
+    if (object.toJSON) return object.toJSON();
+    if (object.ownerDocument === document) return;
+    var results = [];
+    for (var property in object) {
+      var value = Object.toJSON(object[property]);
+      if (value !== undefined)
+        results.push(property.toJSON() + ': ' + value);
+    }
+    return '{' + results.join(', ') + '}';
+  },
+
+  keys: function(object) {
+    var keys = [];
+    for (var property in object)
+      keys.push(property);
+    return keys;
+  },
+
+  values: function(object) {
+    var values = [];
+    for (var property in object)
+      values.push(object[property]);
+    return values;
+  },
+
+  clone: function(object) {
+    return Object.extend({}, object);
+  }
+});
+
+Function.prototype.bind = function() {
+  var __method = this, args = $A(arguments), object = args.shift();
+  return function() {
+    return __method.apply(object, args.concat($A(arguments)));
+  }
+}
+
+Function.prototype.bindAsEventListener = function(object) {
+  var __method = this, args = $A(arguments), object = args.shift();
+  return function(event) {
+    return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments)));
+  }
+}
+
+Object.extend(Number.prototype, {
+  toColorPart: function() {
+    return this.toPaddedString(2, 16);
+  },
+
+  succ: function() {
+    return this + 1;
+  },
+
+  times: function(iterator) {
+    $R(0, this, true).each(iterator);
+    return this;
+  },
+
+  toPaddedString: function(length, radix) {
+    var string = this.toString(radix || 10);
+    return '0'.times(length - string.length) + string;
+  },
+
+  toJSON: function() {
+    return isFinite(this) ? this.toString() : 'null';
+  }
+});
+
+Date.prototype.toJSON = function() {
+  return '"' + this.getFullYear() + '-' +
+    (this.getMonth() + 1).toPaddedString(2) + '-' +
+    this.getDate().toPaddedString(2) + 'T' +
+    this.getHours().toPaddedString(2) + ':' +
+    this.getMinutes().toPaddedString(2) + ':' +
+    this.getSeconds().toPaddedString(2) + '"';
+};
+
+var Try = {
+  these: function() {
+    var returnValue;
+
+    for (var i = 0, length = arguments.length; i < length; i++) {
+      var lambda = arguments[i];
+      try {
+        returnValue = lambda();
+        break;
+      } catch (e) {}
+    }
+
+    return returnValue;
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var PeriodicalExecuter = Class.create();
+PeriodicalExecuter.prototype = {
+  initialize: function(callback, frequency) {
+    this.callback = callback;
+    this.frequency = frequency;
+    this.currentlyExecuting = false;
+
+    this.registerCallback();
+  },
+
+  registerCallback: function() {
+    this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+  },
+
+  stop: function() {
+    if (!this.timer) return;
+    clearInterval(this.timer);
+    this.timer = null;
+  },
+
+  onTimerEvent: function() {
+    if (!this.currentlyExecuting) {
+      try {
+        this.currentlyExecuting = true;
+        this.callback(this);
+      } finally {
+        this.currentlyExecuting = false;
+      }
+    }
+  }
+}
+Object.extend(String, {
+  interpret: function(value) {
+    return value == null ? '' : String(value);
+  },
+  specialChar: {
+    '\b': '\\b',
+    '\t': '\\t',
+    '\n': '\\n',
+    '\f': '\\f',
+    '\r': '\\r',
+    '\\': '\\\\'
+  }
+});
+
+Object.extend(String.prototype, {
+  gsub: function(pattern, replacement) {
+    var result = '', source = this, match;
+    replacement = arguments.callee.prepareReplacement(replacement);
+
+    while (source.length > 0) {
+      if (match = source.match(pattern)) {
+        result += source.slice(0, match.index);
+        result += String.interpret(replacement(match));
+        source  = source.slice(match.index + match[0].length);
+      } else {
+        result += source, source = '';
+      }
+    }
+    return result;
+  },
+
+  sub: function(pattern, replacement, count) {
+    replacement = this.gsub.prepareReplacement(replacement);
+    count = count === undefined ? 1 : count;
+
+    return this.gsub(pattern, function(match) {
+      if (--count < 0) return match[0];
+      return replacement(match);
+    });
+  },
+
+  scan: function(pattern, iterator) {
+    this.gsub(pattern, iterator);
+    return this;
+  },
+
+  truncate: function(length, truncation) {
+    length = length || 30;
+    truncation = truncation === undefined ? '...' : truncation;
+    return this.length > length ?
+      this.slice(0, length - truncation.length) + truncation : this;
+  },
+
+  strip: function() {
+    return this.replace(/^\s+/, '').replace(/\s+$/, '');
+  },
+
+  stripTags: function() {
+    return this.replace(/<\/?[^>]+>/gi, '');
+  },
+
+  stripScripts: function() {
+    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
+  },
+
+  extractScripts: function() {
+    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
+    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+    return (this.match(matchAll) || []).map(function(scriptTag) {
+      return (scriptTag.match(matchOne) || ['', ''])[1];
+    });
+  },
+
+  evalScripts: function() {
+    return this.extractScripts().map(function(script) { return eval(script) });
+  },
+
+  escapeHTML: function() {
+    var self = arguments.callee;
+    self.text.data = this;
+    return self.div.innerHTML;
+  },
+
+  unescapeHTML: function() {
+    var div = document.createElement('div');
+    div.innerHTML = this.stripTags();
+    return div.childNodes[0] ? (div.childNodes.length > 1 ?
+      $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
+      div.childNodes[0].nodeValue) : '';
+  },
+
+  toQueryParams: function(separator) {
+    var match = this.strip().match(/([^?#]*)(#.*)?$/);
+    if (!match) return {};
+
+    return match[1].split(separator || '&').inject({}, function(hash, pair) {
+      if ((pair = pair.split('='))[0]) {
+        var key = decodeURIComponent(pair.shift());
+        var value = pair.length > 1 ? pair.join('=') : pair[0];
+        if (value != undefined) value = decodeURIComponent(value);
+
+        if (key in hash) {
+          if (hash[key].constructor != Array) hash[key] = [hash[key]];
+          hash[key].push(value);
+        }
+        else hash[key] = value;
+      }
+      return hash;
+    });
+  },
+
+  toArray: function() {
+    return this.split('');
+  },
+
+  succ: function() {
+    return this.slice(0, this.length - 1) +
+      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
+  },
+
+  times: function(count) {
+    var result = '';
+    for (var i = 0; i < count; i++) result += this;
+    return result;
+  },
+
+  camelize: function() {
+    var parts = this.split('-'), len = parts.length;
+    if (len == 1) return parts[0];
+
+    var camelized = this.charAt(0) == '-'
+      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
+      : parts[0];
+
+    for (var i = 1; i < len; i++)
+      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
+
+    return camelized;
+  },
+
+  capitalize: function() {
+    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
+  },
+
+  underscore: function() {
+    return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
+  },
+
+  dasherize: function() {
+    return this.gsub(/_/,'-');
+  },
+
+  inspect: function(useDoubleQuotes) {
+    var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
+      var character = String.specialChar[match[0]];
+      return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
+    });
+    if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
+    return "'" + escapedString.replace(/'/g, '\\\'') + "'";
+  },
+
+  toJSON: function() {
+    return this.inspect(true);
+  },
+
+  unfilterJSON: function(filter) {
+    return this.sub(filter || Prototype.JSONFilter, '#{1}');
+  },
+
+  evalJSON: function(sanitize) {
+    var json = this.unfilterJSON();
+    try {
+      if (!sanitize || (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(json)))
+        return eval('(' + json + ')');
+    } catch (e) { }
+    throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
+  },
+
+  include: function(pattern) {
+    return this.indexOf(pattern) > -1;
+  },
+
+  startsWith: function(pattern) {
+    return this.indexOf(pattern) === 0;
+  },
+
+  endsWith: function(pattern) {
+    var d = this.length - pattern.length;
+    return d >= 0 && this.lastIndexOf(pattern) === d;
+  },
+
+  empty: function() {
+    return this == '';
+  },
+
+  blank: function() {
+    return /^\s*$/.test(this);
+  }
+});
+
+if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
+  escapeHTML: function() {
+    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
+  },
+  unescapeHTML: function() {
+    return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
+  }
+});
+
+String.prototype.gsub.prepareReplacement = function(replacement) {
+  if (typeof replacement == 'function') return replacement;
+  var template = new Template(replacement);
+  return function(match) { return template.evaluate(match) };
+}
+
+String.prototype.parseQuery = String.prototype.toQueryParams;
+
+Object.extend(String.prototype.escapeHTML, {
+  div:  document.createElement('div'),
+  text: document.createTextNode('')
+});
+
+with (String.prototype.escapeHTML) div.appendChild(text);
+
+var Template = Class.create();
+Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
+Template.prototype = {
+  initialize: function(template, pattern) {
+    this.template = template.toString();
+    this.pattern  = pattern || Template.Pattern;
+  },
+
+  evaluate: function(object) {
+    return this.template.gsub(this.pattern, function(match) {
+      var before = match[1];
+      if (before == '\\') return match[2];
+      return before + String.interpret(object[match[3]]);
+    });
+  }
+}
+
+var $break    = new Object();
+var $continue = new Object();
+
+var Enumerable = {
+  each: function(iterator) {
+    var index = 0;
+    try {
+      this._each(function(value) {
+        iterator(value, index++);
+      });
+    } catch (e) {
+      if (e != $break) throw e;
+    }
+    return this;
+  },
+
+  eachSlice: function(number, iterator) {
+    var index = -number, slices = [], array = this.toArray();
+    while ((index += number) < array.length)
+      slices.push(array.slice(index, index+number));
+    return slices.map(iterator);
+  },
+
+  all: function(iterator) {
+    var result = true;
+    this.each(function(value, index) {
+      result = result && !!(iterator || Prototype.K)(value, index);
+      if (!result) throw $break;
+    });
+    return result;
+  },
+
+  any: function(iterator) {
+    var result = false;
+    this.each(function(value, index) {
+      if (result = !!(iterator || Prototype.K)(value, index))
+        throw $break;
+    });
+    return result;
+  },
+
+  collect: function(iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      results.push((iterator || Prototype.K)(value, index));
+    });
+    return results;
+  },
+
+  detect: function(iterator) {
+    var result;
+    this.each(function(value, index) {
+      if (iterator(value, index)) {
+        result = value;
+        throw $break;
+      }
+    });
+    return result;
+  },
+
+  findAll: function(iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      if (iterator(value, index))
+        results.push(value);
+    });
+    return results;
+  },
+
+  grep: function(pattern, iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      var stringValue = value.toString();
+      if (stringValue.match(pattern))
+        results.push((iterator || Prototype.K)(value, index));
+    })
+    return results;
+  },
+
+  include: function(object) {
+    var found = false;
+    this.each(function(value) {
+      if (value == object) {
+        found = true;
+        throw $break;
+      }
+    });
+    return found;
+  },
+
+  inGroupsOf: function(number, fillWith) {
+    fillWith = fillWith === undefined ? null : fillWith;
+    return this.eachSlice(number, function(slice) {
+      while(slice.length < number) slice.push(fillWith);
+      return slice;
+    });
+  },
+
+  inject: function(memo, iterator) {
+    this.each(function(value, index) {
+      memo = iterator(memo, value, index);
+    });
+    return memo;
+  },
+
+  invoke: function(method) {
+    var args = $A(arguments).slice(1);
+    return this.map(function(value) {
+      return value[method].apply(value, args);
+    });
+  },
+
+  max: function(iterator) {
+    var result;
+    this.each(function(value, index) {
+      value = (iterator || Prototype.K)(value, index);
+      if (result == undefined || value >= result)
+        result = value;
+    });
+    return result;
+  },
+
+  min: function(iterator) {
+    var result;
+    this.each(function(value, index) {
+      value = (iterator || Prototype.K)(value, index);
+      if (result == undefined || value < result)
+        result = value;
+    });
+    return result;
+  },
+
+  partition: function(iterator) {
+    var trues = [], falses = [];
+    this.each(function(value, index) {
+      ((iterator || Prototype.K)(value, index) ?
+        trues : falses).push(value);
+    });
+    return [trues, falses];
+  },
+
+  pluck: function(property) {
+    var results = [];
+    this.each(function(value, index) {
+      results.push(value[property]);
+    });
+    return results;
+  },
+
+  reject: function(iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      if (!iterator(value, index))
+        results.push(value);
+    });
+    return results;
+  },
+
+  sortBy: function(iterator) {
+    return this.map(function(value, index) {
+      return {value: value, criteria: iterator(value, index)};
+    }).sort(function(left, right) {
+      var a = left.criteria, b = right.criteria;
+      return a < b ? -1 : a > b ? 1 : 0;
+    }).pluck('value');
+  },
+
+  toArray: function() {
+    return this.map();
+  },
+
+  zip: function() {
+    var iterator = Prototype.K, args = $A(arguments);
+    if (typeof args.last() == 'function')
+      iterator = args.pop();
+
+    var collections = [this].concat(args).map($A);
+    return this.map(function(value, index) {
+      return iterator(collections.pluck(index));
+    });
+  },
+
+  size: function() {
+    return this.toArray().length;
+  },
+
+  inspect: function() {
+    return '#<Enumerable:' + this.toArray().inspect() + '>';
+  }
+}
+
+Object.extend(Enumerable, {
+  map:     Enumerable.collect,
+  find:    Enumerable.detect,
+  select:  Enumerable.findAll,
+  member:  Enumerable.include,
+  entries: Enumerable.toArray
+});
+var $A = Array.from = function(iterable) {
+  if (!iterable) return [];
+  if (iterable.toArray) {
+    return iterable.toArray();
+  } else {
+    var results = [];
+    for (var i = 0, length = iterable.length; i < length; i++)
+      results.push(iterable[i]);
+    return results;
+  }
+}
+
+if (Prototype.Browser.WebKit) {
+  $A = Array.from = function(iterable) {
+    if (!iterable) return [];
+    if (!(typeof iterable == 'function' && iterable == '[object NodeList]') &&
+      iterable.toArray) {
+      return iterable.toArray();
+    } else {
+      var results = [];
+      for (var i = 0, length = iterable.length; i < length; i++)
+        results.push(iterable[i]);
+      return results;
+    }
+  }
+}
+
+Object.extend(Array.prototype, Enumerable);
+
+if (!Array.prototype._reverse)
+  Array.prototype._reverse = Array.prototype.reverse;
+
+Object.extend(Array.prototype, {
+  _each: function(iterator) {
+    for (var i = 0, length = this.length; i < length; i++)
+      iterator(this[i]);
+  },
+
+  clear: function() {
+    this.length = 0;
+    return this;
+  },
+
+  first: function() {
+    return this[0];
+  },
+
+  last: function() {
+    return this[this.length - 1];
+  },
+
+  compact: function() {
+    return this.select(function(value) {
+      return value != null;
+    });
+  },
+
+  flatten: function() {
+    return this.inject([], function(array, value) {
+      return array.concat(value && value.constructor == Array ?
+        value.flatten() : [value]);
+    });
+  },
+
+  without: function() {
+    var values = $A(arguments);
+    return this.select(function(value) {
+      return !values.include(value);
+    });
+  },
+
+  indexOf: function(object) {
+    for (var i = 0, length = this.length; i < length; i++)
+      if (this[i] == object) return i;
+    return -1;
+  },
+
+  reverse: function(inline) {
+    return (inline !== false ? this : this.toArray())._reverse();
+  },
+
+  reduce: function() {
+    return this.length > 1 ? this : this[0];
+  },
+
+  uniq: function(sorted) {
+    return this.inject([], function(array, value, index) {
+      if (0 == index || (sorted ? array.last() != value : !array.include(value)))
+        array.push(value);
+      return array;
+    });
+  },
+
+  clone: function() {
+    return [].concat(this);
+  },
+
+  size: function() {
+    return this.length;
+  },
+
+  inspect: function() {
+    return '[' + this.map(Object.inspect).join(', ') + ']';
+  },
+
+  toJSON: function() {
+    var results = [];
+    this.each(function(object) {
+      var value = Object.toJSON(object);
+      if (value !== undefined) results.push(value);
+    });
+    return '[' + results.join(', ') + ']';
+  }
+});
+
+Array.prototype.toArray = Array.prototype.clone;
+
+function $w(string) {
+  string = string.strip();
+  return string ? string.split(/\s+/) : [];
+}
+
+if (Prototype.Browser.Opera){
+  Array.prototype.concat = function() {
+    var array = [];
+    for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
+    for (var i = 0, length = arguments.length; i < length; i++) {
+      if (arguments[i].constructor == Array) {
+        for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
+          array.push(arguments[i][j]);
+      } else {
+        array.push(arguments[i]);
+      }
+    }
+    return array;
+  }
+}
+var Hash = function(object) {
+  if (object instanceof Hash) this.merge(object);
+  else Object.extend(this, object || {});
+};
+
+Object.extend(Hash, {
+  toQueryString: function(obj) {
+    var parts = [];
+    parts.add = arguments.callee.addPair;
+
+    this.prototype._each.call(obj, function(pair) {
+      if (!pair.key) return;
+      var value = pair.value;
+
+      if (value && typeof value == 'object') {
+        if (value.constructor == Array) value.each(function(value) {
+          parts.add(pair.key, value);
+        });
+        return;
+      }
+      parts.add(pair.key, value);
+    });
+
+    return parts.join('&');
+  },
+
+  toJSON: function(object) {
+    var results = [];
+    this.prototype._each.call(object, function(pair) {
+      var value = Object.toJSON(pair.value);
+      if (value !== undefined) results.push(pair.key.toJSON() + ': ' + value);
+    });
+    return '{' + results.join(', ') + '}';
+  }
+});
+
+Hash.toQueryString.addPair = function(key, value, prefix) {
+  key = encodeURIComponent(key);
+  if (value === undefined) this.push(key);
+  else this.push(key + '=' + (value == null ? '' : encodeURIComponent(value)));
+}
+
+Object.extend(Hash.prototype, Enumerable);
+Object.extend(Hash.prototype, {
+  _each: function(iterator) {
+    for (var key in this) {
+      var value = this[key];
+      if (value && value == Hash.prototype[key]) continue;
+
+      var pair = [key, value];
+      pair.key = key;
+      pair.value = value;
+      iterator(pair);
+    }
+  },
+
+  keys: function() {
+    return this.pluck('key');
+  },
+
+  values: function() {
+    return this.pluck('value');
+  },
+
+  merge: function(hash) {
+    return $H(hash).inject(this, function(mergedHash, pair) {
+      mergedHash[pair.key] = pair.value;
+      return mergedHash;
+    });
+  },
+
+  remove: function() {
+    var result;
+    for(var i = 0, length = arguments.length; i < length; i++) {
+      var value = this[arguments[i]];
+      if (value !== undefined){
+        if (result === undefined) result = value;
+        else {
+          if (result.constructor != Array) result = [result];
+          result.push(value)
+        }
+      }
+      delete this[arguments[i]];
+    }
+    return result;
+  },
+
+  toQueryString: function() {
+    return Hash.toQueryString(this);
+  },
+
+  inspect: function() {
+    return '#<Hash:{' + this.map(function(pair) {
+      return pair.map(Object.inspect).join(': ');
+    }).join(', ') + '}>';
+  },
+
+  toJSON: function() {
+    return Hash.toJSON(this);
+  }
+});
+
+function $H(object) {
+  if (object instanceof Hash) return object;
+  return new Hash(object);
+};
+
+// Safari iterates over shadowed properties
+if (function() {
+  var i = 0, Test = function(value) { this.key = value };
+  Test.prototype.key = 'foo';
+  for (var property in new Test('bar')) i++;
+  return i > 1;
+}()) Hash.prototype._each = function(iterator) {
+  var cache = [];
+  for (var key in this) {
+    var value = this[key];
+    if ((value && value == Hash.prototype[key]) || cache.include(key)) continue;
+    cache.push(key);
+    var pair = [key, value];
+    pair.key = key;
+    pair.value = value;
+    iterator(pair);
+  }
+};
+ObjectRange = Class.create();
+Object.extend(ObjectRange.prototype, Enumerable);
+Object.extend(ObjectRange.prototype, {
+  initialize: function(start, end, exclusive) {
+    this.start = start;
+    this.end = end;
+    this.exclusive = exclusive;
+  },
+
+  _each: function(iterator) {
+    var value = this.start;
+    while (this.include(value)) {
+      iterator(value);
+      value = value.succ();
+    }
+  },
+
+  include: function(value) {
+    if (value < this.start)
+      return false;
+    if (this.exclusive)
+      return value < this.end;
+    return value <= this.end;
+  }
+});
+
+var $R = function(start, end, exclusive) {
+  return new ObjectRange(start, end, exclusive);
+}
+
+var Ajax = {
+  getTransport: function() {
+    return Try.these(
+      function() {return new XMLHttpRequest()},
+      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
+    ) || false;
+  },
+
+  activeRequestCount: 0
+}
+
+Ajax.Responders = {
+  responders: [],
+
+  _each: function(iterator) {
+    this.responders._each(iterator);
+  },
+
+  register: function(responder) {
+    if (!this.include(responder))
+      this.responders.push(responder);
+  },
+
+  unregister: function(responder) {
+    this.responders = this.responders.without(responder);
+  },
+
+  dispatch: function(callback, request, transport, json) {
+    this.each(function(responder) {
+      if (typeof responder[callback] == 'function') {
+        try {
+          responder[callback].apply(responder, [request, transport, json]);
+        } catch (e) {}
+      }
+    });
+  }
+};
+
+Object.extend(Ajax.Responders, Enumerable);
+
+Ajax.Responders.register({
+  onCreate: function() {
+    Ajax.activeRequestCount++;
+  },
+  onComplete: function() {
+    Ajax.activeRequestCount--;
+  }
+});
+
+Ajax.Base = function() {};
+Ajax.Base.prototype = {
+  setOptions: function(options) {
+    this.options = {
+      method:       'post',
+      asynchronous: true,
+      contentType:  'application/x-www-form-urlencoded',
+      encoding:     'UTF-8',
+      parameters:   ''
+    }
+    Object.extend(this.options, options || {});
+
+    this.options.method = this.options.method.toLowerCase();
+    if (typeof this.options.parameters == 'string')
+      this.options.parameters = this.options.parameters.toQueryParams();
+  }
+}
+
+Ajax.Request = Class.create();
+Ajax.Request.Events =
+  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
+  _complete: false,
+
+  initialize: function(url, options) {
+    this.transport = Ajax.getTransport();
+    this.setOptions(options);
+    this.request(url);
+  },
+
+  request: function(url) {
+    this.url = url;
+    this.method = this.options.method;
+    var params = Object.clone(this.options.parameters);
+
+    if (!['get', 'post'].include(this.method)) {
+      // simulate other verbs over post
+      params['_method'] = this.method;
+      this.method = 'post';
+    }
+
+    this.parameters = params;
+
+    if (params = Hash.toQueryString(params)) {
+      // when GET, append parameters to URL
+      if (this.method == 'get')
+        this.url += (this.url.include('?') ? '&' : '?') + params;
+      else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
+        params += '&_=';
+    }
+
+    try {
+      if (this.options.onCreate) this.options.onCreate(this.transport);
+      Ajax.Responders.dispatch('onCreate', this, this.transport);
+
+      this.transport.open(this.method.toUpperCase(), this.url,
+        this.options.asynchronous);
+
+      if (this.options.asynchronous)
+        setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
+
+      this.transport.onreadystatechange = this.onStateChange.bind(this);
+      this.setRequestHeaders();
+
+      this.body = this.method == 'post' ? (this.options.postBody || params) : null;
+      this.transport.send(this.body);
+
+      /* Force Firefox to handle ready state 4 for synchronous requests */
+      if (!this.options.asynchronous && this.transport.overrideMimeType)
+        this.onStateChange();
+
+    }
+    catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
+  onStateChange: function() {
+    var readyState = this.transport.readyState;
+    if (readyState > 1 && !((readyState == 4) && this._complete))
+      this.respondToReadyState(this.transport.readyState);
+  },
+
+  setRequestHeaders: function() {
+    var headers = {
+      'X-Requested-With': 'XMLHttpRequest',
+      'X-Prototype-Version': Prototype.Version,
+      'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
+    };
+
+    if (this.method == 'post') {
+      headers['Content-type'] = this.options.contentType +
+        (this.options.encoding ? '; charset=' + this.options.encoding : '');
+
+      /* Force "Connection: close" for older Mozilla browsers to work
+       * around a bug where XMLHttpRequest sends an incorrect
+       * Content-length header. See Mozilla Bugzilla #246651.
+       */
+      if (this.transport.overrideMimeType &&
+          (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
+            headers['Connection'] = 'close';
+    }
+
+    // user-defined headers
+    if (typeof this.options.requestHeaders == 'object') {
+      var extras = this.options.requestHeaders;
+
+      if (typeof extras.push == 'function')
+        for (var i = 0, length = extras.length; i < length; i += 2)
+          headers[extras[i]] = extras[i+1];
+      else
+        $H(extras).each(function(pair) { headers[pair.key] = pair.value });
+    }
+
+    for (var name in headers)
+      this.transport.setRequestHeader(name, headers[name]);
+  },
+
+  success: function() {
+    return !this.transport.status
+        || (this.transport.status >= 200 && this.transport.status < 300);
+  },
+
+  respondToReadyState: function(readyState) {
+    var state = Ajax.Request.Events[readyState];
+    var transport = this.transport, json = this.evalJSON();
+
+    if (state == 'Complete') {
+      try {
+        this._complete = true;
+        (this.options['on' + this.transport.status]
+         || this.options['on' + (this.success() ? 'Success' : 'Failure')]
+         || Prototype.emptyFunction)(transport, json);
+      } catch (e) {
+        this.dispatchException(e);
+      }
+
+      var contentType = this.getHeader('Content-type');
+      if (contentType && contentType.strip().
+        match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
+          this.evalResponse();
+    }
+
+    try {
+      (this.options['on' + state] || Prototype.emptyFunction)(transport, json);
+      Ajax.Responders.dispatch('on' + state, this, transport, json);
+    } catch (e) {
+      this.dispatchException(e);
+    }
+
+    if (state == 'Complete') {
+      // avoid memory leak in MSIE: clean up
+      this.transport.onreadystatechange = Prototype.emptyFunction;
+    }
+  },
+
+  getHeader: function(name) {
+    try {
+      return this.transport.getResponseHeader(name);
+    } catch (e) { return null }
+  },
+
+  evalJSON: function() {
+    try {
+      var json = this.getHeader('X-JSON');
+      return json ? json.evalJSON() : null;
+    } catch (e) { return null }
+  },
+
+  evalResponse: function() {
+    try {
+      return eval((this.transport.responseText || '').unfilterJSON());
+    } catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
+  dispatchException: function(exception) {
+    (this.options.onException || Prototype.emptyFunction)(this, exception);
+    Ajax.Responders.dispatch('onException', this, exception);
+  }
+});
+
+Ajax.Updater = Class.create();
+
+Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
+  initialize: function(container, url, options) {
+    this.container = {
+      success: (container.success || container),
+      failure: (container.failure || (container.success ? null : container))
+    }
+
+    this.transport = Ajax.getTransport();
+    this.setOptions(options);
+
+    var onComplete = this.options.onComplete || Prototype.emptyFunction;
+    this.options.onComplete = (function(transport, param) {
+      this.updateContent();
+      onComplete(transport, param);
+    }).bind(this);
+
+    this.request(url);
+  },
+
+  updateContent: function() {
+    var receiver = this.container[this.success() ? 'success' : 'failure'];
+    var response = this.transport.responseText;
+
+    if (!this.options.evalScripts) response = response.stripScripts();
+
+    if (receiver = $(receiver)) {
+      if (this.options.insertion)
+        new this.options.insertion(receiver, response);
+      else
+        receiver.update(response);
+    }
+
+    if (this.success()) {
+      if (this.onComplete)
+        setTimeout(this.onComplete.bind(this), 10);
+    }
+  }
+});
+
+Ajax.PeriodicalUpdater = Class.create();
+Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
+  initialize: function(container, url, options) {
+    this.setOptions(options);
+    this.onComplete = this.options.onComplete;
+
+    this.frequency = (this.options.frequency || 2);
+    this.decay = (this.options.decay || 1);
+
+    this.updater = {};
+    this.container = container;
+    this.url = url;
+
+    this.start();
+  },
+
+  start: function() {
+    this.options.onComplete = this.updateComplete.bind(this);
+    this.onTimerEvent();
+  },
+
+  stop: function() {
+    this.updater.options.onComplete = undefined;
+    clearTimeout(this.timer);
+    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
+  },
+
+  updateComplete: function(request) {
+    if (this.options.decay) {
+      this.decay = (request.responseText == this.lastText ?
+        this.decay * this.options.decay : 1);
+
+      this.lastText = request.responseText;
+    }
+    this.timer = setTimeout(this.onTimerEvent.bind(this),
+      this.decay * this.frequency * 1000);
+  },
+
+  onTimerEvent: function() {
+    this.updater = new Ajax.Updater(this.container, this.url, this.options);
+  }
+});
+function $(element) {
+  if (arguments.length > 1) {
+    for (var i = 0, elements = [], length = arguments.length; i < length; i++)
+      elements.push($(arguments[i]));
+    return elements;
+  }
+  if (typeof element == 'string')
+    element = document.getElementById(element);
+  return Element.extend(element);
+}
+
+if (Prototype.BrowserFeatures.XPath) {
+  document._getElementsByXPath = function(expression, parentElement) {
+    var results = [];
+    var query = document.evaluate(expression, $(parentElement) || document,
+      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+    for (var i = 0, length = query.snapshotLength; i < length; i++)
+      results.push(query.snapshotItem(i));
+    return results;
+  };
+
+  document.getElementsByClassName = function(className, parentElement) {
+    var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
+    return document._getElementsByXPath(q, parentElement);
+  }
+
+} else document.getElementsByClassName = function(className, parentElement) {
+  var children = ($(parentElement) || document.body).getElementsByTagName('*');
+  var elements = [], child;
+  for (var i = 0, length = children.length; i < length; i++) {
+    child = children[i];
+    if (Element.hasClassName(child, className))
+      elements.push(Element.extend(child));
+  }
+  return elements;
+};
+
+/*--------------------------------------------------------------------------*/
+
+if (!window.Element) var Element = {};
+
+Element.extend = function(element) {
+  var F = Prototype.BrowserFeatures;
+  if (!element || !element.tagName || element.nodeType == 3 ||
+   element._extended || F.SpecificElementExtensions || element == window)
+    return element;
+
+  var methods = {}, tagName = element.tagName, cache = Element.extend.cache,
+   T = Element.Methods.ByTag;
+
+  // extend methods for all tags (Safari doesn't need this)
+  if (!F.ElementExtensions) {
+    Object.extend(methods, Element.Methods),
+    Object.extend(methods, Element.Methods.Simulated);
+  }
+
+  // extend methods for specific tags
+  if (T[tagName]) Object.extend(methods, T[tagName]);
+
+  for (var property in methods) {
+    var value = methods[property];
+    if (typeof value == 'function' && !(property in element))
+      element[property] = cache.findOrStore(value);
+  }
+
+  element._extended = Prototype.emptyFunction;
+  return element;
+};
+
+Element.extend.cache = {
+  findOrStore: function(value) {
+    return this[value] = this[value] || function() {
+      return value.apply(null, [this].concat($A(arguments)));
+    }
+  }
+};
+
+Element.Methods = {
+  visible: function(element) {
+    return $(element).style.display != 'none';
+  },
+
+  toggle: function(element) {
+    element = $(element);
+    Element[Element.visible(element) ? 'hide' : 'show'](element);
+    return element;
+  },
+
+  hide: function(element) {
+    $(element).style.display = 'none';
+    return element;
+  },
+
+  show: function(element) {
+    $(element).style.display = '';
+    return element;
+  },
+
+  remove: function(element) {
+    element = $(element);
+    element.parentNode.removeChild(element);
+    return element;
+  },
+
+  update: function(element, html) {
+    html = typeof html == 'undefined' ? '' : html.toString();
+    $(element).innerHTML = html.stripScripts();
+    setTimeout(function() {html.evalScripts()}, 10);
+    return element;
+  },
+
+  replace: function(element, html) {
+    element = $(element);
+    html = typeof html == 'undefined' ? '' : html.toString();
+    if (element.outerHTML) {
+      element.outerHTML = html.stripScripts();
+    } else {
+      var range = element.ownerDocument.createRange();
+      range.selectNodeContents(element);
+      element.parentNode.replaceChild(
+        range.createContextualFragment(html.stripScripts()), element);
+    }
+    setTimeout(function() {html.evalScripts()}, 10);
+    return element;
+  },
+
+  inspect: function(element) {
+    element = $(element);
+    var result = '<' + element.tagName.toLowerCase();
+    $H({'id': 'id', 'className': 'class'}).each(function(pair) {
+      var property = pair.first(), attribute = pair.last();
+      var value = (element[property] || '').toString();
+      if (value) result += ' ' + attribute + '=' + value.inspect(true);
+    });
+    return result + '>';
+  },
+
+  recursivelyCollect: function(element, property) {
+    element = $(element);
+    var elements = [];
+    while (element = element[property])
+      if (element.nodeType == 1)
+        elements.push(Element.extend(element));
+    return elements;
+  },
+
+  ancestors: function(element) {
+    return $(element).recursivelyCollect('parentNode');
+  },
+
+  descendants: function(element) {
+    return $A($(element).getElementsByTagName('*')).each(Element.extend);
+  },
+
+  firstDescendant: function(element) {
+    element = $(element).firstChild;
+    while (element && element.nodeType != 1) element = element.nextSibling;
+    return $(element);
+  },
+
+  immediateDescendants: function(element) {
+    if (!(element = $(element).firstChild)) return [];
+    while (element && element.nodeType != 1) element = element.nextSibling;
+    if (element) return [element].concat($(element).nextSiblings());
+    return [];
+  },
+
+  previousSiblings: function(element) {
+    return $(element).recursivelyCollect('previousSibling');
+  },
+
+  nextSiblings: function(element) {
+    return $(element).recursivelyCollect('nextSibling');
+  },
+
+  siblings: function(element) {
+    element = $(element);
+    return element.previousSiblings().reverse().concat(element.nextSiblings());
+  },
+
+  match: function(element, selector) {
+    if (typeof selector == 'string')
+      selector = new Selector(selector);
+    return selector.match($(element));
+  },
+
+  up: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return $(element.parentNode);
+    var ancestors = element.ancestors();
+    return expression ? Selector.findElement(ancestors, expression, index) :
+      ancestors[index || 0];
+  },
+
+  down: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return element.firstDescendant();
+    var descendants = element.descendants();
+    return expression ? Selector.findElement(descendants, expression, index) :
+      descendants[index || 0];
+  },
+
+  previous: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
+    var previousSiblings = element.previousSiblings();
+    return expression ? Selector.findElement(previousSiblings, expression, index) :
+      previousSiblings[index || 0];
+  },
+
+  next: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
+    var nextSiblings = element.nextSiblings();
+    return expression ? Selector.findElement(nextSiblings, expression, index) :
+      nextSiblings[index || 0];
+  },
+
+  getElementsBySelector: function() {
+    var args = $A(arguments), element = $(args.shift());
+    return Selector.findChildElements(element, args);
+  },
+
+  getElementsByClassName: function(element, className) {
+    return document.getElementsByClassName(className, element);
+  },
+
+  readAttribute: function(element, name) {
+    element = $(element);
+    if (Prototype.Browser.IE) {
+      if (!element.attributes) return null;
+      var t = Element._attributeTranslations;
+      if (t.values[name]) return t.values[name](element, name);
+      if (t.names[name])  name = t.names[name];
+      var attribute = element.attributes[name];
+      return attribute ? attribute.nodeValue : null;
+    }
+    return element.getAttribute(name);
+  },
+
+  getHeight: function(element) {
+    return $(element).getDimensions().height;
+  },
+
+  getWidth: function(element) {
+    return $(element).getDimensions().width;
+  },
+
+  classNames: function(element) {
+    return new Element.ClassNames(element);
+  },
+
+  hasClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    var elementClassName = element.className;
+    if (elementClassName.length == 0) return false;
+    if (elementClassName == className ||
+        elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
+      return true;
+    return false;
+  },
+
+  addClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    Element.classNames(element).add(className);
+    return element;
+  },
+
+  removeClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    Element.classNames(element).remove(className);
+    return element;
+  },
+
+  toggleClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
+    return element;
+  },
+
+  observe: function() {
+    Event.observe.apply(Event, arguments);
+    return $A(arguments).first();
+  },
+
+  stopObserving: function() {
+    Event.stopObserving.apply(Event, arguments);
+    return $A(arguments).first();
+  },
+
+  // removes whitespace-only text node children
+  cleanWhitespace: function(element) {
+    element = $(element);
+    var node = element.firstChild;
+    while (node) {
+      var nextNode = node.nextSibling;
+      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
+        element.removeChild(node);
+      node = nextNode;
+    }
+    return element;
+  },
+
+  empty: function(element) {
+    return $(element).innerHTML.blank();
+  },
+
+  descendantOf: function(element, ancestor) {
+    element = $(element), ancestor = $(ancestor);
+    while (element = element.parentNode)
+      if (element == ancestor) return true;
+    return false;
+  },
+
+  scrollTo: function(element) {
+    element = $(element);
+    var pos = Position.cumulativeOffset(element);
+    window.scrollTo(pos[0], pos[1]);
+    return element;
+  },
+
+  getStyle: function(element, style) {
+    element = $(element);
+    style = style == 'float' ? 'cssFloat' : style.camelize();
+    var value = element.style[style];
+    if (!value) {
+      var css = document.defaultView.getComputedStyle(element, null);
+      value = css ? css[style] : null;
+    }
+    if (style == 'opacity') return value ? parseFloat(value) : 1.0;
+    return value == 'auto' ? null : value;
+  },
+
+  getOpacity: function(element) {
+    return $(element).getStyle('opacity');
+  },
+
+  setStyle: function(element, styles, camelized) {
+    element = $(element);
+    var elementStyle = element.style;
+
+    for (var property in styles)
+      if (property == 'opacity') element.setOpacity(styles[property])
+      else
+        elementStyle[(property == 'float' || property == 'cssFloat') ?
+          (elementStyle.styleFloat === undefined ? 'cssFloat' : 'styleFloat') :
+          (camelized ? property : property.camelize())] = styles[property];
+
+    return element;
+  },
+
+  setOpacity: function(element, value) {
+    element = $(element);
+    element.style.opacity = (value == 1 || value === '') ? '' :
+      (value < 0.00001) ? 0 : value;
+    return element;
+  },
+
+  getDimensions: function(element) {
+    element = $(element);
+    var display = $(element).getStyle('display');
+    if (display != 'none' && display != null) // Safari bug
+      return {width: element.offsetWidth, height: element.offsetHeight};
+
+    // All *Width and *Height properties give 0 on elements with display none,
+    // so enable the element temporarily
+    var els = element.style;
+    var originalVisibility = els.visibility;
+    var originalPosition = els.position;
+    var originalDisplay = els.display;
+    els.visibility = 'hidden';
+    els.position = 'absolute';
+    els.display = 'block';
+    var originalWidth = element.clientWidth;
+    var originalHeight = element.clientHeight;
+    els.display = originalDisplay;
+    els.position = originalPosition;
+    els.visibility = originalVisibility;
+    return {width: originalWidth, height: originalHeight};
+  },
+
+  makePositioned: function(element) {
+    element = $(element);
+    var pos = Element.getStyle(element, 'position');
+    if (pos == 'static' || !pos) {
+      element._madePositioned = true;
+      element.style.position = 'relative';
+      // Opera returns the offset relative to the positioning context, when an
+      // element is position relative but top and left have not been defined
+      if (window.opera) {
+        element.style.top = 0;
+        element.style.left = 0;
+      }
+    }
+    return element;
+  },
+
+  undoPositioned: function(element) {
+    element = $(element);
+    if (element._madePositioned) {
+      element._madePositioned = undefined;
+      element.style.position =
+        element.style.top =
+        element.style.left =
+        element.style.bottom =
+        element.style.right = '';
+    }
+    return element;
+  },
+
+  makeClipping: function(element) {
+    element = $(element);
+    if (element._overflow) return element;
+    element._overflow = element.style.overflow || 'auto';
+    if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
+      element.style.overflow = 'hidden';
+    return element;
+  },
+
+  undoClipping: function(element) {
+    element = $(element);
+    if (!element._overflow) return element;
+    element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
+    element._overflow = null;
+    return element;
+  }
+};
+
+Object.extend(Element.Methods, {
+  childOf: Element.Methods.descendantOf,
+  childElements: Element.Methods.immediateDescendants
+});
+
+if (Prototype.Browser.Opera) {
+  Element.Methods._getStyle = Element.Methods.getStyle;
+  Element.Methods.getStyle = function(element, style) {
+    switch(style) {
+      case 'left':
+      case 'top':
+      case 'right':
+      case 'bottom':
+        if (Element._getStyle(element, 'position') == 'static') return null;
+      default: return Element._getStyle(element, style);
+    }
+  };
+}
+else if (Prototype.Browser.IE) {
+  Element.Methods.getStyle = function(element, style) {
+    element = $(element);
+    style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
+    var value = element.style[style];
+    if (!value && element.currentStyle) value = element.currentStyle[style];
+
+    if (style == 'opacity') {
+      if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
+        if (value[1]) return parseFloat(value[1]) / 100;
+      return 1.0;
+    }
+
+    if (value == 'auto') {
+      if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
+        return element['offset'+style.capitalize()] + 'px';
+      return null;
+    }
+    return value;
+  };
+
+  Element.Methods.setOpacity = function(element, value) {
+    element = $(element);
+    var filter = element.getStyle('filter'), style = element.style;
+    if (value == 1 || value === '') {
+      style.filter = filter.replace(/alpha\([^\)]*\)/gi,'');
+      return element;
+    } else if (value < 0.00001) value = 0;
+    style.filter = filter.replace(/alpha\([^\)]*\)/gi, '') +
+      'alpha(opacity=' + (value * 100) + ')';
+    return element;
+  };
+
+  // IE is missing .innerHTML support for TABLE-related elements
+  Element.Methods.update = function(element, html) {
+    element = $(element);
+    html = typeof html == 'undefined' ? '' : html.toString();
+    var tagName = element.tagName.toUpperCase();
+    if (['THEAD','TBODY','TR','TD'].include(tagName)) {
+      var div = document.createElement('div');
+      switch (tagName) {
+        case 'THEAD':
+        case 'TBODY':
+          div.innerHTML = '<table><tbody>' +  html.stripScripts() + '</tbody></table>';
+          depth = 2;
+          break;
+        case 'TR':
+          div.innerHTML = '<table><tbody><tr>' +  html.stripScripts() + '</tr></tbody></table>';
+          depth = 3;
+          break;
+        case 'TD':
+          div.innerHTML = '<table><tbody><tr><td>' +  html.stripScripts() + '</td></tr></tbody></table>';
+          depth = 4;
+      }
+      $A(element.childNodes).each(function(node) { element.removeChild(node) });
+      depth.times(function() { div = div.firstChild });
+      $A(div.childNodes).each(function(node) { element.appendChild(node) });
+    } else {
+      element.innerHTML = html.stripScripts();
+    }
+    setTimeout(function() { html.evalScripts() }, 10);
+    return element;
+  }
+}
+else if (Prototype.Browser.Gecko) {
+  Element.Methods.setOpacity = function(element, value) {
+    element = $(element);
+    element.style.opacity = (value == 1) ? 0.999999 :
+      (value === '') ? '' : (value < 0.00001) ? 0 : value;
+    return element;
+  };
+}
+
+Element._attributeTranslations = {
+  names: {
+    colspan:   "colSpan",
+    rowspan:   "rowSpan",
+    valign:    "vAlign",
+    datetime:  "dateTime",
+    accesskey: "accessKey",
+    tabindex:  "tabIndex",
+    enctype:   "encType",
+    maxlength: "maxLength",
+    readonly:  "readOnly",
+    longdesc:  "longDesc"
+  },
+  values: {
+    _getAttr: function(element, attribute) {
+      return element.getAttribute(attribute, 2);
+    },
+    _flag: function(element, attribute) {
+      return $(element).hasAttribute(attribute) ? attribute : null;
+    },
+    style: function(element) {
+      return element.style.cssText.toLowerCase();
+    },
+    title: function(element) {
+      var node = element.getAttributeNode('title');
+      return node.specified ? node.nodeValue : null;
+    }
+  }
+};
+
+(function() {
+  Object.extend(this, {
+    href: this._getAttr,
+    src:  this._getAttr,
+    disabled: this._flag,
+    checked:  this._flag,
+    readonly: this._flag,
+    multiple: this._flag
+  });
+}).call(Element._attributeTranslations.values);
+
+Element.Methods.Simulated = {
+  hasAttribute: function(element, attribute) {
+    var t = Element._attributeTranslations, node;
+    attribute = t.names[attribute] || attribute;
+    node = $(element).getAttributeNode(attribute);
+    return node && node.specified;
+  }
+};
+
+Element.Methods.ByTag = {};
+
+Object.extend(Element, Element.Methods);
+
+if (!Prototype.BrowserFeatures.ElementExtensions &&
+ document.createElement('div').__proto__) {
+  window.HTMLElement = {};
+  window.HTMLElement.prototype = document.createElement('div').__proto__;
+  Prototype.BrowserFeatures.ElementExtensions = true;
+}
+
+Element.hasAttribute = function(element, attribute) {
+  if (element.hasAttribute) return element.hasAttribute(attribute);
+  return Element.Methods.Simulated.hasAttribute(element, attribute);
+};
+
+Element.addMethods = function(methods) {
+  var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
+  if (arguments.length == 2) {
+    var tagName = methods;
+    methods = arguments[1];
+  }
+
+  if (!tagName) Object.extend(Element.Methods, methods || {});
+  else {
+    if (tagName.constructor == Array) tagName.each(extend);
+    else extend(tagName);
+  }
+
+  function extend(tagName) {
+    tagName = tagName.toUpperCase();
+    if (!Element.Methods.ByTag[tagName])
+      Element.Methods.ByTag[tagName] = {};
+    Object.extend(Element.Methods.ByTag[tagName], methods);
+  }
+
+  function copy(methods, destination, onlyIfAbsent) {
+    onlyIfAbsent = onlyIfAbsent || false;
+    var cache = Element.extend.cache;
+    for (var property in methods) {
+      var value = methods[property];
+      if (!onlyIfAbsent || !(property in destination))
+        destination[property] = cache.findOrStore(value);
+    }
+  }
+
+  function findDOMClass(tagName) {
+    var klass;
+    var trans = {
+      "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
+      "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
+      "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
+      "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
+      "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
+      "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
+      "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
+      "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
+      "FrameSet", "IFRAME": "IFrame"
+    };
+    if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
+    if (window[klass]) return window[klass];
+    klass = 'HTML' + tagName + 'Element';
+    if (window[klass]) return window[klass];
+    klass = 'HTML' + tagName.capitalize() + 'Element';
+    if (window[klass]) return window[klass];
+
+    window[klass] = {};
+    window[klass].prototype = document.createElement(tagName).__proto__;
+    return window[klass];
+  }
+
+  if (F.ElementExtensions) {
+    copy(Element.Methods, HTMLElement.prototype);
+    copy(Element.Methods.Simulated, HTMLElement.prototype, true);
+  }
+
+  if (F.SpecificElementExtensions) {
+    for (var tag in Element.Methods.ByTag) {
+      var klass = findDOMClass(tag);
+      if (typeof klass == "undefined") continue;
+      copy(T[tag], klass.prototype);
+    }
+  }
+
+  Object.extend(Element, Element.Methods);
+  delete Element.ByTag;
+};
+
+var Toggle = { display: Element.toggle };
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.Insertion = function(adjacency) {
+  this.adjacency = adjacency;
+}
+
+Abstract.Insertion.prototype = {
+  initialize: function(element, content) {
+    this.element = $(element);
+    this.content = content.stripScripts();
+
+    if (this.adjacency && this.element.insertAdjacentHTML) {
+      try {
+        this.element.insertAdjacentHTML(this.adjacency, this.content);
+      } catch (e) {
+        var tagName = this.element.tagName.toUpperCase();
+        if (['TBODY', 'TR'].include(tagName)) {
+          this.insertContent(this.contentFromAnonymousTable());
+        } else {
+          throw e;
+        }
+      }
+    } else {
+      this.range = this.element.ownerDocument.createRange();
+      if (this.initializeRange) this.initializeRange();
+      this.insertContent([this.range.createContextualFragment(this.content)]);
+    }
+
+    setTimeout(function() {content.evalScripts()}, 10);
+  },
+
+  contentFromAnonymousTable: function() {
+    var div = document.createElement('div');
+    div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
+    return $A(div.childNodes[0].childNodes[0].childNodes);
+  }
+}
+
+var Insertion = new Object();
+
+Insertion.Before = Class.create();
+Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
+  initializeRange: function() {
+    this.range.setStartBefore(this.element);
+  },
+
+  insertContent: function(fragments) {
+    fragments.each((function(fragment) {
+      this.element.parentNode.insertBefore(fragment, this.element);
+    }).bind(this));
+  }
+});
+
+Insertion.Top = Class.create();
+Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
+  initializeRange: function() {
+    this.range.selectNodeContents(this.element);
+    this.range.collapse(true);
+  },
+
+  insertContent: function(fragments) {
+    fragments.reverse(false).each((function(fragment) {
+      this.element.insertBefore(fragment, this.element.firstChild);
+    }).bind(this));
+  }
+});
+
+Insertion.Bottom = Class.create();
+Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
+  initializeRange: function() {
+    this.range.selectNodeContents(this.element);
+    this.range.collapse(this.element);
+  },
+
+  insertContent: function(fragments) {
+    fragments.each((function(fragment) {
+      this.element.appendChild(fragment);
+    }).bind(this));
+  }
+});
+
+Insertion.After = Class.create();
+Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
+  initializeRange: function() {
+    this.range.setStartAfter(this.element);
+  },
+
+  insertContent: function(fragments) {
+    fragments.each((function(fragment) {
+      this.element.parentNode.insertBefore(fragment,
+        this.element.nextSibling);
+    }).bind(this));
+  }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Element.ClassNames = Class.create();
+Element.ClassNames.prototype = {
+  initialize: function(element) {
+    this.element = $(element);
+  },
+
+  _each: function(iterator) {
+    this.element.className.split(/\s+/).select(function(name) {
+      return name.length > 0;
+    })._each(iterator);
+  },
+
+  set: function(className) {
+    this.element.className = className;
+  },
+
+  add: function(classNameToAdd) {
+    if (this.include(classNameToAdd)) return;
+    this.set($A(this).concat(classNameToAdd).join(' '));
+  },
+
+  remove: function(classNameToRemove) {
+    if (!this.include(classNameToRemove)) return;
+    this.set($A(this).without(classNameToRemove).join(' '));
+  },
+
+  toString: function() {
+    return $A(this).join(' ');
+  }
+};
+
+Object.extend(Element.ClassNames.prototype, Enumerable);
+/* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
+ * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
+ * license.  Please see http://www.yui-ext.com/ for more information. */
+
+var Selector = Class.create();
+
+Selector.prototype = {
+  initialize: function(expression) {
+    this.expression = expression.strip();
+    this.compileMatcher();
+  },
+
+  compileMatcher: function() {
+    // Selectors with namespaced attributes can't use the XPath version
+    if (Prototype.BrowserFeatures.XPath && !(/\[[\w-]*?:/).test(this.expression))
+      return this.compileXPathMatcher();
+
+    var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
+        c = Selector.criteria, le, p, m;
+
+    if (Selector._cache[e]) {
+      this.matcher = Selector._cache[e]; return;
+    }
+    this.matcher = ["this.matcher = function(root) {",
+                    "var r = root, h = Selector.handlers, c = false, n;"];
+
+    while (e && le != e && (/\S/).test(e)) {
+      le = e;
+      for (var i in ps) {
+        p = ps[i];
+        if (m = e.match(p)) {
+          this.matcher.push(typeof c[i] == 'function' ? c[i](m) :
+             new Template(c[i]).evaluate(m));
+          e = e.replace(m[0], '');
+          break;
+        }
+      }
+    }
+
+    this.matcher.push("return h.unique(n);\n}");
+    eval(this.matcher.join('\n'));
+    Selector._cache[this.expression] = this.matcher;
+  },
+
+  compileXPathMatcher: function() {
+    var e = this.expression, ps = Selector.patterns,
+        x = Selector.xpath, le,  m;
+
+    if (Selector._cache[e]) {
+      this.xpath = Selector._cache[e]; return;
+    }
+
+    this.matcher = ['.//*'];
+    while (e && le != e && (/\S/).test(e)) {
+      le = e;
+      for (var i in ps) {
+        if (m = e.match(ps[i])) {
+          this.matcher.push(typeof x[i] == 'function' ? x[i](m) :
+            new Template(x[i]).evaluate(m));
+          e = e.replace(m[0], '');
+          break;
+        }
+      }
+    }
+
+    this.xpath = this.matcher.join('');
+    Selector._cache[this.expression] = this.xpath;
+  },
+
+  findElements: function(root) {
+    root = root || document;
+    if (this.xpath) return document._getElementsByXPath(this.xpath, root);
+    return this.matcher(root);
+  },
+
+  match: function(element) {
+    return this.findElements(document).include(element);
+  },
+
+  toString: function() {
+    return this.expression;
+  },
+
+  inspect: function() {
+    return "#<Selector:" + this.expression.inspect() + ">";
+  }
+};
+
+Object.extend(Selector, {
+  _cache: {},
+
+  xpath: {
+    descendant:   "//*",
+    child:        "/*",
+    adjacent:     "/following-sibling::*[1]",
+    laterSibling: '/following-sibling::*',
+    tagName:      function(m) {
+      if (m[1] == '*') return '';
+      return "[local-name()='" + m[1].toLowerCase() +
+             "' or local-name()='" + m[1].toUpperCase() + "']";
+    },
+    className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]",
+    id:           "[@id='#{1}']",
+    attrPresence: "[@#{1}]",
+    attr: function(m) {
+      m[3] = m[5] || m[6];
+      return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
+    },
+    pseudo: function(m) {
+      var h = Selector.xpath.pseudos[m[1]];
+      if (!h) return '';
+      if (typeof h === 'function') return h(m);
+      return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
+    },
+    operators: {
+      '=':  "[@#{1}='#{3}']",
+      '!=': "[@#{1}!='#{3}']",
+      '^=': "[starts-with(@#{1}, '#{3}')]",
+      '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
+      '*=': "[contains(@#{1}, '#{3}')]",
+      '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
+      '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
+    },
+    pseudos: {
+      'first-child': '[not(preceding-sibling::*)]',
+      'last-child':  '[not(following-sibling::*)]',
+      'only-child':  '[not(preceding-sibling::* or following-sibling::*)]',
+      'empty':       "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
+      'checked':     "[@checked]",
+      'disabled':    "[@disabled]",
+      'enabled':     "[not(@disabled)]",
+      'not': function(m) {
+        var e = m[6], p = Selector.patterns,
+            x = Selector.xpath, le, m, v;
+
+        var exclusion = [];
+        while (e && le != e && (/\S/).test(e)) {
+          le = e;
+          for (var i in p) {
+            if (m = e.match(p[i])) {
+              v = typeof x[i] == 'function' ? x[i](m) : new Template(x[i]).evaluate(m);
+              exclusion.push("(" + v.substring(1, v.length - 1) + ")");
+              e = e.replace(m[0], '');
+              break;
+            }
+          }
+        }
+        return "[not(" + exclusion.join(" and ") + ")]";
+      },
+      'nth-child':      function(m) {
+        return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
+      },
+      'nth-last-child': function(m) {
+        return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
+      },
+      'nth-of-type':    function(m) {
+        return Selector.xpath.pseudos.nth("position() ", m);
+      },
+      'nth-last-of-type': function(m) {
+        return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
+      },
+      'first-of-type':  function(m) {
+        m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
+      },
+      'last-of-type':   function(m) {
+        m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
+      },
+      'only-of-type':   function(m) {
+        var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
+      },
+      nth: function(fragment, m) {
+        var mm, formula = m[6], predicate;
+        if (formula == 'even') formula = '2n+0';
+        if (formula == 'odd')  formula = '2n+1';
+        if (mm = formula.match(/^(\d+)$/)) // digit only
+          return '[' + fragment + "= " + mm[1] + ']';
+        if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+          if (mm[1] == "-") mm[1] = -1;
+          var a = mm[1] ? Number(mm[1]) : 1;
+          var b = mm[2] ? Number(mm[2]) : 0;
+          predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
+          "((#{fragment} - #{b}) div #{a} >= 0)]";
+          return new Template(predicate).evaluate({
+            fragment: fragment, a: a, b: b });
+        }
+      }
+    }
+  },
+
+  criteria: {
+    tagName:      'n = h.tagName(n, r, "#{1}", c);   c = false;',
+    className:    'n = h.className(n, r, "#{1}", c); c = false;',
+    id:           'n = h.id(n, r, "#{1}", c);        c = false;',
+    attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;',
+    attr: function(m) {
+      m[3] = (m[5] || m[6]);
+      return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m);
+    },
+    pseudo:       function(m) {
+      if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
+      return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
+    },
+    descendant:   'c = "descendant";',
+    child:        'c = "child";',
+    adjacent:     'c = "adjacent";',
+    laterSibling: 'c = "laterSibling";'
+  },
+
+  patterns: {
+    // combinators must be listed first
+    // (and descendant needs to be last combinator)
+    laterSibling: /^\s*~\s*/,
+    child:        /^\s*>\s*/,
+    adjacent:     /^\s*\+\s*/,
+    descendant:   /^\s/,
+
+    // selectors follow
+    tagName:      /^\s*(\*|[\w\-]+)(\b|$)?/,
+    id:           /^#([\w\-\*]+)(\b|$)/,
+    className:    /^\.([\w\-\*]+)(\b|$)/,
+    pseudo:       /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|\s)/,
+    attrPresence: /^\[([\w]+)\]/,
+    attr:         /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\]]*?)\4|([^'"][^\]]*?)))?\]/
+  },
+
+  handlers: {
+    // UTILITY FUNCTIONS
+    // joins two collections
+    concat: function(a, b) {
+      for (var i = 0, node; node = b[i]; i++)
+        a.push(node);
+      return a;
+    },
+
+    // marks an array of nodes for counting
+    mark: function(nodes) {
+      for (var i = 0, node; node = nodes[i]; i++)
+        node._counted = true;
+      return nodes;
+    },
+
+    unmark: function(nodes) {
+      for (var i = 0, node; node = nodes[i]; i++)
+        node._counted = undefined;
+      return nodes;
+    },
+
+    // mark each child node with its position (for nth calls)
+    // "ofType" flag indicates whether we're indexing for nth-of-type
+    // rather than nth-child
+    index: function(parentNode, reverse, ofType) {
+      parentNode._counted = true;
+      if (reverse) {
+        for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
+          node = nodes[i];
+          if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
+        }
+      } else {
+        for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
+          if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
+      }
+    },
+
+    // filters out duplicates and extends all nodes
+    unique: function(nodes) {
+      if (nodes.length == 0) return nodes;
+      var results = [], n;
+      for (var i = 0, l = nodes.length; i < l; i++)
+        if (!(n = nodes[i])._counted) {
+          n._counted = true;
+          results.push(Element.extend(n));
+        }
+      return Selector.handlers.unmark(results);
+    },
+
+    // COMBINATOR FUNCTIONS
+    descendant: function(nodes) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        h.concat(results, node.getElementsByTagName('*'));
+      return results;
+    },
+
+    child: function(nodes) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        for (var j = 0, children = [], child; child = node.childNodes[j]; j++)
+          if (child.nodeType == 1 && child.tagName != '!') results.push(child);
+      }
+      return results;
+    },
+
+    adjacent: function(nodes) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        var next = this.nextElementSibling(node);
+        if (next) results.push(next);
+      }
+      return results;
+    },
+
+    laterSibling: function(nodes) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        h.concat(results, Element.nextSiblings(node));
+      return results;
+    },
+
+    nextElementSibling: function(node) {
+      while (node = node.nextSibling)
+             if (node.nodeType == 1) return node;
+      return null;
+    },
+
+    previousElementSibling: function(node) {
+      while (node = node.previousSibling)
+        if (node.nodeType == 1) return node;
+      return null;
+    },
+
+    // TOKEN FUNCTIONS
+    tagName: function(nodes, root, tagName, combinator) {
+      tagName = tagName.toUpperCase();
+      var results = [], h = Selector.handlers;
+      if (nodes) {
+        if (combinator) {
+          // fastlane for ordinary descendant combinators
+          if (combinator == "descendant") {
+            for (var i = 0, node; node = nodes[i]; i++)
+              h.concat(results, node.getElementsByTagName(tagName));
+            return results;
+          } else nodes = this[combinator](nodes);
+          if (tagName == "*") return nodes;
+        }
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node.tagName.toUpperCase() == tagName) results.push(node);
+        return results;
+      } else return root.getElementsByTagName(tagName);
+    },
+
+    id: function(nodes, root, id, combinator) {
+      var targetNode = $(id), h = Selector.handlers;
+      if (!nodes && root == document) return targetNode ? [targetNode] : [];
+      if (nodes) {
+        if (combinator) {
+          if (combinator == 'child') {
+            for (var i = 0, node; node = nodes[i]; i++)
+              if (targetNode.parentNode == node) return [targetNode];
+          } else if (combinator == 'descendant') {
+            for (var i = 0, node; node = nodes[i]; i++)
+              if (Element.descendantOf(targetNode, node)) return [targetNode];
+          } else if (combinator == 'adjacent') {
+            for (var i = 0, node; node = nodes[i]; i++)
+              if (Selector.handlers.previousElementSibling(targetNode) == node)
+                return [targetNode];
+          } else nodes = h[combinator](nodes);
+        }
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node == targetNode) return [targetNode];
+        return [];
+      }
+      return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
+    },
+
+    className: function(nodes, root, className, combinator) {
+      if (nodes && combinator) nodes = this[combinator](nodes);
+      return Selector.handlers.byClassName(nodes, root, className);
+    },
+
+    byClassName: function(nodes, root, className) {
+      if (!nodes) nodes = Selector.handlers.descendant([root]);
+      var needle = ' ' + className + ' ';
+      for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
+        nodeClassName = node.className;
+        if (nodeClassName.length == 0) continue;
+        if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
+          results.push(node);
+      }
+      return results;
+    },
+
+    attrPresence: function(nodes, root, attr) {
+      var results = [];
+      for (var i = 0, node; node = nodes[i]; i++)
+        if (Element.hasAttribute(node, attr)) results.push(node);
+      return results;
+    },
+
+    attr: function(nodes, root, attr, value, operator) {
+      if (!nodes) nodes = root.getElementsByTagName("*");
+      var handler = Selector.operators[operator], results = [];
+      for (var i = 0, node; node = nodes[i]; i++) {
+        var nodeValue = Element.readAttribute(node, attr);
+        if (nodeValue === null) continue;
+        if (handler(nodeValue, value)) results.push(node);
+      }
+      return results;
+    },
+
+    pseudo: function(nodes, name, value, root, combinator) {
+      if (nodes && combinator) nodes = this[combinator](nodes);
+      if (!nodes) nodes = root.getElementsByTagName("*");
+      return Selector.pseudos[name](nodes, value, root);
+    }
+  },
+
+  pseudos: {
+    'first-child': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        if (Selector.handlers.previousElementSibling(node)) continue;
+          results.push(node);
+      }
+      return results;
+    },
+    'last-child': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        if (Selector.handlers.nextElementSibling(node)) continue;
+          results.push(node);
+      }
+      return results;
+    },
+    'only-child': function(nodes, value, root) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
+          results.push(node);
+      return results;
+    },
+    'nth-child':        function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root);
+    },
+    'nth-last-child':   function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root, true);
+    },
+    'nth-of-type':      function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root, false, true);
+    },
+    'nth-last-of-type': function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root, true, true);
+    },
+    'first-of-type':    function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, "1", root, false, true);
+    },
+    'last-of-type':     function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, "1", root, true, true);
+    },
+    'only-of-type':     function(nodes, formula, root) {
+      var p = Selector.pseudos;
+      return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
+    },
+
+    // handles the an+b logic
+    getIndices: function(a, b, total) {
+      if (a == 0) return b > 0 ? [b] : [];
+      return $R(1, total).inject([], function(memo, i) {
+        if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
+        return memo;
+      });
+    },
+
+    // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
+    nth: function(nodes, formula, root, reverse, ofType) {
+      if (nodes.length == 0) return [];
+      if (formula == 'even') formula = '2n+0';
+      if (formula == 'odd')  formula = '2n+1';
+      var h = Selector.handlers, results = [], indexed = [], m;
+      h.mark(nodes);
+      for (var i = 0, node; node = nodes[i]; i++) {
+        if (!node.parentNode._counted) {
+          h.index(node.parentNode, reverse, ofType);
+          indexed.push(node.parentNode);
+        }
+      }
+      if (formula.match(/^\d+$/)) { // just a number
+        formula = Number(formula);
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node.nodeIndex == formula) results.push(node);
+      } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+        if (m[1] == "-") m[1] = -1;
+        var a = m[1] ? Number(m[1]) : 1;
+        var b = m[2] ? Number(m[2]) : 0;
+        var indices = Selector.pseudos.getIndices(a, b, nodes.length);
+        for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
+          for (var j = 0; j < l; j++)
+            if (node.nodeIndex == indices[j]) results.push(node);
+        }
+      }
+      h.unmark(nodes);
+      h.unmark(indexed);
+      return results;
+    },
+
+    'empty': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        // IE treats comments as element nodes
+        if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
+        results.push(node);
+      }
+      return results;
+    },
+
+    'not': function(nodes, selector, root) {
+      var h = Selector.handlers, selectorType, m;
+      var exclusions = new Selector(selector).findElements(root);
+      h.mark(exclusions);
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (!node._counted) results.push(node);
+      h.unmark(exclusions);
+      return results;
+    },
+
+    'enabled': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (!node.disabled) results.push(node);
+      return results;
+    },
+
+    'disabled': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (node.disabled) results.push(node);
+      return results;
+    },
+
+    'checked': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (node.checked) results.push(node);
+      return results;
+    }
+  },
+
+  operators: {
+    '=':  function(nv, v) { return nv == v; },
+    '!=': function(nv, v) { return nv != v; },
+    '^=': function(nv, v) { return nv.startsWith(v); },
+    '$=': function(nv, v) { return nv.endsWith(v); },
+    '*=': function(nv, v) { return nv.include(v); },
+    '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
+    '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
+  },
+
+  matchElements: function(elements, expression) {
+    var matches = new Selector(expression).findElements(), h = Selector.handlers;
+    h.mark(matches);
+    for (var i = 0, results = [], element; element = elements[i]; i++)
+      if (element._counted) results.push(element);
+    h.unmark(matches);
+    return results;
+  },
+
+  findElement: function(elements, expression, index) {
+    if (typeof expression == 'number') {
+      index = expression; expression = false;
+    }
+    return Selector.matchElements(elements, expression || '*')[index || 0];
+  },
+
+  findChildElements: function(element, expressions) {
+    var exprs = expressions.join(','), expressions = [];
+    exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
+      expressions.push(m[1].strip());
+    });
+    var results = [], h = Selector.handlers;
+    for (var i = 0, l = expressions.length, selector; i < l; i++) {
+      selector = new Selector(expressions[i].strip());
+      h.concat(results, selector.findElements(element));
+    }
+    return (l > 1) ? h.unique(results) : results;
+  }
+});
+
+function $$() {
+  return Selector.findChildElements(document, $A(arguments));
+}
+var Form = {
+  reset: function(form) {
+    $(form).reset();
+    return form;
+  },
+
+  serializeElements: function(elements, getHash) {
+    var data = elements.inject({}, function(result, element) {
+      if (!element.disabled && element.name) {
+        var key = element.name, value = $(element).getValue();
+        if (value != null) {
+               if (key in result) {
+            if (result[key].constructor != Array) result[key] = [result[key]];
+            result[key].push(value);
+          }
+          else result[key] = value;
+        }
+      }
+      return result;
+    });
+
+    return getHash ? data : Hash.toQueryString(data);
+  }
+};
+
+Form.Methods = {
+  serialize: function(form, getHash) {
+    return Form.serializeElements(Form.getElements(form), getHash);
+  },
+
+  getElements: function(form) {
+    return $A($(form).getElementsByTagName('*')).inject([],
+      function(elements, child) {
+        if (Form.Element.Serializers[child.tagName.toLowerCase()])
+          elements.push(Element.extend(child));
+        return elements;
+      }
+    );
+  },
+
+  getInputs: function(form, typeName, name) {
+    form = $(form);
+    var inputs = form.getElementsByTagName('input');
+
+    if (!typeName && !name) return $A(inputs).map(Element.extend);
+
+    for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
+      var input = inputs[i];
+      if ((typeName && input.type != typeName) || (name && input.name != name))
+        continue;
+      matchingInputs.push(Element.extend(input));
+    }
+
+    return matchingInputs;
+  },
+
+  disable: function(form) {
+    form = $(form);
+    Form.getElements(form).invoke('disable');
+    return form;
+  },
+
+  enable: function(form) {
+    form = $(form);
+    Form.getElements(form).invoke('enable');
+    return form;
+  },
+
+  findFirstElement: function(form) {
+    return $(form).getElements().find(function(element) {
+      return element.type != 'hidden' && !element.disabled &&
+        ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
+    });
+  },
+
+  focusFirstElement: function(form) {
+    form = $(form);
+    form.findFirstElement().activate();
+    return form;
+  },
+
+  request: function(form, options) {
+    form = $(form), options = Object.clone(options || {});
+
+    var params = options.parameters;
+    options.parameters = form.serialize(true);
+
+    if (params) {
+      if (typeof params == 'string') params = params.toQueryParams();
+      Object.extend(options.parameters, params);
+    }
+
+    if (form.hasAttribute('method') && !options.method)
+      options.method = form.method;
+
+    return new Ajax.Request(form.readAttribute('action'), options);
+  }
+}
+
+Object.extend(Form, Form.Methods);
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element = {
+  focus: function(element) {
+    $(element).focus();
+    return element;
+  },
+
+  select: function(element) {
+    $(element).select();
+    return element;
+  }
+}
+
+Form.Element.Methods = {
+  serialize: function(element) {
+    element = $(element);
+    if (!element.disabled && element.name) {
+      var value = element.getValue();
+      if (value != undefined) {
+        var pair = {};
+        pair[element.name] = value;
+        return Hash.toQueryString(pair);
+      }
+    }
+    return '';
+  },
+
+  getValue: function(element) {
+    element = $(element);
+    var method = element.tagName.toLowerCase();
+    return Form.Element.Serializers[method](element);
+  },
+
+  clear: function(element) {
+    $(element).value = '';
+    return element;
+  },
+
+  present: function(element) {
+    return $(element).value != '';
+  },
+
+  activate: function(element) {
+    element = $(element);
+    try {
+      element.focus();
+      if (element.select && (element.tagName.toLowerCase() != 'input' ||
+        !['button', 'reset', 'submit'].include(element.type)))
+        element.select();
+    } catch (e) {}
+    return element;
+  },
+
+  disable: function(element) {
+    element = $(element);
+    element.blur();
+    element.disabled = true;
+    return element;
+  },
+
+  enable: function(element) {
+    element = $(element);
+    element.disabled = false;
+    return element;
+  }
+}
+
+Object.extend(Form.Element, Form.Element.Methods);
+Object.extend(Element.Methods.ByTag, {
+  "FORM":     Object.clone(Form.Methods),
+  "INPUT":    Object.clone(Form.Element.Methods),
+  "SELECT":   Object.clone(Form.Element.Methods),
+  "TEXTAREA": Object.clone(Form.Element.Methods)
+});
+
+/*--------------------------------------------------------------------------*/
+
+var Field = Form.Element;
+var $F = Form.Element.getValue;
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element.Serializers = {
+  input: function(element) {
+    switch (element.type.toLowerCase()) {
+      case 'checkbox':
+      case 'radio':
+        return Form.Element.Serializers.inputSelector(element);
+      default:
+        return Form.Element.Serializers.textarea(element);
+    }
+  },
+
+  inputSelector: function(element) {
+    return element.checked ? element.value : null;
+  },
+
+  textarea: function(element) {
+    return element.value;
+  },
+
+  select: function(element) {
+    return this[element.type == 'select-one' ?
+      'selectOne' : 'selectMany'](element);
+  },
+
+  selectOne: function(element) {
+    var index = element.selectedIndex;
+    return index >= 0 ? this.optionValue(element.options[index]) : null;
+  },
+
+  selectMany: function(element) {
+    var values, length = element.length;
+    if (!length) return null;
+
+    for (var i = 0, values = []; i < length; i++) {
+      var opt = element.options[i];
+      if (opt.selected) values.push(this.optionValue(opt));
+    }
+    return values;
+  },
+
+  optionValue: function(opt) {
+    // extend element because hasAttribute may not be native
+    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.TimedObserver = function() {}
+Abstract.TimedObserver.prototype = {
+  initialize: function(element, frequency, callback) {
+    this.frequency = frequency;
+    this.element   = $(element);
+    this.callback  = callback;
+
+    this.lastValue = this.getValue();
+    this.registerCallback();
+  },
+
+  registerCallback: function() {
+    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+  },
+
+  onTimerEvent: function() {
+    var value = this.getValue();
+    var changed = ('string' == typeof this.lastValue && 'string' == typeof value
+      ? this.lastValue != value : String(this.lastValue) != String(value));
+    if (changed) {
+      this.callback(this.element, value);
+      this.lastValue = value;
+    }
+  }
+}
+
+Form.Element.Observer = Class.create();
+Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+  getValue: function() {
+    return Form.Element.getValue(this.element);
+  }
+});
+
+Form.Observer = Class.create();
+Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+  getValue: function() {
+    return Form.serialize(this.element);
+  }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.EventObserver = function() {}
+Abstract.EventObserver.prototype = {
+  initialize: function(element, callback) {
+    this.element  = $(element);
+    this.callback = callback;
+
+    this.lastValue = this.getValue();
+    if (this.element.tagName.toLowerCase() == 'form')
+      this.registerFormCallbacks();
+    else
+      this.registerCallback(this.element);
+  },
+
+  onElementEvent: function() {
+    var value = this.getValue();
+    if (this.lastValue != value) {
+      this.callback(this.element, value);
+      this.lastValue = value;
+    }
+  },
+
+  registerFormCallbacks: function() {
+    Form.getElements(this.element).each(this.registerCallback.bind(this));
+  },
+
+  registerCallback: function(element) {
+    if (element.type) {
+      switch (element.type.toLowerCase()) {
+        case 'checkbox':
+        case 'radio':
+          Event.observe(element, 'click', this.onElementEvent.bind(this));
+          break;
+        default:
+          Event.observe(element, 'change', this.onElementEvent.bind(this));
+          break;
+      }
+    }
+  }
+}
+
+Form.Element.EventObserver = Class.create();
+Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+  getValue: function() {
+    return Form.Element.getValue(this.element);
+  }
+});
+
+Form.EventObserver = Class.create();
+Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+  getValue: function() {
+    return Form.serialize(this.element);
+  }
+});
+if (!window.Event) {
+  var Event = new Object();
+}
+
+Object.extend(Event, {
+  KEY_BACKSPACE: 8,
+  KEY_TAB:       9,
+  KEY_RETURN:   13,
+  KEY_ESC:      27,
+  KEY_LEFT:     37,
+  KEY_UP:       38,
+  KEY_RIGHT:    39,
+  KEY_DOWN:     40,
+  KEY_DELETE:   46,
+  KEY_HOME:     36,
+  KEY_END:      35,
+  KEY_PAGEUP:   33,
+  KEY_PAGEDOWN: 34,
+
+  element: function(event) {
+    return $(event.target || event.srcElement);
+  },
+
+  isLeftClick: function(event) {
+    return (((event.which) && (event.which == 1)) ||
+            ((event.button) && (event.button == 1)));
+  },
+
+  pointerX: function(event) {
+    return event.pageX || (event.clientX +
+      (document.documentElement.scrollLeft || document.body.scrollLeft));
+  },
+
+  pointerY: function(event) {
+    return event.pageY || (event.clientY +
+      (document.documentElement.scrollTop || document.body.scrollTop));
+  },
+
+  stop: function(event) {
+    if (event.preventDefault) {
+      event.preventDefault();
+      event.stopPropagation();
+    } else {
+      event.returnValue = false;
+      event.cancelBubble = true;
+    }
+  },
+
+  // find the first node with the given tagName, starting from the
+  // node the event was triggered on; traverses the DOM upwards
+  findElement: function(event, tagName) {
+    var element = Event.element(event);
+    while (element.parentNode && (!element.tagName ||
+        (element.tagName.toUpperCase() != tagName.toUpperCase())))
+      element = element.parentNode;
+    return element;
+  },
+
+  observers: false,
+
+  _observeAndCache: function(element, name, observer, useCapture) {
+    if (!this.observers) this.observers = [];
+    if (element.addEventListener) {
+      this.observers.push([element, name, observer, useCapture]);
+      element.addEventListener(name, observer, useCapture);
+    } else if (element.attachEvent) {
+      this.observers.push([element, name, observer, useCapture]);
+      element.attachEvent('on' + name, observer);
+    }
+  },
+
+  unloadCache: function() {
+    if (!Event.observers) return;
+    for (var i = 0, length = Event.observers.length; i < length; i++) {
+      Event.stopObserving.apply(this, Event.observers[i]);
+      Event.observers[i][0] = null;
+    }
+    Event.observers = false;
+  },
+
+  observe: function(element, name, observer, useCapture) {
+    element = $(element);
+    useCapture = useCapture || false;
+
+    if (name == 'keypress' &&
+      (Prototype.Browser.WebKit || element.attachEvent))
+      name = 'keydown';
+
+    Event._observeAndCache(element, name, observer, useCapture);
+  },
+
+  stopObserving: function(element, name, observer, useCapture) {
+    element = $(element);
+    useCapture = useCapture || false;
+
+    if (name == 'keypress' &&
+        (Prototype.Browser.WebKit || element.attachEvent))
+      name = 'keydown';
+
+    if (element.removeEventListener) {
+      element.removeEventListener(name, observer, useCapture);
+    } else if (element.detachEvent) {
+      try {
+        element.detachEvent('on' + name, observer);
+      } catch (e) {}
+    }
+  }
+});
+
+/* prevent memory leaks in IE */
+if (Prototype.Browser.IE)
+  Event.observe(window, 'unload', Event.unloadCache, false);
+var Position = {
+  // set to true if needed, warning: firefox performance problems
+  // NOT neeeded for page scrolling, only if draggable contained in
+  // scrollable elements
+  includeScrollOffsets: false,
+
+  // must be called before calling withinIncludingScrolloffset, every time the
+  // page is scrolled
+  prepare: function() {
+    this.deltaX =  window.pageXOffset
+                || document.documentElement.scrollLeft
+                || document.body.scrollLeft
+                || 0;
+    this.deltaY =  window.pageYOffset
+                || document.documentElement.scrollTop
+                || document.body.scrollTop
+                || 0;
+  },
+
+  realOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.scrollTop  || 0;
+      valueL += element.scrollLeft || 0;
+      element = element.parentNode;
+    } while (element);
+    return [valueL, valueT];
+  },
+
+  cumulativeOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+    } while (element);
+    return [valueL, valueT];
+  },
+
+  positionedOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+      if (element) {
+        if(element.tagName=='BODY') break;
+        var p = Element.getStyle(element, 'position');
+        if (p == 'relative' || p == 'absolute') break;
+      }
+    } while (element);
+    return [valueL, valueT];
+  },
+
+  offsetParent: function(element) {
+    if (element.offsetParent) return element.offsetParent;
+    if (element == document.body) return element;
+
+    while ((element = element.parentNode) && element != document.body)
+      if (Element.getStyle(element, 'position') != 'static')
+        return element;
+
+    return document.body;
+  },
+
+  // caches x/y coordinate pair to use with overlap
+  within: function(element, x, y) {
+    if (this.includeScrollOffsets)
+      return this.withinIncludingScrolloffsets(element, x, y);
+    this.xcomp = x;
+    this.ycomp = y;
+    this.offset = this.cumulativeOffset(element);
+
+    return (y >= this.offset[1] &&
+            y <  this.offset[1] + element.offsetHeight &&
+            x >= this.offset[0] &&
+            x <  this.offset[0] + element.offsetWidth);
+  },
+
+  withinIncludingScrolloffsets: function(element, x, y) {
+    var offsetcache = this.realOffset(element);
+
+    this.xcomp = x + offsetcache[0] - this.deltaX;
+    this.ycomp = y + offsetcache[1] - this.deltaY;
+    this.offset = this.cumulativeOffset(element);
+
+    return (this.ycomp >= this.offset[1] &&
+            this.ycomp <  this.offset[1] + element.offsetHeight &&
+            this.xcomp >= this.offset[0] &&
+            this.xcomp <  this.offset[0] + element.offsetWidth);
+  },
+
+  // within must be called directly before
+  overlap: function(mode, element) {
+    if (!mode) return 0;
+    if (mode == 'vertical')
+      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
+        element.offsetHeight;
+    if (mode == 'horizontal')
+      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
+        element.offsetWidth;
+  },
+
+  page: function(forElement) {
+    var valueT = 0, valueL = 0;
+
+    var element = forElement;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+
+      // Safari fix
+      if (element.offsetParent == document.body)
+        if (Element.getStyle(element,'position')=='absolute') break;
+
+    } while (element = element.offsetParent);
+
+    element = forElement;
+    do {
+      if (!window.opera || element.tagName=='BODY') {
+        valueT -= element.scrollTop  || 0;
+        valueL -= element.scrollLeft || 0;
+      }
+    } while (element = element.parentNode);
+
+    return [valueL, valueT];
+  },
+
+  clone: function(source, target) {
+    var options = Object.extend({
+      setLeft:    true,
+      setTop:     true,
+      setWidth:   true,
+      setHeight:  true,
+      offsetTop:  0,
+      offsetLeft: 0
+    }, arguments[2] || {})
+
+    // find page position of source
+    source = $(source);
+    var p = Position.page(source);
+
+    // find coordinate system to use
+    target = $(target);
+    var delta = [0, 0];
+    var parent = null;
+    // delta [0,0] will do fine with position: fixed elements,
+    // position:absolute needs offsetParent deltas
+    if (Element.getStyle(target,'position') == 'absolute') {
+      parent = Position.offsetParent(target);
+      delta = Position.page(parent);
+    }
+
+    // correct by body offsets (fixes Safari)
+    if (parent == document.body) {
+      delta[0] -= document.body.offsetLeft;
+      delta[1] -= document.body.offsetTop;
+    }
+
+    // set position
+    if(options.setLeft)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
+    if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
+    if(options.setWidth)  target.style.width = source.offsetWidth + 'px';
+    if(options.setHeight) target.style.height = source.offsetHeight + 'px';
+  },
+
+  absolutize: function(element) {
+    element = $(element);
+    if (element.style.position == 'absolute') return;
+    Position.prepare();
+
+    var offsets = Position.positionedOffset(element);
+    var top     = offsets[1];
+    var left    = offsets[0];
+    var width   = element.clientWidth;
+    var height  = element.clientHeight;
+
+    element._originalLeft   = left - parseFloat(element.style.left  || 0);
+    element._originalTop    = top  - parseFloat(element.style.top || 0);
+    element._originalWidth  = element.style.width;
+    element._originalHeight = element.style.height;
+
+    element.style.position = 'absolute';
+    element.style.top    = top + 'px';
+    element.style.left   = left + 'px';
+    element.style.width  = width + 'px';
+    element.style.height = height + 'px';
+  },
+
+  relativize: function(element) {
+    element = $(element);
+    if (element.style.position == 'relative') return;
+    Position.prepare();
+
+    element.style.position = 'relative';
+    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
+    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
+
+    element.style.top    = top + 'px';
+    element.style.left   = left + 'px';
+    element.style.height = element._originalHeight;
+    element.style.width  = element._originalWidth;
+  }
+}
+
+// Safari returns margins on body which is incorrect if the child is absolutely
+// positioned.  For performance reasons, redefine Position.cumulativeOffset for
+// KHTML/WebKit only.
+if (Prototype.Browser.WebKit) {
+  Position.cumulativeOffset = function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      if (element.offsetParent == document.body)
+        if (Element.getStyle(element, 'position') == 'absolute') break;
+
+      element = element.offsetParent;
+    } while (element);
+
+    return [valueL, valueT];
+  }
+}
+
+Element.addMethods();
\ No newline at end of file
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/rico.js b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/rico.js
new file mode 100644 (file)
index 0000000..00e187e
--- /dev/null
@@ -0,0 +1,214 @@
+/**
+  *
+  *  Copyright 2005 Sabre Airline Solutions
+  *
+  *  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.
+  **/
+
+
+// This module does NOT depend on prototype.js
+
+var Rico = {
+  Version: '2.0 beta2',
+  loadRequested: 1,
+  loadComplete: 2,
+  init : function() {
+    try {  // fix IE background image flicker (credit: www.mister-pixel.com)
+      document.execCommand("BackgroundImageCache", false, true);
+    } catch(err) {}
+    this.preloadMsgs='';
+    var elements = document.getElementsByTagName('script');
+    this.baseHref= location.protocol + "//" + location.host;
+    this.loadedFiles={};
+    this.loadQueue=[];    
+    this.windowIsLoaded=false;
+    this.onLoadCallbacks=[];
+    for (var i=0; i<elements.length; i++) {
+      if (!elements[i].src) continue;
+      var src = elements[i].src;
+      var slashIdx = src.lastIndexOf('/');
+      var path = src.substring(0, slashIdx+1);
+      var filename = src.substring(slashIdx+1);
+      this.loadedFiles[filename]=this.loadComplete;
+      if (filename == 'rico.js') {
+        this.jsDir = path;
+        this.cssDir= path+'css/';
+        this.imgDir= path+'images/';
+        this.htmDir= path;
+        this.xslDir= path;
+      }
+    }
+    if (typeof Prototype=='undefined')
+      this.include('prototype.js');
+    this.include('ricoCommon.js');
+    var func=function() { Rico.windowLoaded(); };
+    if (window.addEventListener)
+      window.addEventListener('load', func, false);
+    else if (window.attachEvent)
+      window.attachEvent('onload', func);
+    this.onLoad(function() { Rico.writeDebugMsg('Pre-load messages:\n'+Rico.preloadMsgs); });
+  },
+  
+  // Array entries can reference a javascript file or css stylesheet
+  // A dependency on another module can be indicated with a plus-sign prefix: '+DependsOnModule'
+  moduleDependencies : {
+    Accordion  : ['ricoBehaviors.js','ricoEffects.js','ricoComponents.js'],
+    Color      : ['ricoColor.js'],
+    Corner     : ['ricoStyles.js'],
+    DragAndDrop: ['ricoDragDrop.js'],
+    Effect     : ['ricoEffects.js'],
+    Calendar   : ['ricoCalendar.js', 'ricoCalendar.css'],
+    Tree       : ['ricoTree.js', 'ricoTree.css'],
+    ColorPicker: ['ricoColorPicker.js', 'ricoStyles.js'],
+    SimpleGrid : ['ricoCommon.js', 'ricoGridCommon.js', 'ricoGrid.css', 'ricoSimpleGrid.js'],
+    LiveGrid   : ['ricoCommon.js', 'ricoGridCommon.js', 'ricoGrid.css', 'ricoBehaviors.js', 'ricoLiveGrid.js'],
+    CustomMenu : ['ricoMenu.js', 'ricoMenu.css'],
+    LiveGridMenu : ['+CustomMenu', 'ricoLiveGridMenu.js'],
+    LiveGridAjax : ['+LiveGrid', 'ricoLiveGridAjax.js'],
+    LiveGridForms: ['+LiveGridAjax', '+LiveGridMenu', '+Accordion', '+Corner', 'ricoLiveGridForms.js', 'ricoLiveGridForms.css'],
+    SpreadSheet  : ['+SimpleGrid', 'ricoSheet.js']
+  },
+  
+  // not reliable when used with XSLT
+  loadModule : function(name) {
+    var dep=this.moduleDependencies[name];
+    if (!dep) return;
+    for (var i=0; i<dep.length; i++)
+      if (dep[i].substring(0,1)=='+')
+        this.loadModule(dep[i].slice(1));
+      else
+        this.include(dep[i]);
+  },
+  
+  // not reliable when used with XSLT
+  include : function(filename) {
+    if (this.loadedFiles[filename]) return;
+    this.addPreloadMsg('include: '+filename);
+    var ext = filename.substr(filename.lastIndexOf('.')+1);
+    switch (ext.toLowerCase()) {
+      case 'js':
+        this.loadQueue.push(filename);
+        this.loadedFiles[filename]=this.loadRequested;
+        this.checkLoadQueue();
+        return;
+      case 'css':
+        var el = document.createElement('link');
+        el.type = 'text/css';
+        el.rel = 'stylesheet'
+        el.href = this.cssDir+filename;
+        this.loadedFiles[filename]=this.loadComplete;
+        document.getElementsByTagName('head')[0].appendChild(el);
+        return;
+    }
+  },
+  
+  checkLoadQueue: function() {
+    if (this.loadQueue.length==0) return;
+    if (this.inProcess) return;  // seems to only be required by IE, but applied to all browsers just to be safe
+    this.addScriptToDOM(this.loadQueue.shift());
+  },
+  
+  addScriptToDOM: function(filename) {
+    this.addPreloadMsg('addScriptToDOM: '+filename);
+    var js = document.createElement('script');
+    js.type = 'text/javascript';
+    js.src = this.jsDir+filename;
+    this.loadedFiles[filename]=this.loadRequested;
+    this.inProcess=filename;
+    var head=document.getElementsByTagName('head')[0];
+    if (filename.substring(0,4)=='rico') {
+      head.appendChild(js);
+    } else if(/WebKit|Khtml/i.test(navigator.userAgent)) {
+      head.appendChild(js);
+      this.includeLoaded(filename);
+    } else {
+      js.onload = js.onreadystatechange = function() {
+        if (js.readyState && js.readyState != 'loaded' && js.readyState != 'complete') return;
+        js.onreadystatechange = js.onload = null;
+        Rico.includeLoaded(filename);
+      };
+      head.appendChild(js);
+    }
+  },
+  
+  // called after a script file has finished loading
+  includeLoaded: function(filename) {
+    this.addPreloadMsg('loaded: '+filename);
+    this.loadedFiles[filename]=this.loadComplete;
+    if (filename==this.inProcess) {
+      this.inProcess=null;
+      this.checkLoadQueue();
+      this.checkIfComplete();
+    }
+  },
+
+  // called by the document onload event
+  windowLoaded: function() {
+    this.windowIsLoaded=true;
+    this.checkIfComplete();
+  },
+  
+  checkIfComplete: function() {
+    var waitingFor=this.windowIsLoaded ? '' : 'window';
+    for(var filename in  this.loadedFiles) {
+      if (this.loadedFiles[filename]==this.loadRequested)
+        waitingFor+=' '+filename;
+    }
+    //window.status='waitingFor: '+waitingFor;
+    this.addPreloadMsg('waitingFor: '+waitingFor);
+    if (waitingFor.length==0) {
+      this.addPreloadMsg('Processing callbacks');
+      while (this.onLoadCallbacks.length > 0) {
+        var callback=this.onLoadCallbacks.pop();
+        if (callback) callback();
+      }
+    }
+  },
+  
+  onLoad: function(callback) {
+    this.onLoadCallbacks.push(callback);
+    this.checkIfComplete();
+  },
+
+  isKonqueror : navigator.userAgent.toLowerCase().indexOf("konqueror") >= 0,
+
+  // logging funtions
+   
+  startTime : new Date(),
+
+  timeStamp: function() {
+    var stamp = new Date();
+    return (stamp.getTime()-this.startTime.getTime())+": ";
+  },
+  
+  setDebugArea: function(id, forceit) {
+    if (!this.debugArea || forceit) {
+      var newarea=document.getElementById(id);
+      if (!newarea) return;
+      this.debugArea=newarea;
+      newarea.value='';
+    }
+  },
+
+  addPreloadMsg: function(msg) {
+    this.preloadMsgs+=Rico.timeStamp()+msg+"\n";
+  },
+
+  writeDebugMsg: function(msg, resetFlag) {
+    if (this.debugArea) {
+      if (resetFlag) this.debugArea.value='';
+      this.debugArea.value+=this.timeStamp()+msg+"\n";
+    }
+  }
+
+}
+
+Rico.init();
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoAjaxEngine.js b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoAjaxEngine.js
new file mode 100644 (file)
index 0000000..5363991
--- /dev/null
@@ -0,0 +1,178 @@
+//-------------------- ricoAjaxEngine.js
+Rico.AjaxEngine = Class.create();
+
+Rico.AjaxEngine.prototype = {
+
+   initialize: function() {
+      this.ajaxElements = new Array();
+      this.ajaxObjects  = new Array();
+      this.requestURLS  = new Array();
+      this.options = {};
+   },
+
+   registerAjaxElement: function( anId, anElement ) {
+      if ( !anElement )
+         anElement = $(anId);
+      this.ajaxElements[anId] = anElement;
+   },
+
+   registerAjaxObject: function( anId, anObject ) {
+      this.ajaxObjects[anId] = anObject;
+   },
+
+   registerRequest: function (requestLogicalName, requestURL) {
+      this.requestURLS[requestLogicalName] = requestURL;
+   },
+
+   sendRequest: function(requestName, options) {
+      // Allow for backwards Compatibility
+      if ( arguments.length >= 2 )
+       if (typeof arguments[1] == 'string')
+         options = {parameters: this._createQueryString(arguments, 1)};
+      this.sendRequestWithData(requestName, null, options);
+   },
+
+   sendRequestWithData: function(requestName, xmlDocument, options) {
+      var requestURL = this.requestURLS[requestName];
+      if ( requestURL == null )
+         return;
+
+      // Allow for backwards Compatibility
+      if ( arguments.length >= 3 )
+        if (typeof arguments[2] == 'string')
+          options.parameters = this._createQueryString(arguments, 2);
+
+      new Ajax.Request(requestURL, this._requestOptions(options,xmlDocument));
+   },
+
+   sendRequestAndUpdate: function(requestName,container,options) {
+      // Allow for backwards Compatibility
+      if ( arguments.length >= 3 )
+        if (typeof arguments[2] == 'string')
+          options.parameters = this._createQueryString(arguments, 2);
+
+      this.sendRequestWithDataAndUpdate(requestName, null, container, options);
+   },
+
+   sendRequestWithDataAndUpdate: function(requestName,xmlDocument,container,options) {
+      var requestURL = this.requestURLS[requestName];
+      if ( requestURL == null )
+         return;
+
+      // Allow for backwards Compatibility
+      if ( arguments.length >= 4 )
+        if (typeof arguments[3] == 'string')
+          options.parameters = this._createQueryString(arguments, 3);
+
+      var updaterOptions = this._requestOptions(options,xmlDocument);
+
+      new Ajax.Updater(container, requestURL, updaterOptions);
+   },
+
+   // Private -- not part of intended engine API --------------------------------------------------------------------
+
+   _requestOptions: function(options,xmlDoc) {
+      var requestHeaders = ['X-Rico-Version', Rico.Version ];
+      var sendMethod = 'post';
+      if ( xmlDoc == null )
+        if (Rico.prototypeVersion < 1.4)
+        requestHeaders.push( 'Content-type', 'text/xml' );
+      else
+          sendMethod = 'get';
+      (!options) ? options = {} : '';
+
+      if (!options._RicoOptionsProcessed){
+      // Check and keep any user onComplete functions
+        if (options.onComplete)
+             options.onRicoComplete = options.onComplete;
+        // Fix onComplete
+        if (options.overrideOnComplete)
+          options.onComplete = options.overrideOnComplete;
+        else
+          options.onComplete = this._onRequestComplete.bind(this);
+        options._RicoOptionsProcessed = true;
+      }
+
+     // Set the default options and extend with any user options
+     this.options = {
+                     requestHeaders: requestHeaders,
+                     parameters:     options.parameters,
+                     postBody:       xmlDoc,
+                     method:         sendMethod,
+                     onComplete:     options.onComplete
+                    };
+     // Set any user options:
+     Object.extend(this.options, options);
+     return this.options;
+   },
+
+   _createQueryString: function( theArgs, offset ) {
+      var queryString = ""
+      for ( var i = offset ; i < theArgs.length ; i++ ) {
+          if ( i != offset )
+            queryString += "&";
+
+          var anArg = theArgs[i];
+
+          if ( anArg.name != undefined && anArg.value != undefined ) {
+            queryString += anArg.name +  "=" + escape(anArg.value);
+          }
+          else {
+             var ePos  = anArg.indexOf('=');
+             var argName  = anArg.substring( 0, ePos );
+             var argValue = anArg.substring( ePos + 1 );
+             queryString += argName + "=" + escape(argValue);
+          }
+      }
+      return queryString;
+   },
+
+   _onRequestComplete : function(request) {
+      if(!request)
+          return;
+      // User can set an onFailure option - which will be called by prototype
+      if (request.status != 200)
+        return;
+
+      var response = request.responseXML.getElementsByTagName("ajax-response");
+      if (response == null || response.length != 1)
+         return;
+      this._processAjaxResponse( response[0].childNodes );
+      
+      // Check if user has set a onComplete function
+      var onRicoComplete = this.options.onRicoComplete;
+      if (onRicoComplete != null)
+          onRicoComplete();
+   },
+
+   _processAjaxResponse: function( xmlResponseElements ) {
+      for ( var i = 0 ; i < xmlResponseElements.length ; i++ ) {
+         var responseElement = xmlResponseElements[i];
+
+         // only process nodes of type element.....
+         if ( responseElement.nodeType != 1 )
+            continue;
+
+         var responseType = responseElement.getAttribute("type");
+         var responseId   = responseElement.getAttribute("id");
+
+         if ( responseType == "object" )
+            this._processAjaxObjectUpdate( this.ajaxObjects[ responseId ], responseElement );
+         else if ( responseType == "element" )
+            this._processAjaxElementUpdate( this.ajaxElements[ responseId ], responseElement );
+         else
+            alert('unrecognized AjaxResponse type : ' + responseType );
+      }
+   },
+
+   _processAjaxObjectUpdate: function( ajaxObject, responseElement ) {
+      ajaxObject.ajaxUpdate( responseElement );
+   },
+
+   _processAjaxElementUpdate: function( ajaxElement, responseElement ) {
+      ajaxElement.innerHTML = RicoUtil.getContentAsString(responseElement);
+   }
+
+}
+
+Rico.includeLoaded('ricoAjaxEngine.js');
\ No newline at end of file
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoBehaviors.js b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoBehaviors.js
new file mode 100644 (file)
index 0000000..ed5d572
--- /dev/null
@@ -0,0 +1,188 @@
+/**
+  *  (c) 2005-2007 Richard Cowin (http://openrico.org)
+  *
+  *  Rico is 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
+  **/
+
+
+Rico.selectionSet = function(set, options){
+  new Rico.SelectionSet(set, options)
+}
+
+Rico.SelectionSet = Class.create(); 
+Rico.SelectionSet.prototype = {
+       initialize: function(selectionSet, options){
+               this.options = options || {}
+    if (typeof selectionSet == 'string')
+      selectionSet = $$(selectionSet)
+         this.previouslySelected = [];
+               this.selectionSet = selectionSet;
+               this.selectedClassName = this.options.selectedClass || "selected";
+               this.selectNode = this.options.selectNode || function(e){return e};
+               this.onSelect = this.options.onSelect;
+    this.onFirstSelect = this.options.onFirstSelect;
+               this.clickHandler = this.click.bind(this);
+               selectionSet.each(function(e) {Event.observe(e, "click", new Rico.EventWrapper(this.clickHandler,e).wrapper);}.bind(this))
+    if (!this.options.noDefault)
+                 this.selectIndex(this.options.selectedIndex || 0)
+       },
+       reset: function(){
+         this.previouslySelected = [];
+         this.notifySelected(this.selected);
+       },
+       select: function(element){
+               if (this.selected == element)
+                       return;
+
+               if (this.selected)
+                 new Element.ClassNames(this.selectNode(this.selected)).remove(this.selectedClassName)
+    
+    this.notifySelected(element)
+
+               this.selected = element;
+               new Element.ClassNames(this.selectNode(this.selected)).add(this.selectedClassName)
+       },
+       notifySelected: function(element){
+    var index = this.selectionSet.indexOf(element)
+    if (this.onFirstSelect && !this.previouslySelected[index]){
+      this.onFirstSelect(element, index)
+      this.previouslySelected[index] = true;
+    }
+       if (this.onSelect)
+      try{
+           this.onSelect(element, index)
+      } catch (e) {}
+       },
+       selectIndex: function(index){
+               this.select(this.selectionSet[index])
+       },  
+       nextSelectItem: function(index){
+    var index = this.selectionSet.indexOf(this.selected)
+    if (index + 1 >= this.selectionSet.length)
+      return this.selectionSet[index - 1];
+    else
+      return this.selectionSet[index + 1];       
+       },
+       selectNext: function(){
+    var index = this.selectionSet.indexOf(this.selected)
+    if (index >= this.selectionSet.length)
+      this.selectIndex(index - 1)
+    else
+      this.selectIndex(index + 1)
+       },
+       click: function(event,target) {
+               this.select(target);
+       },
+       add: function(item){
+       //      this.selectionSet.push(item)
+         if (item.constructur == Array)
+           item.each(function(e){
+               Event.observe(e, "click", new Rico.EventWrapper(this.clickHandler,item).wrapper);
+           }.bind(this))
+         else
+                 Event.observe(item, "click", new Rico.EventWrapper(this.clickHandler,item).wrapper);
+       },
+       remove: function(item){
+         this.selectionSet = this.selectionSet.without(item)
+                       //Todo: need to cleanup all events on item - need to keep track of eventwrappers
+       },
+       removeAll: function(){
+               
+       }
+ }
+
+Rico.HoverSet = Class.create();
+Rico.HoverSet.prototype = {
+    initialize: function(hoverSet, options){
+      options = options || [];
+      this.hoverSet = hoverSet;
+      this.hoverClassName = options.hoverClass || "hover";
+      this.hoverNodes = options.hoverNodes || function(e){return [e]};
+               this.listenerHover    = this._onHover.bind(this)
+      this.listenerEndHover = this._onUnHover.bind(this)
+      
+         this.hoverSet.each((function(e) {Event.observe(e, "mousemove", new Rico.EventWrapper(this.listenerHover,e).wrapper);}).bind(this))
+         this.hoverSet.each((function(e) {Event.observe(e, "mouseout", new Rico.EventWrapper(this.listenerEndHover,e).wrapper);}).bind(this))  
+       },
+       _onHover: function(event,target) {
+         this.hover(target);
+       },      
+       _onUnHover: function(event,target) {
+         this.unHover(target);
+       },
+       hover: function(target) {
+         this.hoverNodes(target).each((function(t){Element.classNames(t).add(this.hoverClassName)}).bind(this));
+       },      
+       unHover: function(target) {
+         this.hoverNodes(target).each((function(t){Element.classNames(t).remove(this.hoverClassName)}).bind(this));
+       },
+               add: function(item){
+         Event.observe(item, "mousemove", new Rico.EventWrapper(this.listenerHover,item).wrapper);
+         Event.observe(item, "mouseout", new Rico.EventWrapper(this.listenerEndHover,item).wrapper);
+               },
+               remove: function(item){
+                       //Todo: need to cleanup all events on item - need to keep terack of eventwrappers
+                       //stopObserving
+                       //Event.stopObserving(e, "mousemove", new Rico.EventWrapper(this.listenerHover,e).wrapper);}).bind(this))
+         //this.hoverSet.each((function(e) {Event.observe(e, "mouseout", new Rico.EventWrapper(this.listenerEndHover,e).wrapper);}).bind(this))
+                       //hoverSet
+               },
+               removeAll: function(item){
+               }
+}
+
+Rico.Hover = {
+  groups: {},
+  clearCurrent: function(group) {
+    var last_hover = Rico.Hover.groups[group];
+    if(!last_hover) return  
+    clearTimeout(last_hover[0])
+    last_hover[1].end()
+    Rico.Hover.groups[group] = null;
+  }, 
+  end: function(group) {
+       Rico.Hover.groups[group][1].end();
+  },
+  endWith: function(hover, group) {
+       var timer = setTimeout('Rico.Hover.end("'+ group + '")', hover.exitDelay)
+    Rico.Hover.groups[group] = [timer, hover]
+  }
+}
+
+Rico.HoverDisplay = Class.create();
+Rico.HoverDisplay.prototype = {
+  initialize: function(element, options) {
+       this.element = element;
+       this.options = options || {};
+       this.group = this.options.group;
+       this.exitDelay = this.options.delay || 1000;
+  },
+  begin: function() {
+    Rico.Hover.clearCurrent(this.group)
+               Element.show(this.element)
+  },
+  end: function(delay) {
+    if(delay)
+               Rico.Hover.endWith(this, this.group);
+    else 
+                 Element.hide(this.element)              
+  }
+}
+
+
+Rico.EventWrapper = Class.create();
+Rico.EventWrapper.prototype = {
+  initialize: function(handler, target){
+    this.handler = handler;
+    this.target = target;
+    this.wrapper = this.wrapperCall.bindAsEventListener(this)
+  },
+  wrapperCall: function(event){
+    this.handler(event, this.target)
+  }
+}
+
+Rico.includeLoaded('ricoBehaviors.js');
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoCalendar.js b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoCalendar.js
new file mode 100644 (file)
index 0000000..8aa596c
--- /dev/null
@@ -0,0 +1,443 @@
+//  By Matt Brown\r
+//  June-October 2006\r
+//  email: dowdybrown@yahoo.com\r
+//  Implements a pop-up Gregorian calendar.\r
+//  Dates of adoption of the Gregorian calendar vary by country - accurate as a US & British calendar from 14 Sept 1752 to present.\r
+//  Mark special dates with calls to addHoliday()\r
+//  Inspired by code originally written by Tan Ling Wee on 2 Dec 2001\r
+\r
+//  Requires prototype.js and ricoCommon.js\r
+\r
+Rico.CalendarControl = Class.create();\r
+\r
+Rico.CalendarControl.prototype = {\r
+\r
+  initialize: function(id,options) {\r
+    this.id=id;\r
+    var today=new Date();\r
+    Object.extend(this, new Rico.Popup({ignoreClicks:true}));\r
+    Object.extend(this.options, {\r
+      startAt : 0,           // week starts with 0=sunday, 1=monday\r
+      showWeekNumber : 0,    // show week number in first column?\r
+      showToday : 1,         // show "Today is..." in footer?\r
+      cursorColor: '#FDD',   // color used to highlight dates as the user moves their mouse\r
+      repeatInterval : 100,  // when left/right arrow is pressed, repeat action every x milliseconds\r
+      dateFmt : 'ISO8601',   // default is ISO-8601, 'rico'=use format stored in ricoTranslate object\r
+      selectedDateBorder : "#666666",  // border to indicate currently selected date\r
+      minDate : new Date(today.getFullYear()-50,0,1),  // default to +-50 yrs from current date\r
+      maxDate : new Date(today.getFullYear()+50,11,31)\r
+    });\r
+    Object.extend(this.options, options || {});\r
+    this.close=this.closePopup;\r
+    this.bPageLoaded=false;\r
+    this.img=new Array();\r
+    this.Holidays={};\r
+    this.todayString=RicoTranslate.getPhrase("Today is ");\r
+    this.weekString=RicoTranslate.getPhrase("Wk");\r
+    if (this.options.dateFmt=='rico') this.options.dateFmt=RicoTranslate.dateFmt;\r
+    this.dateParts=new Array();\r
+    this.re=/^\s*(\w+)(\W)(\w+)(\W)(\w+)/i;\r
+    if (this.re.exec(this.options.dateFmt)) {\r
+      this.dateParts[RegExp.$1]=0;\r
+      this.dateParts[RegExp.$3]=1;\r
+      this.dateParts[RegExp.$5]=2;\r
+    }\r
+  },\r
+\r
+\r
+  // y=0 implies a repeating holiday\r
+  addHoliday : function(d, m, y, desc, bgColor, txtColor) {\r
+    this.Holidays[this.holidayKey(y,m-1,d)]={desc:desc, txtColor:txtColor, bgColor:bgColor || '#DDF'};\r
+  },\r
+  \r
+  holidayKey : function(y,m,d) {\r
+    return 'h'+y.toPaddedString(4)+m.toPaddedString(2)+d.toPaddedString(2);\r
+  },\r
+\r
+  atLoad : function() {\r
+    this.container=document.createElement("div");\r
+    this.container.style.display="none"\r
+    this.container.id=this.id;\r
+    this.container.className='ricoCalContainer';\r
+\r
+    this.maintab=document.createElement("table");\r
+    this.maintab.cellSpacing=0;\r
+    this.maintab.cellPadding=0;\r
+    this.maintab.border=0;\r
+    this.maintab.className='ricoCalTab';\r
+\r
+    for (var i=0; i<7; i++) {\r
+      var r=this.maintab.insertRow(-1);\r
+      r.className='row'+i;\r
+      for (var c=0; c<8; c++)\r
+        r.insertCell(-1);\r
+    }\r
+    this.tbody=this.maintab.tBodies[0];\r
+    var r=this.tbody.rows[0];\r
+    r.className='ricoCalDayNames';\r
+    if (this.options.showWeekNumber) {\r
+      r.cells[0].innerHTML=this.weekString;\r
+      for (var i=0; i<7; i++)\r
+        this.tbody.rows[i].cells[0].className='ricoCalWeekNum';\r
+    }\r
+    this.styles=[];\r
+    for (var i=0; i<7; i++) {\r
+      var dow=(i+this.options.startAt) % 7;\r
+      r.cells[i+1].innerHTML=RicoTranslate.dayNames[dow].substring(0,3);\r
+      this.styles[i+1]='ricoCal'+dow;\r
+    }\r
+    \r
+    // table header (navigation controls)\r
+    this.thead=this.maintab.createTHead()\r
+    var r=this.thead.insertRow(-1);\r
+    var c=r.insertCell(-1);\r
+    c.colSpan=8;\r
+    var img=this.createNavArrow('decMonth','left');\r
+    c.appendChild(document.createElement("a")).appendChild(img);\r
+    this.titleMonth=document.createElement("a");\r
+    c.appendChild(this.titleMonth);\r
+    Event.observe(this.titleMonth,"click", this.popUpMonth.bindAsEventListener(this), false);\r
+    var img=this.createNavArrow('incMonth','right');\r
+    c.appendChild(document.createElement("a")).appendChild(img);\r
+    var s=document.createElement("span");\r
+    s.innerHTML='&nbsp;';\r
+    s.style.paddingLeft='3em';\r
+    c.appendChild(s);\r
+\r
+    var img=this.createNavArrow('decYear','left');\r
+    c.appendChild(document.createElement("a")).appendChild(img);\r
+    this.titleYear=document.createElement("a");\r
+    Event.observe(this.titleYear,"click", this.popUpYear.bindAsEventListener(this), false);\r
+    c.appendChild(this.titleYear);\r
+    var img=this.createNavArrow('incYear','right');\r
+    c.appendChild(document.createElement("a")).appendChild(img);\r
+\r
+    // table footer (today)\r
+    if (this.options.showToday) {\r
+      this.tfoot=this.maintab.createTFoot()\r
+      var r=this.tfoot.insertRow(-1);\r
+      this.todayCell=r.insertCell(-1);\r
+      this.todayCell.colSpan=8;\r
+      Event.observe(this.todayCell,"click", this.selectNow.bindAsEventListener(this), false);\r
+    }\r
+    \r
+\r
+    this.container.appendChild(this.maintab);\r
+    \r
+    // close icon (upper right)\r
+    var img=document.createElement("img");\r
+    img.src=Rico.imgDir+'close.gif';\r
+    img.onclick=this.close.bind(this);\r
+    img.style.cursor='pointer';\r
+    img.style.position='absolute';\r
+    img.style.top='1px';   /* assumes a 1px border */\r
+    img.style.right='1px';\r
+    this.container.appendChild(img);\r
+    \r
+    // month selector\r
+    this.monthSelect=document.createElement("table");\r
+    this.monthSelect.className='ricoCalMenu';\r
+    this.monthSelect.cellPadding=2;\r
+    this.monthSelect.cellSpacing=0;\r
+    this.monthSelect.border=0;\r
+    for (var i=0; i<4; i++) {\r
+      var r=this.monthSelect.insertRow(-1);\r
+      for (var j=0; j<3; j++) {\r
+        var c=r.insertCell(-1);\r
+        var a=document.createElement("a");\r
+        a.innerHTML=RicoTranslate.monthNames[i*3+j].substring(0,3);\r
+        a.name=i*3+j;\r
+        c.appendChild(a);\r
+        Event.observe(a,"click", this.selectMonth.bindAsEventListener(this), false);\r
+      }\r
+    }\r
+    this.monthSelect.style.display='none';\r
+    this.container.appendChild(this.monthSelect);\r
+    \r
+    // fix anchors so they work in IE6\r
+    var a=this.container.getElementsByTagName('a');\r
+    for (var i=0; i<a.length; i++)\r
+      a[i].href='#';\r
+    \r
+    Event.observe(this.tbody,"click", this.saveAndClose.bindAsEventListener(this));\r
+    Event.observe(this.tbody,"mouseover", this.mouseOver.bindAsEventListener(this));
+    Event.observe(this.tbody,"mouseout",  this.mouseOut.bindAsEventListener(this));
+    document.getElementsByTagName("body")[0].appendChild(this.container);\r
+    this.setDiv(this.container);\r
+    this.close()\r
+    this.bPageLoaded=true\r
+  },\r
+  \r
+  selectNow : function() {\r
+    this.monthSelected=this.monthNow;\r
+    this.yearSelected=this.yearNow;\r
+    this.constructCalendar();\r
+  },\r
+  \r
+  createNavArrow: function(funcname,gifname) {\r
+    var img=document.createElement("img");\r
+    img.src=Rico.imgDir+gifname+'.gif';\r
+    img.name=funcname;\r
+    Event.observe(img,"click", this[funcname].bindAsEventListener(this), false);\r
+    Event.observe(img,"mousedown", this.mouseDown.bindAsEventListener(this), false);\r
+    Event.observe(img,"mouseup", this.mouseUp.bindAsEventListener(this), false);\r
+    Event.observe(img,"mouseout", this.mouseUp.bindAsEventListener(this), false);\r
+    return img\r
+  },\r
+\r
+  mouseOver: function(e) {\r
+    var el=Event.element(e);\r
+    if (this.lastHighlight==el) return;\r
+    this.unhighlight();\r
+    var s=el.innerHTML.replace(/&nbsp;/g,'');\r
+    if (s=='' || el.className=='ricoCalWeekNum') return;\r
+    var day=parseInt(s);\r
+    if (isNaN(day)) return;\r
+    this.lastHighlight=el;\r
+    this.tmpColor=el.style.backgroundColor;\r
+    el.style.backgroundColor=this.options.cursorColor;\r
+  },\r
+  \r
+  unhighlight: function() {\r
+    if (!this.lastHighlight) return;\r
+    this.lastHighlight.style.backgroundColor=this.tmpColor;\r
+    this.lastHighlight=null;\r
+  },\r
+  \r
+  mouseOut: function(e) {\r
+    var el=Event.element(e);\r
+    if (el==this.lastHighlight) this.unhighlight();\r
+  },\r
+  \r
+  mouseDown: function(e) {\r
+    var el=Event.element(e);\r
+    this.repeatFunc=this[el.name].bind(this);\r
+    this.timeoutID=setTimeout(this.repeatStart.bind(this),500);\r
+  },\r
+  \r
+  mouseUp: function(e) {\r
+    clearTimeout(this.timeoutID);\r
+    clearInterval(this.intervalID)\r
+  },\r
+  \r
+  repeatStart : function() {\r
+    clearInterval(this.intervalID);\r
+    this.intervalID=setInterval(this.repeatFunc,this.options.repeatInterval);\r
+  },\r
+  \r
+  // is yr/mo within minDate/MaxDate?\r
+  isValidMonth : function(yr,mo) {\r
+    if (yr < this.options.minDate.getFullYear()) return false;\r
+    if (yr == this.options.minDate.getFullYear() && mo < this.options.minDate.getMonth()) return false;\r
+    if (yr > this.options.maxDate.getFullYear()) return false;\r
+    if (yr == this.options.maxDate.getFullYear() && mo > this.options.maxDate.getMonth()) return false;\r
+    return true;\r
+  },\r
+\r
+  incMonth : function() {\r
+    var newMonth=this.monthSelected+1;\r
+    var newYear=this.yearSelected;\r
+    if (newMonth>11) {\r
+      newMonth=0;\r
+      newYear++;\r
+    }\r
+    if (!this.isValidMonth(newYear,newMonth)) return;\r
+    this.monthSelected=newMonth;\r
+    this.yearSelected=newYear;\r
+    this.constructCalendar()\r
+  },\r
+\r
+  decMonth : function() {\r
+    var newMonth=this.monthSelected-1;\r
+    var newYear=this.yearSelected;\r
+    if (newMonth<0) {\r
+      newMonth=11;\r
+      newYear--;\r
+    }\r
+    if (!this.isValidMonth(newYear,newMonth)) return;\r
+    this.monthSelected=newMonth;\r
+    this.yearSelected=newYear;\r
+    this.constructCalendar()\r
+  },\r
+  \r
+  selectMonth : function(e) {\r
+    var el=Event.element(e);\r
+    this.monthSelected=parseInt(el.name);\r
+    this.constructCalendar();\r
+    Event.stop(e);\r
+  },\r
+\r
+  popUpMonth : function() {\r
+    this.monthSelect.style.display=this.monthSelect.style.display=='none' ? 'block' : 'none';\r
+  },\r
+\r
+  popDownMonth : function() {\r
+    this.monthSelect.style.display='none';\r
+  },\r
+\r
+  /*** Year Pulldown ***/\r
+\r
+  popUpYear : function() {\r
+    var newYear=prompt(RicoTranslate.getPhrase("Year ("+this.options.minDate.getFullYear()+"-"+this.options.maxDate.getFullYear()+")"),this.yearSelected);\r
+    if (newYear==null) return;\r
+    newYear=parseInt(newYear);\r
+    if (isNaN(newYear) || newYear<this.options.minDate.getFullYear() || newYear>this.options.maxDate.getFullYear()) {\r
+      alert(RicoTranslate.getPhrase("Invalid year"));\r
+    } else {\r
+      this.yearSelected=newYear;\r
+      this.constructCalendar();\r
+    }\r
+  },\r
+  \r
+  incYear : function() {\r
+    if (this.yearSelected>=this.options.maxDate.getFullYear()) return;\r
+    this.yearSelected++;\r
+    this.constructCalendar();\r
+  },\r
+\r
+  decYear : function() {\r
+    if (this.yearSelected<=this.options.minDate.getFullYear()) return;\r
+    this.yearSelected--;\r
+    this.constructCalendar();\r
+  },\r
+\r
+  // tried a number of different week number functions posted on the net\r
+  // this is the only one that produced consistent results when comparing week numbers for December and the following January\r
+  WeekNbr : function(year,month,day) {\r
+    var when = new Date(year,month,day);\r
+    var newYear = new Date(year,0,1);\r
+    var offset = 7 + 1 - newYear.getDay();\r
+    if (offset == 8) offset = 1;\r
+    var daynum = ((Date.UTC(year,when.getMonth(),when.getDate(),0,0,0) - Date.UTC(year,0,1,0,0,0)) /1000/60/60/24) + 1;\r
+    var weeknum = Math.floor((daynum-offset+7)/7);\r
+    if (weeknum == 0) {\r
+        year--;\r
+        var prevNewYear = new Date(year,0,1);\r
+        var prevOffset = 7 + 1 - prevNewYear.getDay();\r
+        if (prevOffset == 2 || prevOffset == 8) weeknum = 53; else weeknum = 52;\r
+    }\r
+    return weeknum;\r
+  },\r
+\r
+  constructCalendar : function() {\r
+    var aNumDays = Array (31,0,31,30,31,30,31,31,30,31,30,31)\r
+    var startDate = new Date (this.yearSelected,this.monthSelected,1)\r
+    var endDate,numDaysInMonth\r
+\r
+    if (typeof this.monthSelected!='number' || this.monthSelected>=12 || this.monthSelected<0) {\r
+      alert('ERROR in calendar: monthSelected='+this.monthSelected);\r
+      return;\r
+    }\r
+    var today = new Date();\r
+    this.dateNow  = today.getDate();\r
+    this.monthNow = today.getMonth();\r
+    this.yearNow  = today.getFullYear();\r
+\r
+    if (this.monthSelected==1) {\r
+      endDate = new Date (this.yearSelected,this.monthSelected+1,1);\r
+      endDate = new Date (endDate - (24*60*60*1000));\r
+      numDaysInMonth = endDate.getDate()\r
+    } else {\r
+      numDaysInMonth = aNumDays[this.monthSelected];\r
+    }\r
+    var dayPointer = startDate.getDay() - this.options.startAt\r
+    if (dayPointer<0) dayPointer+=7;\r
+    this.popDownMonth();\r
+\r
+    this.bgcolor=Element.getStyle(this.tbody,'background-color');\r
+    this.bgcolor=this.bgcolor.replace(/\"/g,'');\r
+    if (this.options.showWeekNumber) {\r
+      for (var i=1; i<7; i++)\r
+        this.tbody.rows[i].cells[0].innerHTML='&nbsp;';\r
+    }\r
+    for ( var i=1; i<=dayPointer; i++ )\r
+      this.resetCell(this.tbody.rows[1].cells[i]);\r
+\r
+    for ( var datePointer=1,r=1; datePointer<=numDaysInMonth; datePointer++,dayPointer++ ) {\r
+      var colnum=dayPointer % 7 + 1;\r
+      if (this.options.showWeekNumber==1 && colnum==1)\r
+        this.tbody.rows[r].cells[0].innerHTML=this.WeekNbr(this.yearSelected,this.monthSelected,datePointer);\r
+      var dateClass=this.styles[colnum];\r
+      if ((datePointer==this.dateNow)&&(this.monthSelected==this.monthNow)&&(this.yearSelected==this.yearNow))\r
+        dateClass='ricoCalToday';\r
+      var c=this.tbody.rows[r].cells[colnum];\r
+      c.innerHTML="&nbsp;" + datePointer + "&nbsp;";\r
+      c.className=dateClass;\r
+      var bordercolor=(datePointer==this.odateSelected) && (this.monthSelected==this.omonthSelected) && (this.yearSelected==this.oyearSelected) ? this.options.selectedDateBorder : this.bgcolor;\r
+      c.style.border='1px solid '+bordercolor;\r
+      var h=this.Holidays[this.holidayKey(this.yearSelected,this.monthSelected,datePointer)];\r
+      if (!h)  h=this.Holidays[this.holidayKey(0,this.monthSelected,datePointer)];\r
+      c.style.color=h ? h.txtColor : '';\r
+      c.style.backgroundColor=h ? h.bgColor : '';\r
+      c.title=h ? h.desc : '';\r
+      if (colnum==7) r++;\r
+    }\r
+    while (dayPointer<42) {\r
+      var colnum=dayPointer % 7 + 1;\r
+      this.resetCell(this.tbody.rows[r].cells[colnum]);\r
+      dayPointer++;\r
+      if (colnum==7) r++;\r
+    }\r
+\r
+    this.titleMonth.innerHTML = RicoTranslate.monthNames[this.monthSelected].substring(0,3);\r
+    this.titleYear.innerHTML = this.yearSelected;\r
+    if (this.options.showToday)\r
+      this.todayCell.innerHTML=this.todayString+'<span>'+this.dateNow + " " + RicoTranslate.monthNames[this.monthNow].substring(0,3) + " " + this.yearNow+'</span>';\r
+    this.monthSelect.style.top=this.thead.offsetHeight+'px';\r
+    this.monthSelect.style.left=this.titleMonth.offsetLeft+'px';\r
+  },\r
+  \r
+  resetCell: function(c) {\r
+    c.innerHTML="&nbsp;";\r
+    c.className='ricoCalEmpty';\r
+    c.style.border='1px solid '+this.bgcolor;\r
+    c.style.color='';\r
+    c.style.backgroundColor='';\r
+    c.title='';\r
+  },\r
+  \r
+  saveAndClose : function(e) {\r
+    Event.stop(e);\r
+    var el=Event.element(e);\r
+    var s=el.innerHTML.replace(/&nbsp;/g,'');\r
+    if (s=='' || el.className=='ricoCalWeekNum') return;\r
+    var day=parseInt(s);\r
+    if (isNaN(day)) return;\r
+    var d=new Date(this.yearSelected,this.monthSelected,day);\r
+    var dateStr=d.formatDate(this.options.dateFmt=='ISO8601' ? 'yyyy-mm-dd' : this.options.dateFmt);\r
+    if (this.returnValue) this.returnValue(dateStr);\r
+    this.close();\r
+  },\r
+\r
+  open : function(curval) {\r
+    if (!this.bPageLoaded) return;\r
+    if (typeof curval=='object') {\r
+      this.dateSelected  = curval.getDate();\r
+      this.monthSelected = curval.getMonth();\r
+      this.yearSelected  = curval.getFullYear();\r
+    } else if (this.options.dateFmt=='ISO8601') {\r
+      var d=new Date;\r
+      d.setISO8601(curval);\r
+      this.dateSelected  = d.getDate();\r
+      this.monthSelected = d.getMonth();\r
+      this.yearSelected  = d.getFullYear();\r
+    } else if (this.re.exec(curval)) {\r
+      var aDate=new Array(RegExp.$1,RegExp.$3,RegExp.$5);\r
+      this.dateSelected  = parseInt(aDate[this.dateParts['dd']], 10);\r
+      this.monthSelected = parseInt(aDate[this.dateParts['mm']], 10) - 1;\r
+      this.yearSelected  = parseInt(aDate[this.dateParts['yyyy']], 10);\r
+    } else {\r
+      if (curval) alert('ERROR: invalid date passed to calendar ('+curval+')');\r
+      this.dateSelected  = this.dateNow\r
+      this.monthSelected = this.monthNow\r
+      this.yearSelected  = this.yearNow\r
+    }\r
+    this.odateSelected=this.dateSelected\r
+    this.omonthSelected=this.monthSelected\r
+    this.oyearSelected=this.yearSelected\r
+    this.constructCalendar();\r
+    this.openPopup();\r
+  }\r
+}\r
+\r
+Rico.includeLoaded('ricoCalendar.js');
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoColor.js b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoColor.js
new file mode 100644 (file)
index 0000000..84c3b98
--- /dev/null
@@ -0,0 +1,29 @@
+function attachValueChangeListeners() {
+   $('red').onkeypress   = colorChangedDeffered;
+   $('green').onkeypress = colorChangedDeffered;
+   $('blue').onkeypress  = colorChangedDeffered;
+}
+
+function colorChangedDeffered() {
+  setTimeout( colorChanged, 1 );
+}
+
+function colorChanged() {
+   var red   = Math.min( parseInt($('red').value)   || 0, 255);
+   var green = Math.min( parseInt($('green').value) || 0, 255);
+   var blue  = Math.min( parseInt($('blue').value)  || 0, 255);
+
+   var color = new Rico.Color( red, green, blue );
+
+   var newIllustrateString = "&nbsp;var color = new Rico.Color( ";
+   newIllustrateString += red + ", ";
+   newIllustrateString += green + ", ";
+   newIllustrateString += blue + " ); // color.asHex() = ";
+   newIllustrateString += color.asHex();
+
+   $('rgbCode').innerHTML = newIllustrateString;
+   $('colorBox').style.backgroundColor = color.asHex();
+   //$('colorBox').innerHTML = color.asHex();
+}
+
+Rico.includeLoaded('ricoColor.js');
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoColorPicker.js b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoColorPicker.js
new file mode 100644 (file)
index 0000000..86ab657
--- /dev/null
@@ -0,0 +1,75 @@
+// ===================================================================\r
+// Original author: Matt Kruse <matt@mattkruse.com>\r
+// WWW: http://www.mattkruse.com/\r
+//\r
+// Adapted to Rico by Matt Brown\r
+// ===================================================================\r
+\r
+\r
+Rico.ColorPicker = Class.create();\r
+\r
+Rico.ColorPicker.prototype = {\r
+\r
+  initialize: function(id,options) {\r
+    this.id=id;\r
+    this.currentValue = "#FFFFFF";\r
+    Object.extend(this, new Rico.Popup());
+    Object.extend(this.options, {\r
+      showColorCode : false,\r
+      cellsPerRow   : 18,\r
+      palette       : []\r
+    });
+    var hexvals=['00','33','66','99','CC','FF'];\r
+    for (var g=0; g<hexvals.length; g++)\r
+      for (var r=0; r<hexvals.length; r++)\r
+        for (var b=0; b<hexvals.length; b++)\r
+          this.options.palette.push(hexvals[r]+hexvals[g]+hexvals[b]);\r
+    Object.extend(this.options, options || {});\r
+  },\r
+\r
+  atLoad : function() {\r
+    this.container=document.createElement("div");\r
+    this.container.style.display="none"\r
+    this.container.className='ricoColorPicker';\r
+    var width = this.options.cellsPerRow;\r
+    var cp_contents = "<TABLE BORDER='1' CELLSPACING='1' CELLPADDING='0'>";\r
+    for (var i=0; i<this.options.palette.length; i++) {\r
+      if ((i % width) == 0) { cp_contents += "<TR>"; }\r
+      cp_contents += '<TD BGCOLOR="'+this.options.palette[i]+'">&nbsp;</TD>';\r
+      if ( ((i+1)>=this.options.palette.length) || (((i+1) % width) == 0))\r
+        cp_contents += "</TR>";\r
+    }\r
+    var halfwidth = Math.floor(width/2);\r
+    if (this.options.showColorCode)\r
+      cp_contents += "<TR><TD COLSPAN='"+halfwidth+"' ID='colorPickerSelectedColor'>&nbsp;</TD><TD COLSPAN='"+(width-halfwidth)+"' ALIGN='CENTER' ID='colorPickerSelectedColorValue'>#FFFFFF</TD></TR>";\r
+    else\r
+      cp_contents += "<TR><TD COLSPAN='"+width+"' ID='colorPickerSelectedColor'>&nbsp;</TD></TR>";\r
+    cp_contents += "</TABLE>";\r
+    this.container.innerHTML=cp_contents;\r
+    document.body.appendChild(this.container);\r
+    this.setDiv(this.container);\r
+    this.open=this.openPopup;\r
+    this.close=this.closePopup;\r
+    Event.observe(this.container,"mouseover", this.highlightColor.bindAsEventListener(this), false);\r
+    Event.observe(this.container,"click", this.selectColor.bindAsEventListener(this), false);\r
+    this.close();\r
+  },\r
+\r
+  selectColor: function(e) {\r
+    if (this.returnValue) this.returnValue(this.currentValue);\r
+    this.close();\r
+  },\r
+\r
+  // This function runs when you move your mouse over a color block, if you have a newer browser\r
+  highlightColor: function(e) {\r
+    var elem = Event.element(e);\r
+    if (!elem.tagName || elem.tagName.toLowerCase() != 'td') return;\r
+    var c=Rico.Color.createColorFromBackground(elem).toString();\r
+    this.currentValue = c;\r
+    Element.setStyle('colorPickerSelectedColor',{'background-color':c});\r
+    d = $("colorPickerSelectedColorValue");\r
+    if (d) d.innerHTML = c;\r
+  }\r
+}\r
+\r
+Rico.includeLoaded('ricoColorPicker.js');
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoCommon.js b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoCommon.js
new file mode 100644 (file)
index 0000000..0e6f4ea
--- /dev/null
@@ -0,0 +1,739 @@
+/**
+  *
+  *  Copyright 2005 Sabre Airline Solutions
+  *
+  *  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.
+  **/
+
+if (typeof Rico=='undefined')
+  throw("Cannot find the Rico object");
+if (typeof Prototype=='undefined')
+  throw("Rico requires the Prototype JavaScript framework");
+Rico.prototypeVersion = parseFloat(Prototype.Version.split(".")[0] + "." + Prototype.Version.split(".")[1]);
+if (Rico.prototypeVersion < 1.3)
+  throw("Rico requires Prototype JavaScript framework version 1.3 or greater");
+
+/** @singleton */
+var RicoUtil = {
+
+   getDirectChildrenByTag: function(e, tagName) {
+      var kids = new Array();
+      var allKids = e.childNodes;
+      tagName=tagName.toLowerCase();
+      for( var i = 0 ; i < allKids.length ; i++ )
+         if ( allKids[i] && allKids[i].tagName && allKids[i].tagName.toLowerCase() == tagName )
+            kids.push(allKids[i]);
+      return kids;
+   },
+
+   createXmlDocument : function() {
+      if (document.implementation && document.implementation.createDocument) {
+         var doc = document.implementation.createDocument("", "", null);
+
+         if (doc.readyState == null) {
+            doc.readyState = 1;
+            doc.addEventListener("load", function () {
+               doc.readyState = 4;
+               if (typeof doc.onreadystatechange == "function")
+                  doc.onreadystatechange();
+            }, false);
+         }
+
+         return doc;
+      }
+
+      if (window.ActiveXObject)
+          return Try.these(
+            function() { return new ActiveXObject('MSXML2.DomDocument')   },
+            function() { return new ActiveXObject('Microsoft.DomDocument')},
+            function() { return new ActiveXObject('MSXML.DomDocument')    },
+            function() { return new ActiveXObject('MSXML3.DomDocument')   }
+          ) || false;
+
+      return null;
+   },
+
+   getInnerText: function(el) {
+     if (typeof el == "string") return el;
+     if (typeof el == "undefined") { return el };
+     var cs = el.childNodes;
+     var l = cs.length;
+     var str = "";
+     for (var i = 0; i < l; i++) {
+       switch (cs[i].nodeType) {
+         case 1: //ELEMENT_NODE
+           if (Element.getStyle(cs[i],'display')=='none') continue;
+           switch (cs[i].tagName.toLowerCase()) {
+             case 'img':   str += cs[i].alt || cs[i].title || cs[i].src; break;
+             case 'input': if (cs[i].type=='hidden') continue;
+             case 'select':
+             case 'textarea': str += $F(cs[i]) || ''; break;
+             default:      str += this.getInnerText(cs[i]); break;
+           }
+           break;
+         case 3: //TEXT_NODE
+           str += cs[i].nodeValue;
+           break;
+       }
+     }
+     return str;
+   },
+
+   // For Konqueror 3.5, isEncoded must be true
+   getContentAsString: function( parentNode, isEncoded ) {
+      if (isEncoded) return this._getEncodedContent(parentNode);
+      if (typeof parentNode.xml != 'undefined') return this._getContentAsStringIE(parentNode);
+      return this._getContentAsStringMozilla(parentNode);
+   },
+
+   _getEncodedContent: function(parentNode) {
+      if (parentNode.innerHTML) return parentNode.innerHTML;
+      switch (parentNode.childNodes.length) {
+        case 0:  return "";
+        case 1:  return parentNode.firstChild.nodeValue;
+        default: return parentNode.childNodes[1].nodeValue;
+      }
+   },
+
+  _getContentAsStringIE: function(parentNode) {
+     var contentStr = "";
+     for ( var i = 0 ; i < parentNode.childNodes.length ; i++ ) {
+         var n = parentNode.childNodes[i];
+         if (n.nodeType == 4) {
+             contentStr += n.nodeValue;
+         }
+         else {
+           contentStr += n.xml;
+       }
+     }
+     return contentStr;
+  },
+
+  _getContentAsStringMozilla: function(parentNode) {
+     var xmlSerializer = new XMLSerializer();
+     var contentStr = "";
+     for ( var i = 0 ; i < parentNode.childNodes.length ; i++ ) {
+          var n = parentNode.childNodes[i];
+          if (n.nodeType == 4) { // CDATA node
+              contentStr += n.nodeValue;
+          }
+          else {
+            contentStr += xmlSerializer.serializeToString(n);
+        }
+     }
+     return contentStr;
+  },
+
+  docElement: function() {
+    return (document.compatMode && document.compatMode.indexOf("CSS")!=-1) ? document.documentElement : document.getElementsByTagName("body")[0];
+  },
+
+/**
+ * return available height - excludes scrollbar & margin
+ */
+  windowHeight: function() {
+    return window.innerHeight? window.innerHeight : this.docElement().clientHeight;
+    //return this.docElement().clientHeight;
+  },
+
+/**
+ * return available width - excludes scrollbar & margin
+ */
+  windowWidth: function() {
+    return this.docElement().clientWidth;
+  },
+
+  docScrollLeft: function() {
+     if ( window.pageXOffset )
+        return window.pageXOffset;
+     else if ( document.documentElement && document.documentElement.scrollLeft )
+        return document.documentElement.scrollLeft;
+     else if ( document.body )
+        return document.body.scrollLeft;
+     else
+        return 0;
+  },
+
+  docScrollTop: function() {
+     if ( window.pageYOffset )
+        return window.pageYOffset;
+     else if ( document.documentElement && document.documentElement.scrollTop )
+        return document.documentElement.scrollTop;
+     else if ( document.body )
+        return document.body.scrollTop;
+     else
+        return 0;
+  },
+
+  nan2zero: function(n) {
+    if (typeof(n)=='string') n=parseInt(n);
+    return isNaN(n) || typeof(n)=='undefined' ? 0 : n;
+  },
+
+  eventKey: function(e) {
+    if( typeof( e.keyCode ) == 'number'  ) {
+      return e.keyCode; //DOM
+    } else if( typeof( e.which ) == 'number' ) {
+      return e.which;   //NS 4 compatible
+    } else if( typeof( e.charCode ) == 'number'  ) {
+      return e.charCode; //also NS 6+, Mozilla 0.9+
+    }
+    return -1;  //total failure, we have no way of obtaining the key code
+  },
+
+/**
+ * Return the previous sibling that has the specified tagName
+ */
+   getPreviosSiblingByTagName: function(el,tagName) {
+       var sib=el.previousSibling;
+       while (sib) {
+               if ((sib.tagName==tagName) && (sib.style.display!='none')) return sib;
+               sib=sib.previousSibling;
+       }
+       return null;
+   },
+
+/**
+ * Return the parent HTML element that has the specified tagName.
+ * @param className optional
+ */
+   getParentByTagName: function(el,tagName,className) {
+       var par=el;
+       tagName=tagName.toLowerCase();
+       while (par) {
+               if (par.tagName && par.tagName.toLowerCase()==tagName)
+        if (!className || par.className.indexOf(className)>=0) return par;
+               par=par.parentNode;
+       }
+       return null;
+   },
+
+/**
+ * Wrap the children of a DOM element in a new element
+ * @param el the element whose children are to be wrapped
+ * @param cls class name of the wrapper (optional)
+ * @param id id of the wrapper (optional)
+ * @param wrapperTag type of wrapper element to be created (optional, defaults to DIV)
+ */
+  wrapChildren: function(el,cls,id,wrapperTag) {
+    var tag=wrapperTag || 'div';
+    var wrapper = document.createElement(tag);
+    if (id) wrapper.id=id;
+    if (cls) wrapper.className=cls;
+    while (el.firstChild)
+      wrapper.appendChild(el.firstChild);
+    el.appendChild(wrapper);
+    return wrapper;
+  },
+
+/**
+ * Format a positive number
+ * @param decPlaces the number of digits to display after the decimal point
+ * @param thouSep the character to use as the thousands separator
+ * @param decPoint the character to use as the decimal point
+ */
+  formatPosNumber: function(posnum,decPlaces,thouSep,decPoint) {
+    var a=posnum.toFixed(decPlaces).split(/\./);
+    if (thouSep) {
+      var rgx = /(\d+)(\d{3})/;
+      while (rgx.test(a[0]))
+        a[0]=a[0].replace(rgx, '$1'+thouSep+'$2');
+    }
+    return a.join(decPoint);
+  },
+
+/**
+ * Post condition - if childNodes[n] is refChild, than childNodes[n+1] is newChild.
+ */
+  DOMNode_insertAfter: function(newChild,refChild) {
+    var parentx=refChild.parentNode;
+    if(parentx.lastChild==refChild) { return parentx.appendChild(newChild);}
+    else {return parentx.insertBefore(newChild,refChild.nextSibling);}
+  },
+
+  positionCtlOverIcon: function(ctl,icon) {
+    if (ctl.style.display=='none') ctl.style.display='block';
+    var offsets=Position.page(icon);
+    var correction=Prototype.Browser.IE ? 1 : 2;  // based on a 1px border
+    var lpad=this.nan2zero(Element.getStyle(icon,'padding-left'))
+    ctl.style.left = (offsets[0]+lpad+correction)+'px';
+    var scrTop=this.docScrollTop();
+    var newTop=offsets[1] + correction + scrTop;
+    var ctlht=ctl.offsetHeight;
+    var iconht=icon.offsetHeight;
+    if (newTop+iconht+ctlht < this.windowHeight()+scrTop)
+      newTop+=iconht;  // display below icon
+    else
+      newTop=Math.max(newTop-ctlht,scrTop);  // display above icon
+    ctl.style.top = newTop+'px';
+  },
+
+  createFormField: function(parent,elemTag,elemType,id,name) {
+    if (typeof name!='string') name=id;
+    if (Prototype.Browser.IE) {
+      // IE cannot set NAME attribute on dynamically created elements
+      var s=elemTag+' id="'+id+'"';
+      if (elemType) s+=' type="'+elemType+'"';
+      if (elemTag.match(/^(form|input|select|textarea|object|button|img)$/)) s+=' name="'+name+'"';
+      var field=document.createElement('<'+s+' />');
+    } else {
+      var field=document.createElement(elemTag);
+      if (elemType) field.type=elemType;
+      field.id=id;
+      if (typeof field.name=='string') field.name=name;
+    }
+    parent.appendChild(field);
+    return field;
+  },
+
+/**
+ * Gets the value of the specified cookie
+ */
+  getCookie: function(itemName) {
+    var arg = itemName+'=';
+    var alen = arg.length;
+    var clen = document.cookie.length;
+    var i = 0;
+    while (i < clen) {
+      var j = i + alen;
+      if (document.cookie.substring(i, j) == arg) {
+        var endstr = document.cookie.indexOf (';', j);
+        if (endstr == -1) endstr=document.cookie.length;
+        return unescape(document.cookie.substring(j, endstr));
+      }
+      i = document.cookie.indexOf(' ', i) + 1;
+      if (i == 0) break;
+    }
+    return null;
+  },
+
+/**
+ * Write information to cookie.
+ * For cookies to be retained for the current session only, set daysToKeep=null.
+ * To erase a cookie, pass a negative daysToKeep value.
+ */
+  setCookie: function(itemName,itemValue,daysToKeep,cookiePath,cookieDomain) {
+       var c = itemName+"="+escape(itemValue);
+       if (typeof(daysToKeep)=='number') {
+               var date = new Date();
+               date.setTime(date.getTime()+(daysToKeep*24*60*60*1000));
+               c+="; expires="+date.toGMTString();
+       }
+       if (typeof(cookiePath)=='string') c+="; path="+cookiePath;
+       if (typeof(cookieDomain)=='string') c+="; domain="+cookieDomain;
+    document.cookie = c;
+  }
+
+};
+
+
+// Translation helper object
+/** @singleton */
+var RicoTranslate = {
+  phrases : {},
+  thouSep : ",",
+  decPoint: ".",
+  langCode: "en",
+  re      : /^(\W*)\b(.*)\b(\W*)$/,
+  dateFmt : "mm/dd/yyyy",
+  timeFmt : "hh:nn:ss a/pm",
+  monthNames: ['January','February','March','April','May','June',
+               'July','August','September','October','November','December'],
+  dayNames: ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],
+
+  addPhrase: function(fromPhrase, toPhrase) {
+    this.phrases[fromPhrase]=toPhrase;
+  },
+
+/**
+ * fromPhrase may contain multiple words/phrases separated by tabs
+ * and each portion will be looked up separately.
+ * Punctuation & spaces at the beginning or
+ * ending of a phrase are ignored.
+ */
+  getPhrase: function(fromPhrase) {
+    var words=fromPhrase.split(/\t/);
+    var transWord,translated = '';
+    for (var i=0; i<words.length; i++) {
+      if (this.re.exec(words[i])) {
+        transWord=this.phrases[RegExp.$2];
+        translated += (typeof transWord=='string') ? RegExp.$1+transWord+RegExp.$3 : words[i];
+      } else {
+        translated += words[i];
+      }
+    }
+    return translated;
+  }
+}
+
+
+if (!Date.prototype.formatDate) {
+  Date.prototype.formatDate = function(fmt) {
+    var d=this;
+    var datefmt=(typeof fmt=='string') ? datefmt=fmt : 'translateDate';
+    switch (datefmt) {
+      case 'locale':
+      case 'localeDateTime':
+        return d.toLocaleString();
+      case 'localeDate':
+        return d.toLocaleDateString();
+      case 'translate':
+      case 'translateDateTime':
+        datefmt=RicoTranslate.dateFmt+' '+RicoTranslate.timeFmt;
+        break;
+      case 'translateDate':
+        datefmt=RicoTranslate.dateFmt;
+        break;
+    }
+    return datefmt.replace(/(yyyy|mmmm|mmm|mm|dddd|ddd|dd|hh|nn|ss|a\/p)/gi,
+      function($1) {
+        switch ($1.toLowerCase()) {
+        case 'yyyy': return d.getFullYear();
+        case 'mmmm': return RicoTranslate.monthNames[d.getMonth()];
+        case 'mmm':  return RicoTranslate.monthNames[d.getMonth()].substr(0, 3);
+        case 'mm':   return (d.getMonth() + 1).toPaddedString(2);
+        case 'm':    return (d.getMonth() + 1);
+        case 'dddd': return RicoTranslate.dayNames[d.getDay()];
+        case 'ddd':  return RicoTranslate.dayNames[d.getDay()].substr(0, 3);
+        case 'dd':   return d.getDate().toPaddedString(2);
+        case 'd':    return d.getDate();
+        case 'hh':   return ((h = d.getHours() % 12) ? h : 12).toPaddedString(2);
+        case 'h':    return ((h = d.getHours() % 12) ? h : 12);
+        case 'HH':   return d.getHours().toPaddedString(2);
+        case 'H':    return d.getHours();
+        case 'nn':   return d.getMinutes().toPaddedString(2);
+        case 'ss':   return d.getSeconds().toPaddedString(2);
+        case 'a/p':  return d.getHours() < 12 ? 'a' : 'p';
+        }
+      }
+    );
+  }
+}
+
+if (!Date.prototype.setISO8601) {
+/**
+ * Converts a string in ISO 8601 format to a date object.
+ * Returns true if string is a valid date or date-time.
+ * Based on info at http://delete.me.uk/2005/03/iso8601.html
+ */
+  Date.prototype.setISO8601 = function (string) {
+    if (!string) return false;
+    var d = string.match(/(\d\d\d\d)(?:-?(\d\d)(?:-?(\d\d)(?:[T ](\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|(?:([-+])(\d\d)(?::?(\d\d))?)?)?)?)?)?/);
+    if (!d) return false;
+    var offset = 0;
+    var date = new Date(d[1], 0, 1);
+
+    if (d[2]) { date.setMonth(d[2] - 1); }
+    if (d[3]) { date.setDate(d[3]); }
+    if (d[4]) { date.setHours(d[4]); }
+    if (d[5]) { date.setMinutes(d[5]); }
+    if (d[6]) { date.setSeconds(d[6]); }
+    if (d[7]) { date.setMilliseconds(Number("0." + d[7]) * 1000); }
+    if (d[8]) {
+        if (d[10] && d[11]) offset = (Number(d[10]) * 60) + Number(d[11]);
+        offset *= ((d[9] == '-') ? 1 : -1);
+        offset -= date.getTimezoneOffset();
+    }
+    var time = (Number(date) + (offset * 60 * 1000));
+    this.setTime(Number(time));
+    return true;
+  }
+}
+
+if (!Date.prototype.toISO8601String) {
+/**
+ * Convert date to an ISO 8601 formatted string.
+ * <p>format is an integer in the range 1-6:<dl>
+ * <dt>1 (year)</dt>
+ *   <dd>YYYY (eg 1997)</dd>
+ * <dt>2 (year and month)</dt>
+ *   <dd>YYYY-MM (eg 1997-07)</dd>
+ * <dt>3 (complete date)</dt>
+ *   <dd>YYYY-MM-DD (eg 1997-07-16)</dd>
+ * <dt>4 (complete date plus hours and minutes)</dt>
+ *   <dd>YYYY-MM-DDThh:mmTZD (eg 1997-07-16T19:20+01:00)</dd>
+ * <dt>5 (complete date plus hours, minutes and seconds)</dt>
+ *   <dd>YYYY-MM-DDThh:mm:ssTZD (eg 1997-07-16T19:20:30+01:00)</dd>
+ * <dt>6 (complete date plus hours, minutes, seconds and a decimal
+ *   fraction of a second)</dt>
+ *   <dd>YYYY-MM-DDThh:mm:ss.sTZD (eg 1997-07-16T19:20:30.45+01:00)</dd>
+ *</dl>
+ * Based on: http://www.codeproject.com/jscript/dateformat.asp
+ */
+  Date.prototype.toISO8601String = function (format, offset) {
+    if (!format) { var format = 6; }
+    if (!offset) {
+        var offset = 'Z';
+        var date = this;
+    } else {
+        var d = offset.match(/([-+])([0-9]{2}):([0-9]{2})/);
+        var offsetnum = (Number(d[2]) * 60) + Number(d[3]);
+        offsetnum *= ((d[1] == '-') ? -1 : 1);
+        var date = new Date(Number(Number(this) + (offsetnum * 60000)));
+    }
+
+    var zeropad = function (num) { return ((num < 10) ? '0' : '') + num; }
+
+    var str = "";
+    str += date.getUTCFullYear();
+    if (format > 1) { str += "-" + zeropad(date.getUTCMonth() + 1); }
+    if (format > 2) { str += "-" + zeropad(date.getUTCDate()); }
+    if (format > 3) {
+        str += "T" + zeropad(date.getUTCHours()) +
+               ":" + zeropad(date.getUTCMinutes());
+    }
+    if (format > 5) {
+        var secs = Number(date.getUTCSeconds() + "." +
+                   ((date.getUTCMilliseconds() < 100) ? '0' : '') +
+                   zeropad(date.getUTCMilliseconds()));
+        str += ":" + zeropad(secs);
+    } else if (format > 4) { str += ":" + zeropad(date.getUTCSeconds()); }
+
+    if (format > 3) { str += offset; }
+    return str;
+  }
+}
+
+if (!String.prototype.formatDate) {
+  String.prototype.formatDate = function(fmt) {
+    var s=this.replace(/-/g,'/');
+    var d = new Date(s);
+    return isNaN(d) ? this : d.formatDate(fmt);
+  }
+}
+
+if (!Number.prototype.formatNumber) {
+/**
+ * Format a number according to the specs in assoc array 'fmt'.
+ * Result is a string, wrapped in a span element with a class of: negNumber, zeroNumber, posNumber
+ * These classes can be set in CSS to display negative numbers in red, for example.
+ *
+ * <p>fmt may contain:<dl>
+ *   <dt>multiplier </dt><dd> the original number is multiplied by this amount before formatting</dd>
+ *   <dt>decPlaces  </dt><dd> number of digits to the right of the decimal point</dd>
+ *   <dt>decPoint   </dt><dd> character to be used as the decimal point</dd>
+ *   <dt>thouSep    </dt><dd> character to use as the thousands separator</dd>
+ *   <dt>prefix     </dt><dd> string added to the beginning of the result (e.g. a currency symbol)</dd>
+ *   <dt>suffix     </dt><dd> string added to the end of the result (e.g. % symbol)</dd>
+ *   <dt>negSign    </dt><dd> specifies format for negative numbers: L=leading minus, T=trailing minus, P=parens</dd>
+ *</dl>
+ */
+  Number.prototype.formatNumber = function(fmt) {
+    if (isNaN(this)) return 'NaN';
+    var n=this;
+    if (typeof fmt.multiplier=='number') n*=fmt.multiplier;
+    var decPlaces=typeof fmt.decPlaces=='number' ? fmt.decPlaces : 0;
+    var thouSep=typeof fmt.thouSep=='string' ? fmt.thouSep : RicoTranslate.thouSep;
+    var decPoint=typeof fmt.decPoint=='string' ? fmt.decPoint : RicoTranslate.decPoint;
+    var prefix=fmt.prefix || "";
+    var suffix=fmt.suffix || "";
+    var negSign=typeof fmt.negSign=='string' ? fmt.negSign : "L";
+    negSign=negSign.toUpperCase();
+    var s,cls;
+    if (n<0.0) {
+      s=RicoUtil.formatPosNumber(-n,decPlaces,thouSep,decPoint);
+      if (negSign=="P") s="("+s+")";
+      s=prefix+s;
+      if (negSign=="L") s="-"+s;
+      if (negSign=="T") s+="-";
+      cls='negNumber';
+    } else {
+      cls=n==0.0 ? 'zeroNumber' : 'posNumber';
+      s=prefix+RicoUtil.formatPosNumber(n,decPlaces,thouSep,decPoint);
+    }
+    return "<span class='"+cls+"'>"+s+suffix+"</span>";
+  }
+}
+
+if (!String.prototype.formatNumber) {
+/**
+ * Take a string that can be converted via parseFloat
+ * and format it according to the specs in assoc array 'fmt'.
+ */
+  String.prototype.formatNumber = function(fmt) {
+    var n=parseFloat(this);
+    return isNaN(n) ? this : n.formatNumber(fmt);
+  }
+}
+
+/**
+ * Fix select control bleed-thru on floating divs in IE.
+ * Based on technique published by Joe King at:
+ * http://dotnetjunkies.com/WebLog/jking/archive/2003/10/30/2975.aspx
+ */
+Rico.Shim = Class.create();
+
+if (Prototype.Browser.IE) {
+  Rico.Shim.prototype = {
+
+    initialize: function(DivRef) {
+      this.ifr = document.createElement('iframe');
+      this.ifr.style.position="absolute";
+      this.ifr.style.display = "none";
+      this.ifr.src="javascript:false;";
+      DivRef.parentNode.appendChild(this.ifr);
+      this.DivRef=DivRef;
+    },
+
+    hide: function() {
+      this.ifr.style.display = "none";
+    },
+
+    show: function() {
+      this.ifr.style.width   = this.DivRef.offsetWidth;
+      this.ifr.style.height  = this.DivRef.offsetHeight;
+      this.ifr.style.top     = this.DivRef.style.top;
+      this.ifr.style.left    = this.DivRef.style.left;
+      this.ifr.style.zIndex  = this.DivRef.currentStyle.zIndex - 1;
+      this.ifr.style.display = "block";
+    }
+  }
+} else {
+  Rico.Shim.prototype = {
+    initialize: function() {},
+    hide: function() {},
+    show: function() {}
+  }
+}
+
+
+/**
+ * Rico.Shadow is intended for positioned elements.
+ * Uses blur filter in IE, and alpha-transparent png images for all other browsers.
+ * Based on: http://www.positioniseverything.net/articles/dropshadows.html
+ */
+Rico.Shadow = Class.create();
+
+Rico.Shadow.prototype = {
+
+  initialize: function(DivRef) {
+    this.div = document.createElement('div');
+    this.div.style.position="absolute";
+    if (typeof this.div.style.filter=='undefined') {
+      new Image().src = Rico.imgDir+"shadow.png";
+      new Image().src = Rico.imgDir+"shadow_ur.png";
+      new Image().src = Rico.imgDir+"shadow_ll.png";
+      this.createShadow();
+      this.offset=5;
+    } else {
+      this.div.style.backgroundColor='#888';
+      this.div.style.filter='progid:DXImageTransform.Microsoft.Blur(makeShadow=1, shadowOpacity=0.3, pixelRadius=3)';
+      this.offset=0; // MS blur filter already does offset
+    }
+    this.div.style.display = "none";
+    DivRef.parentNode.appendChild(this.div);
+    this.DivRef=DivRef;
+  },
+
+  createShadow: function() {
+    var tab = document.createElement('table');
+    tab.style.height='100%';
+    tab.style.width='100%';
+    tab.cellSpacing=0;
+    tab.dir='ltr';
+
+    var tr1=tab.insertRow(-1);
+    tr1.style.height='8px';
+    var td11=tr1.insertCell(-1);
+    td11.style.width='8px';
+    var td12=tr1.insertCell(-1);
+    td12.style.background="transparent url("+Rico.imgDir+"shadow_ur.png"+") no-repeat right bottom"
+
+    var tr2=tab.insertRow(-1);
+    var td21=tr2.insertCell(-1);
+    td21.style.background="transparent url("+Rico.imgDir+"shadow_ll.png"+") no-repeat right bottom"
+    var td22=tr2.insertCell(-1);
+    td22.style.background="transparent url("+Rico.imgDir+"shadow.png"+") no-repeat right bottom"
+
+    this.div.appendChild(tab);
+  },
+
+  hide: function() {
+    this.div.style.display = "none";
+  },
+
+  show: function() {
+    this.div.style.width = this.DivRef.offsetWidth + 'px';
+    this.div.style.height= this.DivRef.offsetHeight + 'px';
+    this.div.style.top   = (parseInt(this.DivRef.style.top)+this.offset)+'px';
+    this.div.style.left  = (parseInt(this.DivRef.style.left)+this.offset)+'px';
+    this.div.style.zIndex= parseInt(Element.getStyle(this.DivRef,'z-index')) - 1;
+    this.div.style.display = "block";
+  }
+}
+
+
+Rico.Popup = Class.create();
+
+Rico.Popup.prototype = {
+
+  initialize: function(options,DivRef) {
+    this.options = {
+      hideOnEscape  : true,
+      hideOnClick   : true,
+      ignoreClicks  : false,
+      position      : 'absolute',
+      shadow        : true
+    }
+    Object.extend(this.options, options || {});
+    if (DivRef) this.setDiv(DivRef);
+  },
+
+  setDiv: function(DivRef,closeFunc) {
+    this.divPopup=$(DivRef);
+    var position=this.options.position == 'auto' ? Element.getStyle(this.divPopup,'position').toLowerCase() : this.options.position;
+    if (!this.divPopup || position != 'absolute') return;
+    this.closeFunc=closeFunc || this.closePopup.bindAsEventListener(this);
+    this.shim=new Rico.Shim(this.divPopup);
+    if (this.options.shadow)
+      this.shadow=new Rico.Shadow(this.divPopup);
+    if (this.options.hideOnClick)
+      Event.observe(document,"click", this.closeFunc);
+    if (this.options.hideOnEscape)
+      Event.observe(document,"keyup", this._checkKey.bindAsEventListener(this));
+    if (this.options.ignoreClicks) this.ignoreClicks();
+  },
+  
+  ignoreClicks: function() {
+    Event.observe(this.divPopup,"click", this._ignoreClick.bindAsEventListener(this));
+  },
+
+  _ignoreClick: function(e) {
+    if (e.stopPropagation)
+      e.stopPropagation();
+    else
+      e.cancelBubble = true;
+    return true;
+  },
+  
+  // event handler to process keyup events (hide menu on escape key)
+  _checkKey: function(e) {
+    if (RicoUtil.eventKey(e)==27) this.closeFunc();
+    return true;
+  },
+
+  openPopup: function(left,top) {
+    if (typeof left=='number') this.divPopup.style.left=left+'px';
+    if (typeof top=='number') this.divPopup.style.top=top+'px';
+    this.divPopup.style.display="block";
+    if (this.shim) this.shim.show();
+    if (this.shadow) this.shadow.show();
+  },
+
+  closePopup: function() {
+    if (this.shim) this.shim.hide();
+    if (this.shadow) this.shadow.hide();
+    this.divPopup.style.display="none"
+  }
+
+}
+
+Rico.includeLoaded('ricoCommon.js');
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoComponents.js b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoComponents.js
new file mode 100644 (file)
index 0000000..1db5830
--- /dev/null
@@ -0,0 +1,194 @@
+/**
+  *  (c) 2005-2007 Richard Cowin (http://openrico.org)
+  *  (c) 2005-2007 Matt Brown (http://dowdybrown.com)
+  *
+  *  Rico is 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
+  **/
+  
+
+Rico.ContentTransitionBase = function() {}
+Rico.ContentTransitionBase.prototype = {
+       initialize: function(titles, contents, options) { 
+    if (typeof titles == 'string')
+      titles = $$(titles)
+    if (typeof contents == 'string')
+      contents = $$(contents)
+         
+         this.titles = titles;
+         this.contents = contents;
+               this.options = Object.extend({
+                       duration:200, 
+                       steps:8,
+                       rate:Rico.Effect.easeIn
+         }, options || {});
+         this.hoverSet = new Rico.HoverSet(titles, options);
+               contents.each(function(p){ if (p) Element.hide(p)})
+         this.selectionSet = new Rico.SelectionSet(titles, Object.extend(this.options, {onSelect: this.select.bind(this)}));
+               if (this.initContent) this.initContent();
+       },
+       reset: function(){
+         this.selectionSet.reset();
+       },
+       select: function(title) {
+         if ( this.selected == this.contentOf(title)) return
+               var panel = this.contentOf(title); 
+               if (this.transition){
+                       if (this.selected){
+                         var effect = this.transition(panel)
+                         if (effect) Rico.animate(effect, this.options)
+      }
+                       else
+                               Element.show(panel);
+               }else{
+                       if (this.selected)
+                               Element.hide(this.selected)
+                       Element.show(panel);
+               }
+               this.selected = panel;
+       },
+       add: function(title, content){
+               this.titles.push(title);
+               this.contents.push(content);
+               this.hoverSet.add(title);
+               this.selectionSet.add(title);   
+               this.selectionSet.select(title);
+       },
+       remove: function(title){},
+       removeAll: function(){
+               this.hoverSet.removeAll();
+               this.selectionSet.removeAll();
+       },
+       openByIndex: function(index){this.selectionSet.selectIndex(index)},
+       contentOf: function(title){ return this.contents[this.titles.indexOf(title)]}
+}
+
+Rico.ContentTransition = Class.create();
+Rico.ContentTransition.prototype = Object.extend(new Rico.ContentTransitionBase(),{});
+
+Rico.SlidingPanel = Class.create();
+Rico.SlidingPanel.prototype = {
+       initialize: function(panel) {
+               this.panel = panel;
+               this.options = arguments[1] || {};
+               this.closed = true;
+               this.showing = false
+               this.openEffect = this.options.openEffect;
+               this.closeEffect = this.options.closeEffect;
+               this.animator = new Rico.Effect.Animator();
+               Element.makeClipping(this.panel)
+       },
+       toggle: function () {
+               if(!this.showing){
+                       this.open();
+               } else { 
+                       this.close();
+    }
+       },
+       open: function () {
+         if (this.closed){
+           this.showing = true;
+                 Element.show(this.panel);
+               this.options.disabler.disableNative();
+    }
+               /*this.animator.stop();*/
+               this.animator.play(this.openEffect,
+                                                                                       { onFinish:function(){ Element.undoClipping($(this.panel))}.bind(this),
+                                                                                               rate:Rico.Effect.easeIn});
+       },
+       close: function () {
+               Element.makeClipping(this.panel)
+               this.animator.stop();
+               this.showing = false;
+               this.animator.play(this.closeEffect,
+                            { onFinish:function(){  Element.hide(this.panel);  
+                                                                                                                                                                                       this.options.disabler.enableNative()}.bind(this),       
+                                                                                               rate:Rico.Effect.easeOut});
+       }
+}
+
+
+//-------------------------------------------
+// Example components
+//-------------------------------------------
+
+Rico.Accordion = Class.create();
+Rico.Accordion.prototype = Object.extend(new Rico.ContentTransitionBase(), {
+  initContent: function() { 
+               this.selected.style.height = this.options.panelHeight + "px";
+       },
+  transition: function(p){ 
+    if (!this.options.noAnimate)
+                 return new Rico.AccordionEffect(this.selected, p, this.options.panelHeight);
+    else{
+      p.style.height = this.options.panelHeight + "px";
+      if (this.selected) Element.hide(this.selected);
+               Element.show(p);
+    }
+       }
+})
+
+
+Rico.TabbedPanel = Class.create();
+Rico.TabbedPanel.prototype = Object.extend(new Rico.ContentTransitionBase(), {
+  initContent: function() { 
+               if (false && (this.options.panelHeight=='auto' || this.options.panelWidth=='auto')) {
+                 // 'auto' is not working yet
+                 var maxwi=0, maxht=0;
+                 for (var i=0; i<this.contents.length; i++) {
+                   var d=Element.getDimensions(this.contents[i]);
+                   maxwi=Math.max(maxwi,d.width);
+                   maxht=Math.max(maxht,d.height);
+                 }
+                 //alert('maxwi='+maxwi+' maxht='+maxht);
+                 if (this.options.panelWidth=='auto') this.options.panelWidth=maxwi;
+                 if (this.options.panelHeight=='auto') this.options.panelHeight=maxht;
+               }
+         if (typeof this.options.panelWidth=='number') this.options.panelWidth+="px";
+         if (typeof this.options.panelHeight=='number') this.options.panelHeight+="px";
+    if (Rico.Corner) {
+      this.options.color='transparent';
+      this.options.corners='top';
+      for (var i=0; i<this.titles.length; i++)
+        if (this.titles[i]) {
+          if (this.options.panelHdrWidth) this.titles[i].style.width=this.options.panelHdrWidth;
+          Rico.Corner.round(this.titles[i], this.options);
+        }
+    }
+               this.transition(this.selected);
+       },
+  transition: function(p){ 
+    if (this.selected) Element.hide(this.selected);
+               Element.show(p);
+    if (this.options.panelHeight) p.style.height = this.options.panelHeight;
+    if (this.options.panelWidth) p.style.width = this.options.panelWidth;
+       }
+})
+
+
+Rico.SlidingPanel.top = function(panel, innerPanel){
+       var options = Object.extend({
+               disabler: Rico.Controls.defaultDisabler
+  }, arguments[2] || {});
+       var height = options.height || Element.getDimensions(innerPanel)[1] || innerPanel.offsetHeight;
+       var top = options.top || Position.positionedOffset(panel)[1];
+       options.openEffect = new Rico.Effect.SizeFromTop(panel, innerPanel, top, height, {baseHeight:height});
+       options.closeEffect = new Rico.Effect.SizeFromTop(panel, innerPanel, top, 1, {baseHeight:height});
+  panel.style.height = "0px";
+       innerPanel.style.top = -height + "px";  
+       return new Rico.SlidingPanel(panel, options);
+}
+
+Rico.SlidingPanel.bottom = function(panel){
+       var options = Object.extend({
+               disabler: Rico.Controls.blankDisabler
+  }, arguments[1] || {});
+       var height = options.height || Element.getDimensions(panel).height;
+       var top = Position.positionedOffset(panel)[1];
+       options.openEffect = new Rico.Effect.SizeFromBottom(panel, top - height, height);
+       options.closeEffect = new Rico.Effect.SizeFromBottom(panel, top, 1);
+       return new Rico.SlidingPanel(panel, options); 
+}
+
+Rico.includeLoaded('ricoComponents.js');
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoDashboard.js b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoDashboard.js
new file mode 100644 (file)
index 0000000..17cc5a7
--- /dev/null
@@ -0,0 +1,329 @@
+
+Rico.Dashboard = Class.create();
+Rico.Dashboard.prototype = {
+       initialize: function(dashboardId, columnCount, options) {
+               this.dashboardDiv = $(dashboardId);
+               this.numCol = columnCount;
+               this.options = options || [];
+               this.cols = new Array(); 
+               this.insertionOutline = document.createElement("div");
+               this.insertionOutline.id = "insertionOutline";
+     
+               //get panels before adding collumns
+               var dashboard = this
+   this.panelList = [];
+   // this.panelList = parsePanels(this.dashboardDiv, function(title, content, panel)
+  //                                 { return new Rico.DashboardPanel(title, content, panel, dashboard);})
+               var colSizes = this.options.columnSizes 
+    if (!colSizes){
+      colSizes = [];
+      for(var i=0; i<this.numCol; i++)
+        colSizes[i] = 100 / columnCount;
+    }
+
+               for(var i=0; i< this.numCol;i++)        {
+          var newColDiv = document.createElement("div");
+          newColDiv.style.width = colSizes[i] + "%";
+          newColDiv.style.minHeight = "1px";
+          newColDiv.className = "column";
+          newColDiv.id = "" + (i+1) ;
+          this.cols.push(newColDiv);
+          this.dashboardDiv.appendChild(newColDiv);
+          //if (i < this.numCol-1){
+         //    var borderDiv = document.createElement("div");
+         //    borderDiv.style.width = "3px";
+         //    borderDiv.style.height = "100%";
+         //    borderDiv.style.background = "111111"
+         //    borderDiv.className = "border";
+         //    //borderDiv.style.visibility = "visible";
+         //    this.dashboardDiv.appendChild(borderDiv);
+         // }
+               }
+      //now add the panels to the columns
+    for (var i=0; i< this.panelList.length; i++) {
+         var panel = this.panelList[i];                
+         this._addToCol(panel, panel.panelDiv.getAttribute('column'));
+               }
+       },
+       
+       addPanel: function(panel, col){
+         this.panelList.push(panel)
+         this._addToCol(panel, col)
+       },
+       
+       _addToCol: function(panel, col) {
+               panel.addToCol(this.cols[col-1]);         
+       },
+       
+       closeAllPanels: function() {
+         var panels = this.panelList;
+         for (var i=0; i<panels.length; i++)
+           panels[i].close();
+         this.panelList = [];
+       },
+       
+       openAllPanels: function(open) {
+               for (var i=0; i<this.panelList.length; i++) 
+                       this.panelList[i].setVisibility(open);
+       },
+       
+       columnAt: function(x) {
+               for (var i=this.cols.length-1; i >=0; i--)      {
+                       if (x >= Position.positionedOffset(this.cols[i])[0])
+                               return this.cols[i];
+               }
+               return this.cols[0];
+       },
+       
+       destroy: function() {
+               try{
+                       for (var i=0; i<this.panelList.length; i++) {
+                               delete this.panelList[i];
+                               this.panelList[i] = null;
+                       }
+                       delete this;
+               }catch(e){}
+       },
+       
+       dropPanel: function(panel){
+         panel.column.removeChild(panel.panelDiv);
+         panel.column = this.insertionColumn;
+         this.insertionColumn.replaceChild(panel.panelDiv, this.insertionOutline);
+  },
+
+  dragPanel: function(panel, left, top){
+    var newCol = this.columnAt(left + panel.panelDiv.offsetWidth/2);
+
+    if (!newCol) return;  
+    
+               this._moveInsertion(newCol);
+               var panels = this.columnPanels(newCol);
+               var insertPos = this._getInsertionPos(panels);
+
+               if (insertPos != 0 && 
+                   top <= Position.positionedOffset(panels[insertPos-1])[1]) {
+                       this.insertionColumn.removeChild(this.insertionOutline);
+                       newCol.insertBefore(this.insertionOutline, panels[insertPos-1]);
+               }
+               if (insertPos != (panels.length-1) && 
+                   top >= Position.positionedOffset(panels[insertPos+1])[1]) {
+                       if (panels[insertPos + 2]) 
+                                 newCol.insertBefore(this.insertionOutline, panels[insertPos+2]);
+                        else
+                                 newCol.appendChild(this.insertionOutline);
+               }    
+               this.insertionColumn = newCol;
+  },
+
+  _moveInsertion: function(column){
+               if (this.insertionColumn != column) {
+                       this.insertionColumn.removeChild(this.insertionOutline)
+                       this.insertionColumn = column;
+                       column.appendChild(this.insertionOutline);
+       }
+  },
+  
+       columnPanels: function(column){
+                       var panels = [];
+                       for (var i=0; i<column.childNodes.length; i++) {
+                               if (!column.childNodes[i].isDragging)  {
+                                       panels.push(column.childNodes[i]);
+                               }
+                       }
+                       return panels;
+       },
+  
+       _getInsertionPos : function(panels) {
+               for (var i=0; i<panels.length; i++) {
+                       if (panels[i] == this.insertionOutline) 
+                         return i;
+               }
+       },
+       
+       startInsertionOutline: function(panelDiv){
+         this.insertionOutline.style.height = panelDiv.offsetHeight + "px";
+         panelDiv.parentNode.insertBefore(this.insertionOutline, panelDiv);
+         this.insertionColumn = panelDiv.parentNode;
+  }
+}
+
+Rico.PanelCreation = {
+  create: function(title, url, dashboard) {    
+               var panelDiv = document.createElement("div");
+    var titleDiv = PanelCreation.createHeader(title)
+    var contentDiv = PanelCreation.createContent()
+               panelDiv.className = "panel";
+               panelDiv.appendChild(titleDiv);
+               panelDiv.appendChild(contentDiv);       
+       return new Rico.DashboardPanel(titleDiv, contentDiv, panelDiv, dashboard)
+       },
+       createHeader: function(title) {
+               this.panelHeaderDiv = document.createElement("div");
+               this.panelHeaderDiv.className = "panelHeader";
+               this.panelHeaderDiv.innerHTML = document.createTextNode(this.title);
+               initializeHeader(this.panelHeaderDiv);
+               return this.panelHeaderDiv;
+       },
+       createContent: function() {
+               this.panelContentDiv = document.createElement("div");
+               this.panelContentDiv.className = "panelContent";
+               this.panelContentDiv.innerHTML = "Loading";
+               return this.panelContentDiv;
+       }
+}
+
+Rico.DashboardPanel = Class.create();
+Rico.DashboardPanel.prototype = {
+       initialize: function(headerDiv, contentDiv, panelDiv, dashboard) {
+               this.dashboard = dashboard;
+               this.panelHeaderDiv = headerDiv;
+               this.panelContentDiv = contentDiv;
+               this.panelDiv = panelDiv;
+               this.open = true;
+               panelDiv.style.zIndex = 1000;
+               this.initializeHeader(headerDiv);
+       Event.observe(headerDiv, "mousedown", this._startDrag.bind(this));
+  },
+    
+       initializeHeader: function(headerDiv) {
+               headerDiv.onmouseover = this.hover.bind(this);
+               headerDiv.onmouseout = this.unHover.bind(this);
+               
+//             this.visibilityToggleDiv = document.createElement("div");
+//             this.visibilityToggleDiv.className = "visibilityToggle";
+//             this.visibilityToggleDiv.innerHTML = '<img src="/images/bkgd_panel_arrow.png"/>';
+//             this.visibilityToggleDiv.style.visibility = "hidden";
+//             this.visibilityToggleDiv.onmousedown = this.toggleVisibility.bind(this);
+       
+               this.titleDiv = document.createElement("div");
+               this.titleDiv.innerHTML = headerDiv.innerHTML;          
+               this.titleDiv.className = "title";
+               
+               headerDiv.innerHTML = '';
+               
+               this.closeDiv = document.createElement("div");
+               this.closeDiv.className = "close";
+               this.closeDiv.innerHTML = '<img src="/images/icn_close.png" alt="Remove" title="Remove this metric from the report" />';
+               this.closeDiv.style.display = "none";
+               this.closeDiv.onmousedown = this.close.bind(this);    
+               
+//             headerDiv.appendChild(this.visibilityToggleDiv);
+               headerDiv.appendChild(this.closeDiv);
+               headerDiv.appendChild(this.titleDiv);
+       },
+
+       addToCol: function(col, isNew) {
+         this.column  = col;
+               if (isNew && toCol.hasChildNodes())
+                       this.column.insertBefore(this.panelDiv, this.column.firstChild);
+               else
+                       this.column.appendChild(this.panelDiv);
+       },
+       
+       moveToColumn: function(col){
+               if (this.column != col) {
+                       this.column.removeChild(this.panelDiv)
+                       this.column = col;
+                       col.appendChild(this.panelDiv);
+               }
+       },    
+    //this.obj.root.onDragStart(parseInt(panel.panelDiv.style.left), parseInt(pnel.panelDiv.style.top), 
+               //                          event.clientX, event.clientY);
+       _startDrag: function(event) {
+               if (this.dashboard.options.startingDrag)
+                       this.dashboard.options.startingDrag();
+
+               Position.absolutize(this.panelDiv)
+               this.dashboard.startInsertionOutline(this.panelDiv)
+               this.panelDiv.style.opacity = .7;
+               this.panelDiv.style.zIndex = 900;
+               //this.panelDiv.style.width = (parseInt(this.panelDiv.offestWidth)-4)+"px";
+               new DragPanel(this, event);
+               Event.stop(event);
+       },
+       
+       hover: function() {
+//             this.visibilityToggleDiv.style.visibility = "visible";
+               this.closeDiv.show();
+       },
+       
+       unHover: function() {
+//             this.visibilityToggleDiv.style.visibility = "hidden";
+               this.closeDiv.hide();
+       },
+       
+       setVisibility: function(visibility) {
+               if (visibility) {
+                       this.panelDiv.show(); 
+               } else {
+                  this.panelDiv.hide();
+               }
+       },
+       
+       toggleVisibility: function() {
+          this.setVisibility(this.panelContentDiv.style.display =='none');
+       },
+       
+       close: function() {
+    if (this.open)
+                 this.panelDiv.parentNode.removeChild(this.panelDiv);
+                 this.open = false;
+       },
+       
+       show: function() {
+               this.panelContentDiv.show();
+               this.visibilityToggleDiv.firstChild.setAttribute("src", "/images/bkgd_panel_arrow.png");
+       },
+
+       hide: function() {
+               this.panelContentDiv.hide();
+               this.visibilityToggleDiv.firstChild.setAttribute("src", "/images/bkgd_panel_arrow.png");
+       },  
+       
+       drop: function() {
+         this.dashboard.dropPanel(this);
+         
+         this.panelDiv.style.position = "static";
+               //this.panelDiv.style.width = "100%";
+               this.unHover();
+               this.panelDiv.style.opacity = 1;
+               if (this.dashboard.options.endingDrag)
+                       this.dashboard.options.endingDrag();
+       }
+}
+
+DragPanel = Class.create();
+DragPanel.prototype = {
+       initialize : function(panel,event){     
+           this.panel = panel;     
+                       this.lastMouseX = event.clientX;
+                       this.lastMouseY = event.clientY;
+                       this.dragHandler = this.drag.bindAsEventListener(this)
+                       this.dropHandler = this.endDrag.bindAsEventListener(this)
+                       Event.observe(document, "mousemove", this.dragHandler);
+                       Event.observe(document, "mouseup", this.dropHandler);
+                       this.panel.panelDiv.isDragging = true
+       },      
+       drag : function(event){
+
+         panelDiv = this.panel.panelDiv
+               var newLeft = parseInt(panelDiv.style.left) + event.clientX - this.lastMouseX;
+               var newTop = parseInt(panelDiv.style.top) + event.clientY - this.lastMouseY;
+               panelDiv.style.left = newLeft + "px";
+               panelDiv.style.top = newTop + "px";                     
+               this.lastMouseX = event.clientX;
+               this.lastMouseY = event.clientY;
+    this.panel.dashboard.dragPanel(this.panel, newLeft, newTop);
+    Event.stop(event);
+       },
+       endDrag : function(event){                      
+               Event.stopObserving(document, "mousemove", this.dragHandler);
+       Event.stopObserving(document, "mouseup", this.dropHandler);     
+               this.panel.drop();      
+               this.panel.panelDiv.style.zIndex = 1000;
+               this.panel.panelDiv.isDragging = false;
+               Event.stop(event);
+       }
+}
+
+Rico.includeLoaded('ricoDashboard.js');
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoDragDrop.js b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoDragDrop.js
new file mode 100644 (file)
index 0000000..ce4f326
--- /dev/null
@@ -0,0 +1,770 @@
+/**
+  *
+  *  Copyright 2005 Sabre Airline Solutions
+  *
+  *  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.
+  **/
+//-------------------- ricoDragAndDrop.js
+Rico.DragAndDrop = Class.create();
+
+Rico.DragAndDrop.prototype = {
+
+   initialize: function() {
+      this.dropZones                = new Array();
+      this.draggables               = new Array();
+      this.currentDragObjects       = new Array();
+      this.dragElement              = null;
+      this.lastSelectedDraggable    = null;
+      this.currentDragObjectVisible = false;
+      this.interestedInMotionEvents = false;
+      this._mouseDown = this._mouseDownHandler.bindAsEventListener(this);
+      this._mouseMove = this._mouseMoveHandler.bindAsEventListener(this);
+      this._mouseUp = this._mouseUpHandler.bindAsEventListener(this);
+   },
+
+   registerDropZone: function(aDropZone) {
+      this.dropZones[ this.dropZones.length ] = aDropZone;
+   },
+
+   deregisterDropZone: function(aDropZone) {
+      var newDropZones = new Array();
+      var j = 0;
+      for ( var i = 0 ; i < this.dropZones.length ; i++ ) {
+         if ( this.dropZones[i] != aDropZone )
+            newDropZones[j++] = this.dropZones[i];
+      }
+
+      this.dropZones = newDropZones;
+   },
+
+   clearDropZones: function() {
+      this.dropZones = new Array();
+   },
+
+   registerDraggable: function( aDraggable ) {
+      this.draggables[ this.draggables.length ] = aDraggable;
+      this._addMouseDownHandler( aDraggable );
+   },
+
+   clearSelection: function() {
+      for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
+         this.currentDragObjects[i].deselect();
+      this.currentDragObjects = new Array();
+      this.lastSelectedDraggable = null;
+   },
+
+   hasSelection: function() {
+      return this.currentDragObjects.length > 0;
+   },
+
+   setStartDragFromElement: function( e, mouseDownElement ) {
+      this.origPos = RicoUtil.toDocumentPosition(mouseDownElement);
+      this.startx = e.screenX - this.origPos.x
+      this.starty = e.screenY - this.origPos.y
+      //this.startComponentX = e.layerX ? e.layerX : e.offsetX;
+      //this.startComponentY = e.layerY ? e.layerY : e.offsetY;
+      //this.adjustedForDraggableSize = false;
+
+      this.interestedInMotionEvents = this.hasSelection();
+      this._terminateEvent(e);
+   },
+
+   updateSelection: function( draggable, extendSelection ) {
+      if ( ! extendSelection )
+         this.clearSelection();
+
+      if ( draggable.isSelected() ) {
+         this.currentDragObjects.removeItem(draggable);
+         draggable.deselect();
+         if ( draggable == this.lastSelectedDraggable )
+            this.lastSelectedDraggable = null;
+      }
+      else {
+         this.currentDragObjects[ this.currentDragObjects.length ] = draggable;
+         draggable.select();
+         this.lastSelectedDraggable = draggable;
+      }
+   },
+
+   _mouseDownHandler: function(e) {
+      if ( arguments.length == 0 )
+         e = event;
+
+      // if not button 1 ignore it...
+      var nsEvent = e.which != undefined;
+      if ( (nsEvent && e.which != 1) || (!nsEvent && e.button != 1))
+         return;
+
+      var eventTarget      = e.target ? e.target : e.srcElement;
+      var draggableObject  = eventTarget.draggable;
+
+      var candidate = eventTarget;
+      while (draggableObject == null && candidate.parentNode) {
+         candidate = candidate.parentNode;
+         draggableObject = candidate.draggable;
+      }
+   
+      if ( draggableObject == null )
+         return;
+
+      this.updateSelection( draggableObject, e.ctrlKey );
+
+      // clear the drop zones postion cache...
+      if ( this.hasSelection() )
+         for ( var i = 0 ; i < this.dropZones.length ; i++ )
+            this.dropZones[i].clearPositionCache();
+
+      this.setStartDragFromElement( e, draggableObject.getMouseDownHTMLElement() );
+   },
+
+
+   _mouseMoveHandler: function(e) {
+      var nsEvent = e.which != undefined;
+      if ( !this.interestedInMotionEvents ) {
+         //this._terminateEvent(e);
+         return;
+      }
+
+      if ( ! this.hasSelection() )
+         return;
+
+      if ( ! this.currentDragObjectVisible )
+         this._startDrag(e);
+
+      if ( !this.activatedDropZones )
+         this._activateRegisteredDropZones();
+
+      //if ( !this.adjustedForDraggableSize )
+      //   this._adjustForDraggableSize(e);
+
+      this._updateDraggableLocation(e);
+      this._updateDropZonesHover(e);
+
+      this._terminateEvent(e);
+   },
+
+   _makeDraggableObjectVisible: function(e)
+   {
+      if ( !this.hasSelection() )
+         return;
+
+      var dragElement;
+      if ( this.currentDragObjects.length > 1 )
+         dragElement = this.currentDragObjects[0].getMultiObjectDragGUI(this.currentDragObjects);
+      else
+         dragElement = this.currentDragObjects[0].getSingleObjectDragGUI();
+
+      // go ahead and absolute position it...
+      if ( RicoUtil.getElementsComputedStyle(dragElement, "position")  != "absolute" )
+/*      if (Element.getStyle(dragElement,'position')=='absolute')*/
+         dragElement.style.position = "absolute";
+
+      // need to parent him into the document...
+      if ( dragElement.parentNode == null || dragElement.parentNode.nodeType == 11 )
+         document.body.appendChild(dragElement);
+
+      this.dragElement = dragElement;
+      this._updateDraggableLocation(e);
+
+      this.currentDragObjectVisible = true;
+   },
+
+   /**
+   _adjustForDraggableSize: function(e) {
+      var dragElementWidth  = this.dragElement.offsetWidth;
+      var dragElementHeight = this.dragElement.offsetHeight;
+      if ( this.startComponentX > dragElementWidth )
+         this.startx -= this.startComponentX - dragElementWidth + 2;
+      if ( e.offsetY ) {
+         if ( this.startComponentY > dragElementHeight )
+            this.starty -= this.startComponentY - dragElementHeight + 2;
+      }
+      this.adjustedForDraggableSize = true;
+   },
+   **/
+
+   _leftOffset: function(e) {
+          return e.offsetX ? document.body.scrollLeft : 0
+       },
+
+   _topOffset: function(e) {
+          return e.offsetY ? document.body.scrollTop:0
+       },
+
+               
+   _updateDraggableLocation: function(e) {
+      var dragObjectStyle = this.dragElement.style;
+      dragObjectStyle.left = (e.screenX + this._leftOffset(e) - this.startx) + "px"
+      dragObjectStyle.top  = (e.screenY + this._topOffset(e) - this.starty) + "px";
+   },
+
+   _updateDropZonesHover: function(e) {
+      var n = this.dropZones.length;
+      for ( var i = 0 ; i < n ; i++ ) {
+         if ( ! this._mousePointInDropZone( e, this.dropZones[i] ) )
+            this.dropZones[i].hideHover();
+      }
+
+      for ( var i = 0 ; i < n ; i++ ) {
+         if ( this._mousePointInDropZone( e, this.dropZones[i] ) ) {
+            if ( this.dropZones[i].canAccept(this.currentDragObjects) )
+               this.dropZones[i].showHover();
+         }
+      }
+   },
+
+   _startDrag: function(e) {
+      for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
+         this.currentDragObjects[i].startDrag();
+
+      this._makeDraggableObjectVisible(e);
+   },
+
+   _mouseUpHandler: function(e) {
+      if ( ! this.hasSelection() )
+         return;
+
+      var nsEvent = e.which != undefined;
+      if ( (nsEvent && e.which != 1) || (!nsEvent && e.button != 1))
+         return;
+
+      this.interestedInMotionEvents = false;
+
+      if ( this.dragElement == null ) {
+         this._terminateEvent(e);
+         return;
+      }
+
+      if ( this._placeDraggableInDropZone(e) )
+         this._completeDropOperation(e);
+      else {
+         this._terminateEvent(e);
+         Rico.animate(new Rico.Effect.Position( this.dragElement, this.origPos.x, this.origPos.y),
+                      {duration: 200,
+                       steps: 20,
+                       onFinish : this._doCancelDragProcessing.bind(this) } );
+      }
+
+     Event.stopObserving(document.body, "mousemove", this._mouseMove);
+     Event.stopObserving(document.body, "mouseup",  this._mouseUp);
+   },
+
+   _retTrue: function () {
+      return true;
+   },
+
+   _completeDropOperation: function(e) {
+      if ( this.dragElement != this.currentDragObjects[0].getMouseDownHTMLElement() ) {
+         if ( this.dragElement.parentNode != null )
+            this.dragElement.parentNode.removeChild(this.dragElement);
+      }
+
+      this._deactivateRegisteredDropZones();
+      this._endDrag();
+      this.clearSelection();
+      this.dragElement = null;
+      this.currentDragObjectVisible = false;
+      this._terminateEvent(e);
+   },
+
+   _doCancelDragProcessing: function() {
+      this._cancelDrag();
+
+        if ( this.dragElement != this.currentDragObjects[0].getMouseDownHTMLElement() && this.dragElement)
+           if ( this.dragElement.parentNode != null )
+              this.dragElement.parentNode.removeChild(this.dragElement);
+
+
+      this._deactivateRegisteredDropZones();
+      this.dragElement = null;
+      this.currentDragObjectVisible = false;
+   },
+
+   _placeDraggableInDropZone: function(e) {
+      var foundDropZone = false;
+      var n = this.dropZones.length;
+      for ( var i = 0 ; i < n ; i++ ) {
+         if ( this._mousePointInDropZone( e, this.dropZones[i] ) ) {
+            if ( this.dropZones[i].canAccept(this.currentDragObjects) ) {
+               this.dropZones[i].hideHover();
+               this.dropZones[i].accept(this.currentDragObjects);
+               foundDropZone = true;
+               break;
+            }
+         }
+      }
+
+      return foundDropZone;
+   },
+
+   _cancelDrag: function() {
+      for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
+         this.currentDragObjects[i].cancelDrag();
+   },
+
+   _endDrag: function() {
+      for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
+         this.currentDragObjects[i].endDrag();
+   },
+
+   _mousePointInDropZone: function( e, dropZone ) {
+
+      var absoluteRect = dropZone.getAbsoluteRect();
+
+      return e.clientX  > absoluteRect.left + this._leftOffset(e) &&
+             e.clientX  < absoluteRect.right + this._leftOffset(e) &&
+             e.clientY  > absoluteRect.top + this._topOffset(e)   &&
+             e.clientY  < absoluteRect.bottom + this._topOffset(e);
+   },
+
+   _addMouseDownHandler: function( aDraggable )
+   {
+       htmlElement  = aDraggable.getMouseDownHTMLElement();
+      if ( htmlElement  != null ) { 
+         htmlElement.draggable = aDraggable;
+         Event.observe(htmlElement , "mousedown", this._onmousedown.bindAsEventListener(this));
+         Event.observe(htmlElement, "mousedown", this._mouseDown);
+      }
+   },
+
+   _activateRegisteredDropZones: function() {
+      var n = this.dropZones.length;
+      for ( var i = 0 ; i < n ; i++ ) {
+         var dropZone = this.dropZones[i];
+         if ( dropZone.canAccept(this.currentDragObjects) )
+            dropZone.activate();
+      }
+
+      this.activatedDropZones = true;
+   },
+
+   _deactivateRegisteredDropZones: function() {
+      var n = this.dropZones.length;
+      for ( var i = 0 ; i < n ; i++ )
+         this.dropZones[i].deactivate();
+      this.activatedDropZones = false;
+   },
+
+   _onmousedown: function () {
+     Event.observe(document.body, "mousemove", this._mouseMove);
+     Event.observe(document.body, "mouseup",  this._mouseUp);
+   },
+
+   _terminateEvent: function(e) {
+      if ( e.stopPropagation != undefined )
+         e.stopPropagation();
+      else if ( e.cancelBubble != undefined )
+         e.cancelBubble = true;
+
+      if ( e.preventDefault != undefined )
+         e.preventDefault();
+      else
+         e.returnValue = false;
+   },
+
+
+          initializeEventHandlers: function() {
+             if ( typeof document.implementation != "undefined" &&
+                document.implementation.hasFeature("HTML",   "1.0") &&
+                document.implementation.hasFeature("Events", "2.0") &&
+                document.implementation.hasFeature("CSS",    "2.0") ) {
+                document.addEventListener("mouseup",   this._mouseUpHandler.bindAsEventListener(this),  false);
+                document.addEventListener("mousemove", this._mouseMoveHandler.bindAsEventListener(this), false);
+             }
+             else {
+                document.attachEvent( "onmouseup",   this._mouseUpHandler.bindAsEventListener(this) );
+                document.attachEvent( "onmousemove", this._mouseMoveHandler.bindAsEventListener(this) );
+             }
+          }
+       }
+
+       var dndMgr = new Rico.DragAndDrop();
+       dndMgr.initializeEventHandlers();
+
+
+//-------------------- ricoDraggable.js
+Rico.Draggable = Class.create();
+
+Rico.Draggable.prototype = {
+
+   initialize: function( type, htmlElement ) {
+      this.type          = type;
+      this.htmlElement   = $(htmlElement);
+      this.selected      = false;
+   },
+
+   /**
+    *   Returns the HTML element that should have a mouse down event
+    *   added to it in order to initiate a drag operation
+    *
+    **/
+   getMouseDownHTMLElement: function() {
+      return this.htmlElement;
+   },
+
+   select: function() {
+      this.selected = true;
+
+      if ( this.showingSelected )
+         return;
+
+      var htmlElement = this.getMouseDownHTMLElement();
+
+      var color = Rico.Color.createColorFromBackground(htmlElement);
+      color.isBright() ? color.darken(0.033) : color.brighten(0.033);
+
+      this.saveBackground = RicoUtil.getElementsComputedStyle(htmlElement, "backgroundColor", "background-color");
+//      this.saveBackground = Element.getStyle(htmlElement,'backgroundColor') || Element.getStyle(htmlElement,'background-color')
+      htmlElement.style.backgroundColor = color.asHex();
+      this.showingSelected = true;
+   },
+
+   deselect: function() {
+      this.selected = false;
+      if ( !this.showingSelected )
+         return;
+
+      var htmlElement = this.getMouseDownHTMLElement();
+
+      htmlElement.style.backgroundColor = this.saveBackground;
+      this.showingSelected = false;
+   },
+
+   isSelected: function() {
+      return this.selected;
+   },
+
+   startDrag: function() {
+   },
+
+   cancelDrag: function() {
+   },
+
+   endDrag: function() {
+   },
+
+   getSingleObjectDragGUI: function() {
+      return this.htmlElement;
+   },
+
+   getMultiObjectDragGUI: function( draggables ) {
+      return this.htmlElement;
+   },
+
+   getDroppedGUI: function() {
+      return this.htmlElement;
+   },
+
+   toString: function() {
+      return this.type + ":" + this.htmlElement + ":";
+   }
+
+}
+
+
+//-------------------- ricoDropzone.js
+Rico.Dropzone = Class.create();
+
+Rico.Dropzone.prototype = {
+
+   initialize: function( htmlElement ) {
+      this.htmlElement  = $(htmlElement);
+      this.absoluteRect = null;
+   },
+
+   getHTMLElement: function() {
+      return this.htmlElement;
+   },
+
+   clearPositionCache: function() {
+      this.absoluteRect = null;
+   },
+
+   getAbsoluteRect: function() {
+      if ( this.absoluteRect == null ) {
+         var htmlElement = this.getHTMLElement();
+         var pos = RicoUtil.toViewportPosition(htmlElement);
+
+         this.absoluteRect = {
+            top:    pos.y,
+            left:   pos.x,
+            bottom: pos.y + htmlElement.offsetHeight,
+            right:  pos.x + htmlElement.offsetWidth
+         };
+      }
+      return this.absoluteRect;
+   },
+
+   activate: function() {
+      var htmlElement = this.getHTMLElement();
+      if (htmlElement == null  || this.showingActive)
+         return;
+
+      this.showingActive = true;
+      this.saveBackgroundColor = htmlElement.style.backgroundColor;
+
+      var fallbackColor = "#ffea84";
+      var currentColor = Rico.Color.createColorFromBackground(htmlElement);
+      if ( currentColor == null )
+         htmlElement.style.backgroundColor = fallbackColor;
+      else {
+         currentColor.isBright() ? currentColor.darken(0.2) : currentColor.brighten(0.2);
+         htmlElement.style.backgroundColor = currentColor.asHex();
+      }
+   },
+
+   deactivate: function() {
+      var htmlElement = this.getHTMLElement();
+      if (htmlElement == null || !this.showingActive)
+         return;
+
+      htmlElement.style.backgroundColor = this.saveBackgroundColor;
+      this.showingActive = false;
+      this.saveBackgroundColor = null;
+   },
+
+   showHover: function() {
+      var htmlElement = this.getHTMLElement();
+      if ( htmlElement == null || this.showingHover )
+         return;
+
+      this.saveBorderWidth = htmlElement.style.borderWidth;
+      this.saveBorderStyle = htmlElement.style.borderStyle;
+      this.saveBorderColor = htmlElement.style.borderColor;
+
+      this.showingHover = true;
+      htmlElement.style.borderWidth = "1px";
+      htmlElement.style.borderStyle = "solid";
+      //htmlElement.style.borderColor = "#ff9900";
+      htmlElement.style.borderColor = "#ffff00";
+   },
+
+   hideHover: function() {
+      var htmlElement = this.getHTMLElement();
+      if ( htmlElement == null || !this.showingHover )
+         return;
+
+      htmlElement.style.borderWidth = this.saveBorderWidth;
+      htmlElement.style.borderStyle = this.saveBorderStyle;
+      htmlElement.style.borderColor = this.saveBorderColor;
+      this.showingHover = false;
+   },
+
+   canAccept: function(draggableObjects) {
+      return true;
+   },
+
+   accept: function(draggableObjects) {
+      var htmlElement = this.getHTMLElement();
+      if ( htmlElement == null )
+         return;
+
+      n = draggableObjects.length;
+      for ( var i = 0 ; i < n ; i++ )
+      {
+         var theGUI = draggableObjects[i].getDroppedGUI();
+/*         if (Element.getStyle(theGUI,'position')=='absolute')*/
+         if ( RicoUtil.getElementsComputedStyle( theGUI, "position" ) == "absolute" )
+         {
+            theGUI.style.position = "static";
+            theGUI.style.top = "";
+            theGUI.style.top = "";
+         }
+         htmlElement.appendChild(theGUI);
+      }
+   }
+}
+
+RicoUtil = Object.extend(RicoUtil, {
+   getElementsComputedStyle: function ( htmlElement, cssProperty, mozillaEquivalentCSS) {
+      if ( arguments.length == 2 )
+         mozillaEquivalentCSS = cssProperty;
+
+      var el = $(htmlElement);
+      if ( el.currentStyle )
+         return el.currentStyle[cssProperty];
+      else
+         return document.defaultView.getComputedStyle(el, null).getPropertyValue(mozillaEquivalentCSS);
+   },
+
+   createXmlDocument : function() {
+      if (document.implementation && document.implementation.createDocument) {
+         var doc = document.implementation.createDocument("", "", null);
+
+         if (doc.readyState == null) {
+            doc.readyState = 1;
+            doc.addEventListener("load", function () {
+               doc.readyState = 4;
+               if (typeof doc.onreadystatechange == "function")
+                  doc.onreadystatechange();
+            }, false);
+         }
+
+         return doc;
+      }
+
+      if (window.ActiveXObject)
+          return Try.these(
+            function() { return new ActiveXObject('MSXML2.DomDocument')   },
+            function() { return new ActiveXObject('Microsoft.DomDocument')},
+            function() { return new ActiveXObject('MSXML.DomDocument')    },
+            function() { return new ActiveXObject('MSXML3.DomDocument')   }
+          ) || false;
+
+      return null;
+   },
+
+   getContentAsString: function( parentNode ) {
+      return parentNode.xml != undefined ? 
+         this._getContentAsStringIE(parentNode) :
+         this._getContentAsStringMozilla(parentNode);
+   },
+
+  _getContentAsStringIE: function(parentNode) {
+     var contentStr = "";
+     for ( var i = 0 ; i < parentNode.childNodes.length ; i++ ) {
+         var n = parentNode.childNodes[i];
+         if (n.nodeType == 4) {
+             contentStr += n.nodeValue;
+         }
+         else {
+           contentStr += n.xml;
+       }
+     }
+     return contentStr;
+  },
+
+  _getContentAsStringMozilla: function(parentNode) {
+     var xmlSerializer = new XMLSerializer();
+     var contentStr = "";
+     for ( var i = 0 ; i < parentNode.childNodes.length ; i++ ) {
+          var n = parentNode.childNodes[i];
+          if (n.nodeType == 4) { // CDATA node
+              contentStr += n.nodeValue;
+          }
+          else {
+            contentStr += xmlSerializer.serializeToString(n);
+        }
+     }
+     return contentStr;
+  },
+
+   toViewportPosition: function(element) {
+      return this._toAbsolute(element,true);
+   },
+
+   toDocumentPosition: function(element) {
+      return this._toAbsolute(element,false);
+   },
+
+   /**
+    *  Compute the elements position in terms of the window viewport
+    *  so that it can be compared to the position of the mouse (dnd)
+    *  This is additions of all the offsetTop,offsetLeft values up the
+    *  offsetParent hierarchy, ...taking into account any scrollTop,
+    *  scrollLeft values along the way...
+    *
+    * IE has a bug reporting a correct offsetLeft of elements within a
+    * a relatively positioned parent!!!
+    **/
+   _toAbsolute: function(element,accountForDocScroll) {
+
+      if ( navigator.userAgent.toLowerCase().indexOf("msie") == -1 )
+         return this._toAbsoluteMozilla(element,accountForDocScroll);
+
+      var x = 0;
+      var y = 0;
+      var parent = element;
+      while ( parent ) {
+
+         var borderXOffset = 0;
+         var borderYOffset = 0;
+         if ( parent != element ) {
+            var borderXOffset = parseInt(this.getElementsComputedStyle(parent, "borderLeftWidth" ));
+            var borderYOffset = parseInt(this.getElementsComputedStyle(parent, "borderTopWidth" ));
+            borderXOffset = isNaN(borderXOffset) ? 0 : borderXOffset;
+            borderYOffset = isNaN(borderYOffset) ? 0 : borderYOffset;
+         }
+
+         x += parent.offsetLeft - parent.scrollLeft + borderXOffset;
+         y += parent.offsetTop - parent.scrollTop + borderYOffset;
+         parent = parent.offsetParent;
+      }
+
+      if ( accountForDocScroll ) {
+         x -= this.docScrollLeft();
+         y -= this.docScrollTop();
+      }
+
+      return { x:x, y:y };
+   },
+
+   /**
+    *  Mozilla did not report all of the parents up the hierarchy via the
+    *  offsetParent property that IE did.  So for the calculation of the
+    *  offsets we use the offsetParent property, but for the calculation of
+    *  the scrollTop/scrollLeft adjustments we navigate up via the parentNode
+    *  property instead so as to get the scroll offsets...
+    *
+    **/
+   _toAbsoluteMozilla: function(element,accountForDocScroll) {
+      var x = 0;
+      var y = 0;
+      var parent = element;
+      while ( parent ) {
+         x += parent.offsetLeft;
+         y += parent.offsetTop;
+         parent = parent.offsetParent;
+      }
+
+      parent = element;
+      while ( parent &&
+              parent != document.body &&
+              parent != document.documentElement ) {
+         if ( parent.scrollLeft  )
+            x -= parent.scrollLeft;
+         if ( parent.scrollTop )
+            y -= parent.scrollTop;
+         parent = parent.parentNode;
+      }
+
+      if ( accountForDocScroll ) {
+         x -= this.docScrollLeft();
+         y -= this.docScrollTop();
+      }
+
+      return { x:x, y:y };
+   },
+
+   docScrollLeft: function() {
+      if ( window.pageXOffset )
+         return window.pageXOffset;
+      else if ( document.documentElement && document.documentElement.scrollLeft )
+         return document.documentElement.scrollLeft;
+      else if ( document.body )
+         return document.body.scrollLeft;
+      else
+         return 0;
+   },
+
+   docScrollTop: function() {
+      if ( window.pageYOffset )
+         return window.pageYOffset;
+      else if ( document.documentElement && document.documentElement.scrollTop )
+         return document.documentElement.scrollTop;
+      else if ( document.body )
+         return document.body.scrollTop;
+      else
+         return 0;
+   }
+});
+
+Rico.includeLoaded('ricoDragDrop.js');
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoEffects.js b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoEffects.js
new file mode 100644 (file)
index 0000000..608bb60
--- /dev/null
@@ -0,0 +1,389 @@
+ /**
+   *  (c) 2005-2007 Richard Cowin (http://openrico.org)
+   *
+   *  Rico is 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
+   **/
+
+Rico.animate = function(effect){
+       new Rico.Effect.Animator().play(effect, arguments[1]);
+}
+
+Rico.Effect = {}
+Rico.Effect.easeIn = function(step){
+  return Math.sqrt(step)
+}
+Rico.Effect.easeOut = function(step){
+  return step*step
+}
+Rico.Stepping = {}
+Rico.Stepping.easeIn = Rico.Effect.easeIn;
+Rico.Stepping.easeOut = Rico.Effect.easeOut;
+
+Rico.Effect.Animator = Class.create();
+Rico.Effect.Animator.prototype = {
+       initialize : function(effect) {
+               this.animateMethod = this.animate.bind(this);
+               this.options = arguments[1] || {};
+               this.stepsLeft = 0;
+               if (!effect) return;
+               this.reset(effect, arguments[1]);
+       },
+       reset: function(effect){
+               this.effect = effect;
+    if (arguments[1]) this.setOptions(arguments[1]);
+               this.stepsLeft = this.options.steps;
+               this.duration = this.options.duration;
+       },
+       setOptions: function(options){
+         this.options = Object.extend({
+                       steps: 10,
+                       duration: 200,
+                       rate: function(steps){ return steps;}
+    }, options|| {});
+       },
+       play: function(effect) {
+         this.setOptions(arguments[1])
+         if (effect)
+         if (effect.step)
+                 this.reset(effect, arguments[1]);
+               else{
+                 $H(effect).keys().each((function(e){
+                   var effectClass = {fadeOut:Rico.Effect.FadeOut}[e];
+                   this.reset(new effectClass(effect[e]));
+                 }).bind(this))                  
+               }
+               this.animate();
+       },
+       stop: function() {
+               this.stepsLeft = this.options.steps;
+       },
+       pause: function() {
+               this.interupt = true;
+       },
+       resume: function() {
+         this.interupt = false;
+         if (this.stepsLeft >0)
+           this.animate();
+       },
+       animate: function() {
+         if (this.interupt)
+           return;
+               if (this.stepsLeft <=0) {
+                       if (this.effect.finish)  this.effect.finish();
+                       if (this.options.onFinish) this.options.onFinish();
+                       return;
+               }
+               if (this.timer)
+                       clearTimeout(this.timer);
+               this.effect.step(this.options.rate(this.stepsLeft));
+               this.startNextStep();
+  },
+       startNextStep: function() {
+               var stepDuration = Math.round(this.duration/this.stepsLeft) ;
+    this.duration -= stepDuration;
+    this.stepsLeft--;
+    this.timer = setTimeout(this.animateMethod, stepDuration);
+       },
+  isPlaying: function(){
+    return this.stepsLeft != 0 && !this.interupt;
+  }
+}
+
+Rico.Effect.Group = Class.create();
+Rico.Effect.Group.prototype = {
+  initialize: function(effects){
+    this.effects = effects;
+  },
+  step: function(stepsToGo){ 
+    this.effects.each(function(e){e.step(stepsToGo)});
+  },
+  finish: function(){
+    this.effects.each(function(e){if (e.finish) e.finish()});
+  }
+}
+
+Rico.Effect.SizeAndPosition = Class.create();
+Rico.Effect.SizeAndPosition.prototype = {
+   initialize: function(element, x, y, w, h) {
+      this.element = $(element);
+      this.x = x || this.element.offsetLeft;
+      this.y = y || this.element.offsetTop;
+      this.w = w || this.element.offsetWidth;
+      this.h = h || this.element.offsetHeight;
+   },
+   step: function(stepsToGo) {  
+                       var left = this.element.offsetLeft + ((this.x - this.element.offsetLeft)/stepsToGo) + "px"
+                       var top = this.element.offsetTop + ((this.y - this.element.offsetTop)/stepsToGo) + "px"
+                       var width = this.element.offsetWidth + ((this.w - this.element.offsetWidth)/stepsToGo) + "px"
+                       var height = this.element.offsetHeight + ((this.h - this.element.offsetHeight)/stepsToGo) + "px"
+      var style = this.element.style;
+                       style.left = left;
+                       style.top = top;
+                       style.width = width;
+                       style.height = height;
+   }
+}
+
+Rico.AccordionEffect = Class.create();
+Rico.AccordionEffect.prototype = {
+  initialize: function(toClose, toOpen, height) {
+    this.toClose   = toClose;
+    this.toOpen    = toOpen;
+/*    if (!navigator.appVersion.match(/\bMSIE\b/)) {*/
+      Element.makeClipping(toOpen);
+      Element.makeClipping(toClose);
+/*    }*/
+    Rico.Controls.disableNativeControls(toClose);
+    Element.show(toOpen);
+    this.toOpen.style.height = "0px";
+    this.endHeight = height;
+  },
+  step: function(framesLeft) {
+     var cHeight = Math.max(1,this.toClose.offsetHeight - parseInt((parseInt(this.toClose.offsetHeight))/framesLeft));
+     var closeHeight = cHeight + "px";
+     var openHeight = (this.endHeight - cHeight) + "px"
+     this.toClose.style.height = closeHeight;
+     this.toOpen.style.height = openHeight;
+  },
+  finish: function(){
+    Element.hide(this.toClose)
+    this.toOpen.style.height = this.endHeight + "px";
+    this.toClose.style.height = "0px";
+/*    if (!navigator.appVersion.match(/\bMSIE\b/)) {*/
+      Element.undoClipping(this.toOpen);
+      Element.undoClipping(this.toClose);
+/*    }*/
+
+               Rico.Controls.enableNativeControls(this.toOpen);
+  }
+};
+
+Rico.Effect.SizeFromBottom = Class.create()
+Rico.Effect.SizeFromBottom.prototype = {
+  initialize: function(element, y, h) {
+    this.element = $(element);
+    this.y = y || this.element.offsetTop;
+    this.h = h || this.element.offsetHeight;
+    this.options  = arguments[3] || {};
+  },
+  step: function(framesToGo) {  
+               var top = this.element.offsetTop + ((this.y - this.element.offsetTop)/framesToGo) + "px"
+               var height = this.element.offsetHeight + ((this.h - this.element.offsetHeight)/framesToGo) + "px"
+    var style = this.element.style;
+               style.height = height;     
+               style.top = top;
+  }
+}
+
+Rico.Effect.Position = Class.create();
+Rico.Effect.Position.prototype = {
+  initialize: function(element, x, y) {
+    this.element = $(element);
+    this.x = x || this.element.offsetLeft;
+    this.destTop = y || this.element.offsetTop;
+  },
+  step: function(stepsToGo) {  
+       var left = this.element.offsetLeft + ((this.x - this.element.offsetLeft)/stepsToGo) + "px"
+       var top = this.element.offsetTop + ((this.destTop - this.element.offsetTop)/stepsToGo) + "px"
+    var style = this.element.style;
+       style.left = left;
+       style.top = top;
+  }
+}
+
+Rico.Effect.FadeTo = Class.create()
+Rico.Effect.FadeTo.prototype = {
+  initialize: function(element, value){
+    this.element = element;
+    this.opacity = Element.getStyle(this.element, 'opacity') || 1.0;
+    this.target = Math.min(value, 1.0);
+  },
+  step: function(framesLeft) {
+    var curOpacity = Element.getStyle(this.element, 'opacity');
+    var newOpacity = curOpacity + (this.target - curOpacity)/framesLeft
+    Rico.Effect.setOpacity(this.element, Math.min(Math.max(0,newOpacity),1.0));
+  }
+}
+
+Rico.Effect.FadeOut = Class.create()
+Rico.Effect.FadeOut.prototype = {
+  initialize: function(element){
+    this.effect = new Rico.Effect.FadeTo(element, 0.0)
+  },
+  step: function(framesLeft) {
+    this.effect.step(framesLeft);
+  }
+}
+
+Rico.Effect.FadeIn = Class.create()
+Rico.Effect.FadeIn.prototype = {
+  initialize: function(element){
+    var options = arguments[1] || {}
+    var startValue = options.startValue || 0
+    Element.setStyle(element, 'opacity', startValue);
+    this.effect = new Rico.Effect.FadeTo(element, 1.0)
+  },
+  step: function(framesLeft) {
+    this.effect.step(framesLeft);
+  }
+}
+
+Rico.Effect.setOpacity= function(element, value) {
+   element.style.filter = "alpha(opacity:"+Math.round(value*100)+")";
+   element.style.opacity = value; 
+}
+
+Rico.Effect.SizeFromTop = Class.create()
+Rico.Effect.SizeFromTop.prototype = {
+  initialize: function(element, scrollElement, y, h) {
+     this.element = $(element);
+     this.h = h || this.element.offsetHeight;
+       //       element.style.top = y;
+     this.scrollElement = scrollElement;
+     this.options  = arguments[4] || {};
+     this.baseHeight = this.options.baseHeight ||  Math.max(this.h, this.element.offsetHeight)
+  },
+  step: function(framesToGo) {  
+    var rawHeight = this.element.offsetHeight + ((this.h - this.element.offsetHeight)/framesToGo);
+               var height = rawHeight + "px"
+               var scroll = (rawHeight - this.baseHeight) + "px";
+               this.scrollElement.style.top = scroll;
+               this.element.style.height = height;     
+  }
+}
+
+
+Rico.Effect.Height = Class.create()
+Rico.Effect.Height.prototype = {
+  initialize: function(element, endHeight) {
+    this.element = element
+               this.endHeight = endHeight
+  },
+  step: function(stepsLeft) {
+    if (this.element.constructor != Array){
+      var height = this.element.offsetHeight + ((this.endHeight - this.element.offsetHeight)/stepsLeft) + "px"
+      this.element.style.height = height;
+    } else {
+      var height = this.element[0].offsetHeight + ((this.endHeight - this.element[0].offsetHeight)/stepsLeft) + "px"
+      this.element.each(function(e){e.style.height = height})
+    }
+  }
+}
+
+Rico.Effect.SizeWidth = Class.create();
+Rico.Effect.SizeWidth.prototype = {
+    initialize: function(element, endWidth) {
+      this.element = element
+                       this.endWidth = endWidth
+    },
+    step: function(stepsLeft) {
+       delta = Math.abs(this.endWidth - parseInt(this.element.offsetWidth))/(stepsLeft);
+       this.element.style.width = (this.element.offsetWidth - delta) + "px";
+    }
+}
+
+//these are to support non Safari browsers and keep controls from bleeding through on absolute positioned element.
+Rico.Controls = {
+       editors: [],
+       scrollSelectors: [],
+       
+       disableNativeControls: function(element) {
+               Rico.Controls.defaultDisabler.disableNative(element);
+  },
+       enableNativeControls: function(element){
+               Rico.Controls.defaultDisabler.enableNative(element);
+       },
+       prepareForSizing: function(element){
+    Element.makeClipping(element)
+    Rico.Controls.disableNativeControls(element)
+  },
+  resetSizing: function(element){
+    Element.undoClipping(element)
+    Rico.Controls.enableNativeControls(element)
+       },
+       registerScrollSelectors: function(selectorSet) {
+         selectorSet.each(function(s){Rico.Controls.scrollSelectors.push(Rico.selector(s))});
+       }
+}
+
+Rico.Controls.Disabler = Class.create();
+Rico.Controls.Disabler.prototype = {
+       initialize: function(){
+               this.options = Object.extend({
+                       excludeSet: [],
+                       hidables: Rico.Controls.editors
+    }, arguments[0] || {});
+       },
+  disableNative: function(element) {
+    if (!(/Konqueror|Safari|KHTML/.test(navigator.userAgent))){
+                       if (!navigator.appVersion.match(/\bMSIE\b/))
+                               this.blockControls(element).each(function(e){Element.makeClipping(e)});
+                       else
+                         this.hidableControls(element).each(function(e){e.disable()});
+    }
+  },
+  enableNative: function(element){
+    if (!(/Konqueror|Safari|KHTML/.test(navigator.userAgent))){
+                       if (!navigator.appVersion.match(/\bMSIE\b/))
+                               this.blockControls(element).each(function(e){Element.undoClipping(e)});
+                       else
+                         this.hidableControls(element).each(function(e){e.enable()});
+    }
+  },
+       blockControls: function(element){
+         try{
+               var includes = [];
+               if (this.options.includeSet)
+                       includes = this.options.includeSet;
+               else{
+                 var selectors = this.options.includeSelectors || Rico.Controls.scrollSelectors;
+                       includes = selectors.map(function(s){return s.findAll(element)}).flatten();
+    }
+               return includes.select(function(e){return (Element.getStyle(e, 'display') != 'none') && !this.options.excludeSet.include(e)}.bind(this));
+  }catch(e) { return []}
+       },
+       hidableControls: function(element){
+               if (element)
+                       return this.options.hidables.select(function(e){return Element.childOf(e, element)});
+               else
+                       return this.options.hidables;
+       }
+}      
+                    
+Rico.Controls.defaultDisabler = new Rico.Controls.Disabler();
+Rico.Controls.blankDisabler = new Rico.Controls.Disabler({includeSet:[],hidables:[]});
+                 
+Rico.Controls.HidableInput = Class.create(); 
+Rico.Controls.HidableInput.prototype = {
+       initialize: function(field, view){      
+               this.field = field;
+               this.view = view;
+               this.enable();
+               Rico.Controls.editors.push(this);
+       },
+       enable: function(){
+               Element.hide(this.view);
+               Element.show(this.field);
+       },
+       disable: function(){
+               this.view.value = $F(this.field);
+               if (this.field.offsetWidth > 1) {
+           this.view.style.width =  parseInt(this.field.offsetWidth)  + "px";
+                 Element.hide(this.field);
+                 Element.show(this.view);
+         }
+       }
+}
+
+
+
+Element.forceRefresh = function(item) {
+  try {
+    var n = document.createTextNode(' ')
+    item.appendChild(n); item.removeChild(n);
+  } catch(e) { }
+};
+
+Rico.includeLoaded('ricoEffects.js');
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoGridCommon.js b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoGridCommon.js
new file mode 100644 (file)
index 0000000..01f7392
--- /dev/null
@@ -0,0 +1,685 @@
+/**
+  *  (c) 2005-2007 Richard Cowin (http://openrico.org)
+  *  (c) 2005-2007 Matt Brown (http://dowdybrown.com)
+  *
+  *  Rico is 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
+  **/
+
+
+if(typeof Rico=='undefined') throw("GridCommon requires the Rico JavaScript framework");
+if(typeof RicoUtil=='undefined') throw("GridCommon requires the RicoUtil Library");
+
+
+/**
+ * Define methods that are common to both SimpleGrid and LiveGrid
+ */
+Rico.GridCommon = function() {};
+
+Rico.GridCommon.prototype = {
+
+  baseInit: function() {
+    this.options = {
+      resizeBackground : 'resize.gif',
+      saveColumnInfo   : {width:true, filter:false, sort:false},  // save info in cookies?
+      allowColResize   : true,      // allow user to resize columns
+      windowResize     : true,      // Resize grid on window.resize event? Set to false when embedded in an accordian.
+      click            : null,
+      dblclick         : null,
+      contextmenu      : null,
+      useUnformattedColWidth : true,
+      menuEvent        : 'dblclick',  // event that triggers menus - click, dblclick, contextmenu, or none (no menus)
+      defaultWidth     : 100,   // in the absence of any other width info, columns will be this many pixels wide
+      scrollBarWidth   : 19,    // this is the value used in positioning calculations, it does not actually change the width of the scrollbar
+      minScrollWidth   : 100,   // min scroll area width when width of frozen columns exceeds window width
+      columnSpecs      : []
+    }
+    this.colWidths = new Array();
+    this.hdrCells=new Array();
+    this.headerColCnt=0;
+    this.headerRowIdx=0;
+    this.tabs=new Array(2);
+    this.thead=new Array(2);
+    this.tbody=new Array(2);
+  },
+
+  attachMenuEvents: function() {
+    if (!this.options.menuEvent || this.options.menuEvent=='none') return;
+    this.hideScroll=navigator.userAgent.match(/Macintosh\b.*\b(Firefox|Camino)\b/i) || Prototype.Browser.Opera;
+    this.options[this.options.menuEvent]=this.handleMenuClick.bindAsEventListener(this);
+    if (this.highlightDiv) {
+      switch (this.options.highlightElem) {
+        case 'cursorRow':
+          this.attachMenu(this.highlightDiv);
+          break;
+        case 'cursorCell':
+          for (var i=0; i<2; i++)
+            this.attachMenu(this.highlightDiv[i]);
+          break;
+      }
+    }
+    for (var i=0; i<2; i++)
+      this.attachMenu(this.tbody[i]);
+  },
+
+  attachMenu: function(elem) {
+    if (this.options.click)
+      Event.observe(elem, 'click', this.options.click, false);
+    if (this.options.dblclick) {
+      if (Prototype.Browser.WebKit || Prototype.Browser.Opera)
+        Event.observe(elem, 'click', this.handleDblClick.bindAsEventListener(this), false);
+      else
+        Event.observe(elem, 'dblclick', this.options.dblclick, false);
+    }
+    if (this.options.contextmenu) {
+      if (Prototype.Browser.Opera)
+        Event.observe(elem, 'click', this.handleContextMenu.bindAsEventListener(this), false);
+      else
+        Event.observe(elem, 'contextmenu', this.options.contextmenu, false);
+    }
+  },
+
+  // implement double-click for browsers that don't support a double-click event (e.g. Safari)
+  handleDblClick: function(e) {
+    var elem=Event.element(e);
+    if (this.dblClickElem == elem) {
+      this.options.dblclick(e);
+    } else {
+      this.dblClickElem = elem;
+      this.safariTimer=setTimeout(this.clearDblClick.bind(this),300);
+    }
+  },
+
+  clearDblClick: function() {
+    this.dblClickElem=null;
+  },
+
+  // implement right-click for browsers that don't support contextmenu event (e.g. Opera)
+  // use control-click instead
+  handleContextMenu: function(e) {
+    if( typeof( e.which ) == 'number' )
+      var b = e.which; //Netscape compatible
+    else if( typeof( e.button ) == 'number' )
+      var b = e.button; //DOM
+    else
+      return;
+    if (b==1 && e.ctrlKey)
+      this.options.contextmenu(e);
+  },
+
+  cancelMenu: function() {
+    if (this.menu && this.menu.isVisible()) this.menu.cancelmenu();
+  },
+
+  // gather info from original headings
+  getColumnInfo: function(hdrSrc) {
+    Rico.writeDebugMsg("getColumnInfo start");
+    //alert(hdrSrc.tagName+' '+hdrSrc.id+' len='+hdrSrc.length);
+    if (hdrSrc.length == 0) return;
+    this.headerRowCnt=hdrSrc.length;
+    var colcnt;
+    for (r=0; r<this.headerRowCnt; r++) {
+      var headerRow = hdrSrc[r];
+      var headerCells=headerRow.cells;
+      if (r >= this.hdrCells.length) this.hdrCells[r]=new Array();
+      for (c=0; c<headerCells.length; c++) {
+        var obj={};
+        obj.cell=headerCells[c];
+        obj.colSpan=headerCells[c].colSpan || 1;  // Safari & Konqueror return default colspan of 0
+        if (this.options.useUnformattedColWidth) obj.initWidth=headerCells[c].offsetWidth
+        this.hdrCells[r].push(obj);
+      }
+      if (headerRow.id.slice(-5)=='_main') {
+        colcnt=this.hdrCells[r].length;
+        this.headerRowIdx=r;
+      }
+    }
+    Rico.writeDebugMsg("getColumnInfo end");
+    if (!colcnt) {
+      this.headerRowIdx=this.headerRowCnt-1;
+      colcnt=this.hdrCells[this.headerRowIdx].length
+    }
+    return colcnt;
+  },
+
+  // create column array
+  createColumnArray: function() {
+    this.direction=Element.getStyle(this.outerDiv,'direction').toLowerCase();  // ltr or rtl
+    this.align=this.direction=='rtl' ? ['right','left'] : ['left','right'];
+    //alert(this.direction+' : '+this.align[0]);
+    this.columns = new Array();
+    for (var c=0 ; c < this.headerColCnt; c++) {
+      Rico.writeDebugMsg("createColumnArray: c="+c);
+      var tabidx=c<this.options.frozenColumns ? 0 : 1;
+      this.columns.push(new Rico.TableColumn(this, c, this.hdrCells[this.headerRowIdx][c], tabidx));
+    }
+    this.getCookie();
+  },
+
+  // create div structure
+  createDivs: function() {
+    Rico.writeDebugMsg("createDivs start");
+    this.outerDiv   = this.createDiv("outer");
+    this.scrollDiv  = this.createDiv("scroll",this.outerDiv);
+    this.frozenTabs = this.createDiv("frozenTabs",this.outerDiv);
+    this.innerDiv   = this.createDiv("inner",this.outerDiv);
+    this.resizeDiv  = this.createDiv("resize",this.outerDiv);
+    this.resizeDiv.style.display="none";
+    this.exportDiv  = this.createDiv("export",this.outerDiv);
+    this.exportDiv.style.display="none";
+    //this.frozenTabs.style[this.align[0]]='0px';
+    //this.innerDiv.style[this.align[0]]='0px';
+    Rico.writeDebugMsg("createDivs end");
+  },
+
+  createDiv: function(elemName,elemParent) {
+    var id=this.tableId+"_"+elemName+"Div";
+    newdiv=$(id);
+    if (!newdiv) {
+      var newdiv = document.createElement("div");
+      newdiv.id = id;
+      if (elemParent) elemParent.appendChild(newdiv);
+    }
+    newdiv.className = "ricoLG_"+elemName+"Div";
+    return newdiv;
+  },
+
+  baseSizeDivs: function() {
+    this.setOtherHdrCellWidths();
+    this.tabs[0].style.display=this.options.frozenColumns ? '' : 'none';
+    this.hdrHt=Math.max(RicoUtil.nan2zero(this.thead[0].offsetHeight),this.thead[1].offsetHeight);
+    this.dataHt=Math.max(RicoUtil.nan2zero(this.tbody[0].offsetHeight),this.tbody[1].offsetHeight);
+    this.frzWi=this.borderWidth(this.tabs[0]);
+    var borderWi=this.borderWidth(this.columns[0].dataCell);
+    Rico.writeDebugMsg('baseSizeDivs '+this.tableId+': hdrHt='+this.hdrHt+' dataHt='+this.dataHt);
+    //alert(this.tableId+' frzWi='+this.frzWi+' borderWi='+borderWi);
+    for (var i=0; i<this.options.frozenColumns; i++)
+      if (this.columns[i].visible) this.frzWi+=parseInt(this.columns[i].colWidth)+borderWi;
+    this.scrTabWi=this.borderWidth(this.tabs[1]);
+    for (var i=this.options.frozenColumns; i<this.columns.length; i++)
+      if (this.columns[i].visible) this.scrTabWi+=parseInt(this.columns[i].colWidth)+borderWi;
+    this.scrWi=this.scrTabWi+this.options.scrollBarWidth;
+    var wiLimit=RicoUtil.windowWidth()-this.options.scrollBarWidth-8;
+    if (this.outerDiv.parentNode.clientWidth > 0)
+      wiLimit=Math.min(this.outerDiv.parentNode.clientWidth, wiLimit);
+    var overage=this.frzWi+this.scrWi-wiLimit;
+    Rico.writeDebugMsg('baseSizeDivs '+this.tableId+': scrWi='+this.scrWi+' wiLimit='+wiLimit+' overage='+overage+' clientWidth='+this.outerDiv.parentNode.clientWidth);
+    if (overage > 0 && this.options.frozenColumns < this.columns.length)
+      this.scrWi=Math.max(this.scrWi-overage, this.options.minScrollWidth);
+    this.scrollDiv.style.width=this.scrWi+'px';
+    this.scrollDiv.style.top=this.hdrHt+'px';
+    this.frozenTabs.style.width=this.scrollDiv.style[this.align[0]]=this.innerDiv.style[this.align[0]]=this.frzWi+'px';
+    this.outerDiv.style.width=(this.frzWi+this.scrWi)+'px';
+  },
+
+  borderWidth: function(elem) {
+    return RicoUtil.nan2zero(Element.getStyle(elem,'border-left-width')) + RicoUtil.nan2zero(Element.getStyle(elem,'border-right-width'));
+  },
+
+  setOtherHdrCellWidths: function() {
+    for (var r=0; r<this.hdrCells.length; r++) {
+      if (r==this.headerRowIdx) continue;
+      Rico.writeDebugMsg('setOtherHdrCellWidths: r='+r);
+      var c=i=0;
+      while (i<this.headerColCnt && c<this.hdrCells[r].length) {
+        var hdrcell=this.hdrCells[r][c];
+        var cell=hdrcell.cell;
+        var origSpan=newSpan=hdrcell.colSpan;
+        for (var w=j=0; j<origSpan; j++, i++) {
+          if (this.columns[i].hdrCell.style.display=='none')
+            newSpan--;
+          else if (this.columns[i].hdrColDiv.style.display!='none')
+            w+=parseInt(this.columns[i].colWidth);
+        }
+        if (!hdrcell.hdrColDiv || !hdrcell.hdrCellDiv) {
+          var divs=cell.getElementsByTagName('div');
+          hdrcell.hdrColDiv=(divs.length<1) ? RicoUtil.wrapChildren(cell,'ricoLG_col') : divs[0];
+          hdrcell.hdrCellDiv=(divs.length<2) ? RicoUtil.wrapChildren(hdrcell.hdrColDiv,'ricoLG_cell') : divs[1];
+        }
+        if (newSpan==0) {
+          cell.style.display='none';
+        } else if (w==0) {
+          hdrcell.hdrColDiv.style.display='none';
+          cell.colSpan=newSpan;
+        } else {
+          cell.style.display='';
+          hdrcell.hdrColDiv.style.display='';
+          cell.colSpan=newSpan;
+          hdrcell.hdrColDiv.style.width=w+'px';
+        }
+        c++;
+      }
+    }
+  },
+  
+  cell: function(r,c) {
+    return (0<=c && c<this.columns.length && r>=0) ? this.columns[c].cell(r) : null;
+  },
+
+  availHt: function() {
+    var divPos=Position.page(this.outerDiv);
+    return RicoUtil.windowHeight()-divPos[1]-2*this.options.scrollBarWidth-15;  // allow for scrollbar and some margin
+  },
+
+  handleScroll: function(e) {
+    var newTop=(this.hdrHt-this.scrollDiv.scrollTop)+'px';
+    this.tabs[0].style.top=newTop;
+    this.setHorizontalScroll();
+  },
+
+  setHorizontalScroll: function() {
+    var newLeft=(-this.scrollDiv.scrollLeft)+'px';
+    this.hdrTabs[1].style.left=newLeft;
+  },
+
+  pluginScroll: function() {
+     if (this.scrollPluggedIn) return;
+     Event.observe(this.scrollDiv,"scroll",this.scrollEventFunc, false);
+     this.scrollPluggedIn=true;
+  },
+
+  unplugScroll: function() {
+     Event.stopObserving(this.scrollDiv,"scroll", this.scrollEventFunc , false);
+     this.scrollPluggedIn=false;
+  },
+
+  printVisible: function(exportType) {
+    this.exportStart();
+    var limit=this.pageSize;
+    if (this.buffer && this.buffer.totalRows < limit) limit=this.buffer.totalRows;
+    for(var r=0; r < limit; r++) {
+      this.exportText+="<tr>";
+      for (var c=0; c<this.columns.length; c++) {
+        if (this.columns[c].visible)
+          this.exportText+="<td style='"+this.exportStyle(this.columns[c].cell(r))+"'>"+this.columns[c].getFormattedValue(r)+"</td>";
+      }
+      this.exportText+="</tr>";
+    }
+    this.exportFinish(exportType);
+  },
+
+  exportStart: function() {
+    this.exportText="<table border='1' cellspacing='0'><thead style='display: table-header-group;'>";
+
+    for (var r=0; r<this.hdrCells.length; r++) {
+      if (this.hdrCells[r].length==0 || Element.getStyle(this.hdrCells[r][0].cell.parentNode,'display')=='none') continue;
+      this.exportText+="<tr>";
+      for (var c=0,i=0; c<this.hdrCells[r].length; c++) {
+        var hdrcell=this.hdrCells[r][c];
+        var newSpan=hdrcell.colSpan;
+        for (var j=0; j<hdrcell.colSpan; j++, i++)
+          if (!this.columns[i].visible) newSpan--;
+        if (newSpan > 0) {
+          var divs=Element.getElementsByClassName(hdrcell.cell,'ricoLG_cell');
+          var cell=divs && divs.length>0 ? divs[0] : hdrcell.cell;
+          this.exportText+="<td style='"+this.exportStyle(cell)+"'";
+          if (hdrcell.colSpan > 1) this.exportText+=" colspan='"+newSpan+"'";
+          this.exportText+=">"+RicoUtil.getInnerText(cell)+"</td>";
+        }
+      }
+      this.exportText+="</tr>";
+    }
+
+    for (var c=0; c<this.columns.length; c++)
+    this.exportText+="</thead><tbody>";
+  },
+
+  exportFinish: function(exportType) {
+    if (this.hideMsg) this.hideMsg();
+    this.exportText+="</tbody></table>";
+    this.exportDiv.innerHTML=this.exportText;
+    this.exportText=undefined;
+    if (this.cancelMenu) this.cancelMenu();
+    window.open(Rico.htmDir+'export-'+(exportType || 'plain')+'.html?'+this.exportDiv.id,'',this.options.exportWindow);
+  },
+  
+  exportStyle: function(elem) {
+    var styleList=['background-color','color','text-align','font-weight']
+    for (var i=0,s=''; i < styleList.length; i++) {
+      var curstyle=Element.getStyle(elem,styleList[i]);
+      if (curstyle) s+=styleList[i]+':'+curstyle+';';
+    }
+    return s;
+  },
+
+  // Gets the value of the specified cookie
+  getCookie: function() {
+    var c=RicoUtil.getCookie(this.tableId);
+    if (!c) return;
+       var cookieVals=c.split(',');
+       for (var i=0; i<cookieVals.length; i++) {
+         var v=cookieVals[i].split(':');
+         if (v.length!=2) continue;
+         var colnum=parseInt(v[0].slice(1));
+         if (colnum < 0 || colnum >= this.columns.length) continue;
+         var col=this.columns[colnum];
+         switch (v[0].charAt(0)) {
+           case 'w':
+             col.setColWidth(v[1]);
+          col.customWidth=true;
+             break;
+           case 'h':
+             if (v[1].toLowerCase()=='true')
+               col.showColumn(true);
+             else
+               col.hideColumn(true);
+             break;
+           case 's':
+             col.setSorted(v[1]);
+             break;
+           case 'f':
+             var filterTemp=v[1].split('~');
+             col.filterOp=filterTemp.shift();
+          col.filterValues = [];
+          col.filterType = Rico.TableColumn.USERFILTER;
+          for (var j=0; j<filterTemp.length; j++)
+            col.filterValues.push(unescape(filterTemp[j]));
+             break;
+         }
+       }
+  },
+  
+  // Write information to cookie
+  setCookie: function() {
+       var cookieVals=[];
+       for (var i=0; i<this.columns.length; i++) {
+         var col=this.columns[i];
+         if (this.options.saveColumnInfo.width) {
+         if (col.customWidth) cookieVals.push('w'+i+':'+col.colWidth);
+         if (col.customVisible) cookieVals.push('h'+i+':'+col.visible);
+         }
+      if (this.options.saveColumnInfo.sort) {
+        if (col.currentSort != Rico.TableColumn.UNSORTED)
+          cookieVals.push('s'+i+':'+col.currentSort);
+      }
+      if (this.options.saveColumnInfo.filter && col.filterType == Rico.TableColumn.USERFILTER) {
+        var filterTemp=[col.filterOp];
+        for (var j=0; j<col.filterValues.length; j++)
+          filterTemp.push(escape(col.filterValues[j]));
+        cookieVals.push('f'+i+':'+filterTemp.join('~'));
+      }
+       }
+       if (cookieVals.length > 0)
+         RicoUtil.setCookie(this.tableId, cookieVals.join(','), this.options.cookieDays, this.options.cookiePath, this.options.cookieDomain);
+  }
+
+}
+
+Rico.TableColumn = Class.create();
+
+Rico.TableColumn.UNFILTERED   = 0;
+Rico.TableColumn.SYSTEMFILTER = 1;  /* system-generated filter, not shown to user */
+Rico.TableColumn.USERFILTER   = 2;
+
+Rico.TableColumn.UNSORTED   = 0;
+Rico.TableColumn.SORT_ASC   = "ASC";
+Rico.TableColumn.SORT_DESC  = "DESC";
+Rico.TableColumn.MINWIDTH   = 10; // min column width when user is resizing
+
+Rico.TableColumn.DOLLAR  = {type:'number', prefix:'$', decPlaces:2, ClassName:'alignright'};
+Rico.TableColumn.EURO    = {type:'number', prefix:'&euro;', decPlaces:2, ClassName:'alignright'};
+Rico.TableColumn.PERCENT = {type:'number', suffix:'%', decPlaces:2, multiplier:100, ClassName:'alignright'};
+Rico.TableColumn.QTY     = {type:'number', decPlaces:0, ClassName:'alignright'};
+Rico.TableColumn.DEFAULT = {type:"raw"};
+
+Rico.TableColumn.prototype = {
+
+  baseInit: function(liveGrid,colIdx,hdrInfo,tabIdx) {
+    Rico.writeDebugMsg("TableColumn.init index="+colIdx+" tabIdx="+tabIdx);
+    this.liveGrid  = liveGrid;
+    this.index     = colIdx;
+    this.hideWidth = Rico.isKonqueror || Prototype.Browser.WebKit || liveGrid.headerRowCnt>1 ? 5 : 2;  // column width used for "hidden" columns. Anything less than 5 causes problems with Konqueror. Best to keep this greater than padding used inside cell.
+    this.options   = liveGrid.options;
+    this.tabIdx    = tabIdx;
+    this.hdrCell   = hdrInfo.cell;
+    this.body = document.getElementsByTagName("body")[0];  // work around FireFox bug (document.body doesn't exist after XSLT)
+    this.displayName  = this.getDisplayName(this.hdrCell);
+    var divs=this.hdrCell.getElementsByTagName('div');
+    this.hdrColDiv=(divs.length<1) ? RicoUtil.wrapChildren(this.hdrCell,'ricoLG_col') : divs[0];
+    this.hdrCellDiv=(divs.length<2) ? RicoUtil.wrapChildren(this.hdrColDiv,'ricoLG_cell') : divs[1];
+    var sectionIndex= tabIdx==0 ? colIdx : colIdx-liveGrid.options.frozenColumns;
+    this.dataCell = liveGrid.tbody[tabIdx].rows[0].cells[sectionIndex];
+    var divs=this.dataCell.getElementsByTagName('div');
+    this.dataColDiv=(divs.length<1) ? RicoUtil.wrapChildren(this.dataCell,'ricoLG_col') : divs[0];
+
+    this.mouseDownHandler= this.handleMouseDown.bindAsEventListener(this);
+    this.mouseMoveHandler= this.handleMouseMove.bindAsEventListener(this);
+    this.mouseUpHandler  = this.handleMouseUp.bindAsEventListener(this);
+    this.mouseOutHandler = this.handleMouseOut.bindAsEventListener(this);
+
+    this.fieldName = 'col'+this.index;
+    var spec = liveGrid.options.columnSpecs[colIdx];
+    this.format=Object.extend( {}, Rico.TableColumn.DEFAULT);
+    switch (typeof spec) {
+      case 'object':
+        if (typeof spec.format=='string') Object.extend(this.format, Rico.TableColumn[spec.format.toUpperCase()]);
+        Object.extend(this.format, spec);
+        break;
+      case 'string':
+        if (spec.slice(0,4)=='spec') spec=spec.slice(4).toUpperCase();  // for backwards compatibility
+        this.format=typeof Rico.TableColumn[spec]=='object' ? Rico.TableColumn[spec] : Rico.TableColumn.DEFAULT;
+        break;
+    }
+    this.dataColDiv.className += (this.format.ClassName) ? ' '+this.format.ClassName : ' '+liveGrid.tableId+'_col'+colIdx;
+    this.visible=true;
+    if (typeof this.format.visible=='boolean') this.visible=this.format.visible;
+    if (typeof this.format.type!='string') this.format.type='raw';
+    Rico.writeDebugMsg("TableColumn.init index="+colIdx+" fieldName="+this.fieldName+' type='+this.format.type);
+    this.sortable     = typeof this.format.canSort=='boolean' ? this.format.canSort : liveGrid.options.canSortDefault;
+    this.currentSort  = Rico.TableColumn.UNSORTED;
+    this.filterable   = typeof this.format.canFilter=='boolean' ? this.format.canFilter : liveGrid.options.canFilterDefault;
+    this.filterType   = Rico.TableColumn.UNFILTERED;
+    this.hideable     = typeof this.format.canHide=='boolean' ? this.format.canHide : liveGrid.options.canHideDefault;
+    if (typeof this.isNullable!='boolean') this.isNullable = /number|date/.test(this.format.type);
+    this.isText       = /raw|text/.test(this.format.type);
+
+    var wi=(typeof(this.format.width)=='number') ? this.format.width : hdrInfo.initWidth;
+    wi=(typeof(wi)=='number') ? Math.max(wi,Rico.TableColumn.MINWIDTH) : liveGrid.options.defaultWidth;
+    this.setColWidth(wi);
+    if (!this.visible) this.setDisplayNone();
+    if (this.options.allowColResize && !this.format.noResize) this.insertResizer();
+  },
+
+  insertResizer: function() {
+    this.hdrCell.style.width='';
+    var resizer=this.hdrCellDiv.appendChild(document.createElement('div'));
+    resizer.className='ricoLG_Resize';
+    resizer.style[this.liveGrid.align[1]]='0px';
+    if (this.options.resizeBackground) {
+      var resizePath=Rico.imgDir+this.options.resizeBackground;
+      if (Prototype.Browser.IE) resizePath=location.protocol+resizePath;
+      resizer.style.backgroundImage='url('+resizePath+')';
+    }
+    Event.observe(resizer,"mousedown", this.mouseDownHandler, false);
+  },
+
+  // get the display name of a column
+  getDisplayName: function(el) {
+    var anchors=el.getElementsByTagName("A");
+    //Check the existance of A tags
+    if (anchors.length > 0)
+      return anchors[0].innerHTML;
+    else
+      return el.innerHTML.stripTags();
+  },
+  
+  _clear: function(gridCell) {
+    gridCell.innerHTML='&nbsp;';
+  },
+
+  clearCell: function(rowIndex) {
+    var gridCell=this.cell(rowIndex);
+    this._clear(gridCell,rowIndex);
+    if (!this.liveGrid.buffer) return;
+    var acceptAttr=this.liveGrid.buffer.options.acceptAttr;
+    for (var k=0; k<acceptAttr.length; k++) {
+      switch (acceptAttr[k]) {
+        case 'style': gridCell.style.cssText=''; break;
+        case 'class': gridCell.className=''; break;
+        default:      gridCell['_'+acceptAttr[k]]=''; break;
+      }
+    }
+  },
+
+  dataTable: function() {
+    return this.liveGrid.tabs[this.tabIdx];
+  },
+  
+  numRows: function() {
+    return this.dataColDiv.childNodes.length;
+  },
+
+  clearColumn: function() {
+    var childCnt=this.numRows();
+    for (var r=0; r<childCnt; r++)
+      this.clearCell(r);
+  },
+
+  cell: function(r) {
+    return this.dataColDiv.childNodes[r];
+  },
+  
+  getFormattedValue: function(r) {
+    return RicoUtil.getInnerText(this.cell(r));
+  },
+
+  setColWidth: function(wi) {
+    if (typeof wi=='number') {
+      wi=parseInt(wi);
+      if (wi < Rico.TableColumn.MINWIDTH) return;
+      wi=wi+'px';
+    }
+    Rico.writeDebugMsg('setColWidth '+this.index+': '+wi);
+    this.colWidth=wi;
+    this.hdrColDiv.style.width=wi;
+    this.dataColDiv.style.width=wi;
+  },
+
+  pluginMouseEvents: function() {
+    if (this.mousePluggedIn==true) return;
+    Event.observe(this.body,"mousemove", this.mouseMoveHandler, false);
+    Event.observe(this.body,"mouseup",   this.mouseUpHandler  , false);
+    Event.observe(this.body,"mouseout",  this.mouseOutHandler , false);
+    this.mousePluggedIn=true;
+  },
+
+  unplugMouseEvents: function() {
+    Event.stopObserving(this.body,"mousemove", this.mouseMoveHandler, false);
+    Event.stopObserving(this.body,"mouseup",   this.mouseUpHandler  , false);
+    Event.stopObserving(this.body,"mouseout",  this.mouseOutHandler , false);
+    this.mousePluggedIn=false;
+  },
+
+  handleMouseDown: function(e) {
+    this.resizeStart=e.clientX;
+    this.origWidth=parseInt(this.colWidth);
+    var p=Position.positionedOffset(this.hdrCell);
+    if (this.liveGrid.direction=='rtl') {
+      this.edge=p[0]+this.liveGrid.options.scrollBarWidth;
+      switch (this.tabIdx) {
+        case 0: this.edge+=this.liveGrid.innerDiv.offsetWidth; break;
+        case 1: this.edge-=this.liveGrid.scrollDiv.scrollLeft; break;
+      }
+    } else {
+      this.edge=p[0]+this.hdrCell.offsetWidth;
+      if (this.tabIdx>0) this.edge+=RicoUtil.nan2zero(this.liveGrid.tabs[0].offsetWidth)-this.liveGrid.scrollDiv.scrollLeft;
+    }
+    this.liveGrid.resizeDiv.style.left=this.edge+"px";
+    this.liveGrid.resizeDiv.style.display="";
+    this.liveGrid.outerDiv.style.cursor='e-resize';
+    this.tmpHighlight=this.liveGrid.highlightEnabled;
+    this.liveGrid.highlightEnabled=false;
+    this.pluginMouseEvents();
+    Event.stop(e);
+  },
+
+  handleMouseMove: function(e) {
+    var delta=e.clientX-this.resizeStart;
+    var newWidth=(this.liveGrid.direction=='rtl') ? this.origWidth-delta : this.origWidth+delta;
+    if (newWidth < Rico.TableColumn.MINWIDTH) return;
+    this.liveGrid.resizeDiv.style.left=(this.edge+delta)+"px";
+    this.colWidth=newWidth;
+    Event.stop(e);
+  },
+
+  handleMouseUp: function(e) {
+    this.unplugMouseEvents();
+    Rico.writeDebugMsg('handleMouseUp '+this.liveGrid.tableId);
+    this.liveGrid.outerDiv.style.cursor='';
+    this.liveGrid.resizeDiv.style.display="none";
+    this.setColWidth(this.colWidth);
+    this.customWidth=true;
+    this.liveGrid.setCookie();
+    this.liveGrid.highlightEnabled=this.tmpHighlight;
+    this.liveGrid.sizeDivs();
+    Event.stop(e);
+  },
+
+  handleMouseOut: function(e) {
+    var reltg = (e.relatedTarget) ? e.relatedTarget : e.toElement;
+    while (reltg != null && reltg.nodeName.toLowerCase() != 'body')
+      reltg=reltg.parentNode;
+    if (reltg!=null && reltg.nodeName.toLowerCase() == 'body') return true;
+    this.handleMouseUp(e);
+    return true;
+  },
+
+  setDisplayNone: function() {
+    this.hdrCell.style.display='none';
+    this.hdrColDiv.style.display='none';
+    this.dataCell.style.display='none';
+    this.dataColDiv.style.display='none';
+  },
+
+  // recalcTableWidth defaults to true
+  hideColumn: function(noresize) {
+    Rico.writeDebugMsg('hideColumn '+this.liveGrid.tableId);
+    this.setDisplayNone();
+    this.liveGrid.cancelMenu();
+    this.visible=false;
+    this.customVisible=true;
+    if (noresize) return;
+    this.liveGrid.setCookie();
+    this.liveGrid.sizeDivs();
+  },
+
+  showColumn: function(noresize) {
+    Rico.writeDebugMsg('showColumn '+this.liveGrid.tableId);
+    this.hdrCell.style.display='';
+    this.hdrColDiv.style.display='';
+    this.dataCell.style.display='';
+    this.dataColDiv.style.display='';
+    this.liveGrid.cancelMenu();
+    this.visible=true;
+    this.customVisible=true;
+    if (noresize) return;
+    this.liveGrid.setCookie();
+    this.liveGrid.sizeDivs();
+  },
+
+  setImage: function() {
+    if ( this.currentSort == Rico.TableColumn.SORT_ASC ) {
+       this.imgSort.style.display='';
+       this.imgSort.src=Rico.imgDir+this.options.sortAscendImg;
+    } else if ( this.currentSort == Rico.TableColumn.SORT_DESC ) {
+       this.imgSort.style.display='';
+       this.imgSort.src=Rico.imgDir+this.options.sortDescendImg;
+    } else {
+       this.imgSort.style.display='none';
+    }
+    if (this.filterType == Rico.TableColumn.USERFILTER) {
+       this.imgFilter.style.display='';
+       this.imgFilter.title=this.getFilterText();
+    } else {
+       this.imgFilter.style.display='none';
+    }
+  },
+
+  canHideShow: function() {
+    return this.hideable;
+  }
+
+};
+
+Rico.includeLoaded('ricoGridCommon.js');
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoLiveGrid.js b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoLiveGrid.js
new file mode 100644 (file)
index 0000000..bc2af62
--- /dev/null
@@ -0,0 +1,1806 @@
+/**
+  *  (c) 2005-2007 Richard Cowin (http://openrico.org)
+  *  (c) 2005-2007 Matt Brown (http://dowdybrown.com)
+  *
+  *  Rico is 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
+  **/
+
+
+if(typeof Rico=='undefined') throw("LiveGrid requires the Rico JavaScript framework");
+if(typeof RicoUtil=='undefined') throw("LiveGrid requires the RicoUtil Library");
+if(typeof RicoTranslate=='undefined') throw("LiveGrid requires the RicoTranslate Library");
+if(typeof Rico.TableColumn=='undefined') throw("LiveGrid requires ricoGridCommon.js");
+
+
+Rico.Buffer = {};
+
+/**
+ * Loads buffer with data that already exists in the document as an HTML table (no AJAX).
+ * Also serves as a base class for AJAX-enabled buffers.
+ */
+Rico.Buffer.Base = Class.create();
+
+Rico.Buffer.Base.prototype = {
+
+  initialize: function(dataTable, options) {
+    this.clear();
+    this.updateInProgress = false;
+    this.lastOffset = 0;
+    this.rcvdRowCount = false;  // true if an eof element was included in the last xml response
+    this.foundRowCount = false; // true if an xml response is ever received with eof true
+    this.totalRows = 0;
+    this.rowcntContent = "";
+    this.rcvdOffset = -1;
+    this.options = {
+      fixedHdrRows     : 0,
+      canFilter        : false, // does buffer object support filtering?
+      isEncoded        : true,  // is the data received via ajax html encoded?
+      acceptAttr       : []     // attributes that can be copied from original/ajax data (e.g. className, style, id)
+    }
+    Object.extend(this.options, options || {});
+    if (dataTable) {
+      this.loadRowsFromTable(dataTable);
+    } else {
+      this.clear();
+    }
+  },
+
+  registerGrid: function(liveGrid) {
+    this.liveGrid = liveGrid;
+  },
+
+  setTotalRows: function( newTotalRows ) {
+    if (this.totalRows == newTotalRows) return;
+    this.totalRows = newTotalRows;
+    if (this.liveGrid) {
+      Rico.writeDebugMsg("setTotalRows, newTotalRows="+newTotalRows);
+      if (this.liveGrid.sizeTo=='data') this.liveGrid.resizeWindow();
+      this.liveGrid.updateHeightDiv();
+    }
+  },
+
+  loadRowsFromTable: function(tableElement) {
+    this.rows = this.dom2jstable(tableElement,this.options.fixedHdrRows);
+    this.startPos = 0;
+    this.size = this.rows.length;
+    this.setTotalRows(this.size);
+    this.rowcntContent = this.size.toString();
+    this.rcvdRowCount = true;
+    this.foundRowCount = true;
+  },
+
+  dom2jstable: function(rowsElement,firstRow) {
+    var newRows = new Array();
+    var trs = rowsElement.getElementsByTagName("tr");
+    var acceptAttr=this.options.acceptAttr;
+    for ( var i=firstRow || 0; i < trs.length; i++ ) {
+      var row = new Array();
+      var cells = trs[i].getElementsByTagName("td");
+      for ( var j=0; j < cells.length ; j++ ) {
+        row[j]={};
+        row[j].content=RicoUtil.getContentAsString(cells[j],this.options.isEncoded);
+        for (var k=0; k<acceptAttr.length; k++) {
+          row[j]['_'+acceptAttr[k]]=cells[j].getAttribute(acceptAttr[k]);
+        }
+        if (Prototype.Browser.IE) row[j]._class=cells[j].getAttribute('className');
+      }
+      newRows.push( row );
+    }
+    return newRows;
+  },
+
+  _blankRow: function() {
+    var newRow=[];
+    for (var i=0; i<this.liveGrid.columns.length; i++) {
+      newRow[i]={};
+      newRow[i].content='';
+    }
+    return newRow;
+  },
+
+  insertRow: function(beforeRowIndex) {
+    this.rows.splice(beforeRowIndex,0,this._blankRow());
+  },
+
+  appendRows: function(cnt) {
+    for (var i=0; i<cnt; i++)
+      this.rows.push(this._blankRow());
+    this.size=this.rows.length;
+  },
+
+  sortBuffer: function(colnum,sortdir,coltype,getvalfunc) {
+    this.sortColumn=colnum;
+    this.getValFunc=getvalfunc;
+    var sortFunc;
+    switch (coltype) {
+      case 'number': sortFunc=this._sortNumeric.bind(this); break;
+      case 'control':sortFunc=this._sortControl.bind(this); break;
+      default:       sortFunc=this._sortAlpha.bind(this); break;
+    }
+    this.rows.sort(sortFunc);
+    if (sortdir=='DESC') this.rows.reverse();
+  },
+
+  _sortAlpha: function(a,b) {
+    var aa = this.sortColumn<a.length ? RicoUtil.getInnerText(a[this.sortColumn].content) : '';
+    var bb = this.sortColumn<b.length ? RicoUtil.getInnerText(b[this.sortColumn].content) : '';
+    if (aa==bb) return 0;
+    if (aa<bb) return -1;
+    return 1;
+  },
+
+  _sortNumeric: function(a,b) {
+    var aa = this.sortColumn<a.length ? parseFloat(RicoUtil.getInnerText(a[this.sortColumn].content)) : 0;
+    if (isNaN(aa)) aa = 0;
+    var bb = this.sortColumn<b.length ? parseFloat(RicoUtil.getInnerText(b[this.sortColumn].content)) : 0;
+    if (isNaN(bb)) bb = 0;
+    return aa-bb;
+  },
+
+  _sortControl: function(a,b) {
+    var aa = this.sortColumn<a.length ? RicoUtil.getInnerText(a[this.sortColumn].content) : '';
+    var bb = this.sortColumn<b.length ? RicoUtil.getInnerText(b[this.sortColumn].content) : '';
+    if (this.getValFunc) {
+      aa=this.getValFunc(aa);
+      bb=this.getValFunc(bb);
+    }
+    if (aa==bb) return 0;
+    if (aa<bb) return -1;
+    return 1;
+  },
+
+  clear: function() {
+    this.rows = new Array();
+    this.startPos = -1;
+    this.size = 0;
+    this.windowPos = 0;
+  },
+
+  isInRange: function(position) {
+    var lastRow=Math.min(this.totalRows, position + this.liveGrid.pageSize)
+    return (position >= this.startPos) && (lastRow <= this.endPos()); // && (this.size != 0);
+  },
+
+  endPos: function() {
+    return this.startPos + this.rows.length;
+  },
+
+  fetch: function(offset) {
+    this.liveGrid.refreshContents(offset);
+    return;
+  },
+
+  exportAllRows: function(populate,finish) {
+    populate(this.getRows(0,this.totalRows));
+    finish();
+  },
+
+  setWindow: function(start, count) {
+    this.windowStart = start - this.startPos;
+    this.windowEnd = Math.min(this.windowStart + count,this.size);
+    this.windowPos = start;
+  },
+
+  isVisible: function(bufRow) {
+    return bufRow < this.rows.length && bufRow >= this.windowStart && bufRow < this.windowEnd;
+  },
+
+  getWindowCell: function(windowRow,col) {
+    var bufrow=this.windowStart+windowRow;
+    return this.isVisible(bufrow) && col < this.rows[bufrow].length ? this.rows[bufrow][col] : null;
+  },
+
+  getWindowValue: function(windowRow,col) {
+    var cell=this.getWindowCell(windowRow,col);
+    return cell ? cell.content : null;
+  },
+
+  setWindowValue: function(windowRow,col,newval) {
+    var bufRow=this.windowStart+windowRow;
+    if (bufRow >= this.windowEnd) return false;
+    return this.setValue(bufRow,col,newval);
+  },
+
+  getCell: function(bufRow,col) {
+    return bufRow < this.size ? this.rows[bufRow][col] : null;
+  },
+
+  getValue: function(bufRow,col) {
+    var cell=this.getCell(bufRow,col);
+    return cell ? cell.content : null;
+  },
+
+  setValue: function(bufRow,col,newval,newstyle) {
+    if (bufRow>=this.size) return false;
+    if (!this.rows[bufRow][col]) this.rows[bufRow][col]={};
+    this.rows[bufRow][col].content=newval;
+    if (typeof newstyle=='string') this.rows[bufRow][col]._style=newstyle;
+    this.rows[bufRow][col].modified=true;
+    return true;
+  },
+
+  getRows: function(start, count) {
+    var begPos = start - this.startPos;
+    var endPos = Math.min(begPos + count,this.size);
+    var results = new Array();
+    for ( var i=begPos; i < endPos; i++ )
+      results.push(this.rows[i]);
+    return results
+  }
+
+};
+
+
+// Rico.LiveGrid -----------------------------------------------------
+
+Rico.LiveGrid = Class.create();
+
+Rico.LiveGrid.prototype = {
+
+  initialize: function( tableId, buffer, options ) {
+    Object.extend(this, new Rico.GridCommon);
+    Object.extend(this, new Rico.LiveGridMethods);
+    this.baseInit();
+    this.tableId = tableId;
+    this.buffer = buffer;
+    Rico.setDebugArea(tableId+"_debugmsgs");    // if used, this should be a textarea
+
+    Object.extend(this.options, {
+      visibleRows      : -1,    // -1 or 'window'=size grid to client window; -2 or 'data'=size grid to min(window,data); -3 or 'body'=size so body does not have a scrollbar
+      frozenColumns    : 0,
+      offset           : 0,     // first row to be displayed
+      prefetchBuffer   : true,  // load table on page load?
+      minPageRows      : 1,
+      maxPageRows      : 50,
+      canSortDefault   : true,  // can be overridden in the column specs
+      canFilterDefault : buffer.options.canFilter, // can be overridden in the column specs
+      canHideDefault   : true,  // can be overridden in the column specs
+      cookiePrefix     : 'liveGrid.'+tableId,
+
+      // highlight & selection parameters
+      highlightElem    : 'none',// what gets highlighted/selected (cursorRow, cursorCell, menuRow, menuCell, selection, or none)
+      highlightSection : 3,     // which section gets highlighted (frozen=1, scrolling=2, all=3, none=0)
+      highlightMethod  : 'class', // outline, class, both (outline is less CPU intensive on the client)
+      highlightClass   : 'ricoLG_selection',
+
+      // export/print parameters
+      maxPrint         : 1000,  // max # of rows that can be printed/exported, 0=disable print/export feature
+      exportWindow     : "height=300,width=500,scrollbars=1,menubar=1,resizable=1",
+
+      // heading parameters
+      headingSort      : 'link', // link: make headings a link that will sort column, hover: make headings a hoverset, none: events on headings are disabled
+      hdrIconsFirst    : true,   // true: put sort & filter icons before header text, false: after
+      sortAscendImg    : 'sort_asc.gif',
+      sortDescendImg   : 'sort_desc.gif',
+      filterImg        : 'filtercol.gif'
+    });
+    // other options:
+    //   sortCol: initial sort column
+
+    this.options.sortHandler = this.sortHandler.bind(this);
+    this.options.filterHandler = this.filterHandler.bind(this);
+    this.options.onRefreshComplete = this.bookmarkHandler.bind(this);
+    this.options.rowOverHandler = this.rowMouseOver.bindAsEventListener(this);
+    this.options.mouseDownHandler = this.selectMouseDown.bindAsEventListener(this);
+    this.options.mouseOverHandler = this.selectMouseOver.bindAsEventListener(this);
+    this.options.mouseUpHandler  = this.selectMouseUp.bindAsEventListener(this);
+    Object.extend(this.options, options || {});
+
+    switch (typeof this.options.visibleRows) {
+      case 'string':
+        this.sizeTo=this.options.visibleRows;
+        this.options.visibleRows=-1;
+        break;
+      case 'number':
+        switch (this.options.visibleRows) {
+          case -1: this.sizeTo='window'; break;
+          case -2: this.sizeTo='data'; break;
+          case -3: this.sizeTo='body'; break;
+        }
+        break;
+      default:
+        this.sizeTo='window';
+        this.options.visibleRows=-1;
+    }
+    this.highlightEnabled=this.options.highlightSection>0;
+    this.pageSize=0;
+    this.createTables();
+    if (this.headerColCnt==0) {
+      alert('ERROR: no columns found in "'+this.tableId+'"');
+      return;
+    }
+    this.createColumnArray();
+       if (this.options.headingSort=='hover')
+         this.createHoverSet();
+
+    this.bookmark=$(this.tableId+"_bookmark");
+    this.sizeDivs();
+    this.createDataCells(this.options.visibleRows);
+    if (this.pageSize == 0) return;
+    this.buffer.registerGrid(this);
+    if (this.buffer.setBufferSize) this.buffer.setBufferSize(this.pageSize);
+    this.scrollTimeout = null;
+    this.lastScrollPos = 0;
+    this.attachMenuEvents();
+
+    // preload the images...
+    new Image().src = Rico.imgDir+this.options.filterImg;
+    new Image().src = Rico.imgDir+this.options.sortAscendImg;
+    new Image().src = Rico.imgDir+this.options.sortDescendImg;
+    Rico.writeDebugMsg("images preloaded");
+
+    this.setSortUI( this.options.sortCol, this.options.sortDir );
+    this.setImages();
+    if (this.listInvisible().length==this.columns.length)
+      this.columns[0].showColumn();
+    this.sizeDivs();
+    this.scrollDiv.style.display="";
+    if (this.buffer.totalRows>0)
+      this.updateHeightDiv();
+    if (this.options.prefetchBuffer) {
+      if (this.bookmark) this.bookmark.innerHTML = RicoTranslate.getPhrase("Loading...");
+      if (this.options.canFilterDefault && this.options.getQueryParms)
+        this.checkForFilterParms();
+      this.buffer.fetch(this.options.offset);
+    }
+    this.scrollEventFunc=this.handleScroll.bindAsEventListener(this);
+    this.wheelEventFunc=this.handleWheel.bindAsEventListener(this);
+    this.wheelEvent=(Prototype.Browser.IE || Prototype.Browser.Opera || Prototype.Browser.WebKit) ? 'mousewheel' : 'DOMMouseScroll';
+    if (this.options.offset && this.options.offset < this.buffer.totalRows)
+      setTimeout(this.scrollToRow.bind(this,this.options.offset),50);  // Safari requires a delay
+    this.pluginScroll();
+    this.setHorizontalScroll();
+    if (this.options.windowResize)
+      setTimeout(this.pluginWindowResize.bind(this),100);
+  }
+};
+
+Rico.LiveGridMethods = function() {};
+
+Rico.LiveGridMethods.prototype = {
+
+  createHoverSet: function() {
+    var hdrs=[];
+    for( var c=0; c < this.headerColCnt; c++ )
+      hdrs.push(this.columns[c].hdrCellDiv);
+         this.hoverSet = new Rico.HoverSet(hdrs);
+  },
+
+  checkForFilterParms: function() {
+    var s=window.location.search;
+    if (s.charAt(0)=='?') s=s.substring(1);
+    var pairs = s.split('&');
+    for (var i=0; i<pairs.length; i++)
+      if (pairs[i].match(/^f\[\d+\]/)) {
+        this.buffer.options.requestParameters.push(pairs[i]);
+      }
+  },
+
+/**
+ * Create one table for frozen columns and one for scrolling columns.
+ * Also create div's to contain them.
+ */
+  createTables: function() {
+    var insertloc;
+    var result = -1;
+    var table = $(this.tableId);
+    if (!table) return result;
+    if (table.tagName.toLowerCase()=='table') {
+      var theads=table.getElementsByTagName("thead");
+      if (theads.length == 1) {
+        Rico.writeDebugMsg("createTables: using thead section, id="+this.tableId);
+        var hdrSrc=theads[0].rows;
+      } else {
+        Rico.writeDebugMsg("createTables: using tbody section, id="+this.tableId);
+        var hdrSrc=new Array(table.rows[0]);
+      }
+      insertloc=table;
+    } else if (this.options.columnSpecs.length > 0) {
+      insertloc=table;
+      Rico.writeDebugMsg("createTables: inserting at "+table.tagName+", id="+this.tableId);
+    } else {
+      alert("ERROR!\n\nUnable to initialize '"+this.tableId+"'\n\nLiveGrid terminated");
+      return result;
+    }
+
+    this.createDivs();
+    this.scrollTabs = this.createDiv("scrollTabs",this.innerDiv);
+    this.shadowDiv  = this.createDiv("shadow",this.scrollDiv);
+    this.shadowDiv.style.direction='ltr';  // avoid FF bug
+    this.messageDiv = this.createDiv("message",this.outerDiv);
+    this.messageDiv.style.display="none";
+    this.messageShadow=new Rico.Shadow(this.messageDiv);
+    this.scrollDiv.style.display="none";
+    this.scrollDiv.scrollTop=0;
+    if (this.options.highlightMethod!='class') {
+      this.highlightDiv=[];
+      switch (this.options.highlightElem) {
+        case 'menuRow':
+        case 'cursorRow':
+          this.highlightDiv[0] = this.createDiv("highlight",this.outerDiv);
+          this.highlightDiv[0].style.display="none";
+          break;
+        case 'menuCell':
+        case 'cursorCell':
+          for (var i=0; i<2; i++) {
+            this.highlightDiv[i] = this.createDiv("highlight",i==0 ? this.frozenTabs : this.scrollTabs);
+            this.highlightDiv[i].style.display="none";
+            this.highlightDiv[i].id+=i;
+          }
+          break;
+        case 'selection':
+          // create one div for each side of the rectangle
+          var parentDiv=this.options.highlightSection==1 ? this.frozenTabs : this.scrollTabs;
+          for (var i=0; i<4; i++) {
+            this.highlightDiv[i] = this.createDiv("highlight",parentDiv);
+            this.highlightDiv[i].style.display="none";
+            this.highlightDiv[i].id+=i;
+            this.highlightDiv[i].style[i % 2==0 ? 'height' : 'width']="0px";
+          }
+          break;
+      }
+    }
+
+    // create new tables
+    for (var i=0; i<2; i++) {
+      this.tabs[i] = document.createElement("table");
+      this.tabs[i].className = 'ricoLG_table';
+      this.tabs[i].border=0;
+      this.tabs[i].cellPadding=0;
+      this.tabs[i].cellSpacing=0;
+      this.tabs[i].id = this.tableId+"_tab"+i;
+      this.thead[i]=this.tabs[i].createTHead();
+      this.thead[i].className='ricoLG_top';
+      if (this.tabs[i].tBodies.length==0)
+        this.tbody[i]=this.tabs[i].appendChild(document.createElement("tbody"));
+      else
+        this.tbody[i]=this.tabs[i].tBodies[0];
+      this.tbody[i].className='ricoLG_bottom';
+      this.tbody[i].insertRow(-1);
+    }
+    this.frozenTabs.appendChild(this.tabs[0]);
+    this.scrollTabs.appendChild(this.tabs[1]);
+    insertloc.parentNode.insertBefore(this.outerDiv,insertloc);
+    if (hdrSrc)
+      this.loadHdrSrc(hdrSrc);
+    else
+      this.createHdr();
+    for( var c=0; c < this.headerColCnt; c++ )
+      this.tbody[c<this.options.frozenColumns ? 0 : 1].rows[0].insertCell(-1);
+    if (table) table.parentNode.removeChild(table);
+    Rico.writeDebugMsg('createTables end');
+  },
+
+  createDataCells: function(visibleRows) {
+    if (visibleRows < 0) {
+      this.appendBlankRow();
+      this.sizeDivs();
+      this.autoAppendRows(this.remainingHt());
+    } else {
+      for( var r=0; r < visibleRows; r++ )
+        this.appendBlankRow();
+    }
+    var s=this.options.highlightSection;
+    if (s & 1) this.attachHighlightEvents(this.tbody[0]);
+    if (s & 2) this.attachHighlightEvents(this.tbody[1]);
+    return;
+  },
+
+  createHdr: function() {
+    for (var i=0; i<2; i++) {
+      var start=(i==0) ? 0 : this.options.frozenColumns;
+      var limit=(i==0) ? this.options.frozenColumns : this.options.columnSpecs.length;
+      Rico.writeDebugMsg('createHdr: i='+i+' start='+start+' limit='+limit);
+      if (this.options.PanelNamesOnTabHdr && this.options.panels) {
+        // place panel names on first row of thead
+        var r = this.thead[i].insertRow(-1);
+        r.className='ricoLG_hdg';
+        var lastIdx=-1, span, newCell=null, spanIdx=0;
+        for( var c=start; c < limit; c++ ) {
+          if (lastIdx == this.options.columnSpecs[c].panelIdx) {
+            span++;
+          } else {
+            if (newCell) newCell.colSpan=span;
+            newCell = r.insertCell(-1);
+            span=1;
+            lastIdx=this.options.columnSpecs[c].panelIdx;
+            newCell.innerHTML=this.options.panels[lastIdx];
+          }
+        }
+        if (newCell) newCell.colSpan=span;
+      }
+      var mainRow = this.thead[i].insertRow(-1);
+      mainRow.id=this.tableId+'_tab'+i+'h_main';
+      mainRow.className='ricoLG_hdg';
+      for( var c=start; c < limit; c++ ) {
+        var newCell = mainRow.insertCell(-1);
+        newCell.innerHTML=this.options.columnSpecs[c].Hdg;
+      }
+      this.headerColCnt = this.getColumnInfo(this.thead[i].rows);
+    }
+  },
+
+  loadHdrSrc: function(hdrSrc) {
+    Rico.writeDebugMsg('loadHdrSrc start');
+    this.headerColCnt = this.getColumnInfo(hdrSrc);
+    for (var i=0; i<2; i++) {
+      for (var r=0; r<hdrSrc.length; r++) {
+        var newrow = this.thead[i].insertRow(-1);
+        newrow.className='ricoLG_hdg';
+      }
+    }
+    if (hdrSrc.length==1) {
+      var cells=hdrSrc[0].cells;
+      for (var c=0; cells.length > 0; c++)
+        this.thead[c<this.options.frozenColumns ? 0 : 1].rows[0].appendChild(cells[0]);
+    } else {
+      for (var r=0; r<hdrSrc.length; r++) {
+        var cells=hdrSrc[r].cells;
+        for (var c=0; cells.length > 0; c++) {
+          if (cells[0].className=='ricoFrozen') {
+            this.thead[0].rows[r].appendChild(cells[0]);
+            if (r==this.headerRowIdx) this.options.frozenColumns=c+1;
+          } else {
+            this.thead[1].rows[r].appendChild(cells[0]);
+          }
+        }
+      }
+    }
+    Rico.writeDebugMsg('loadHdrSrc end');
+  },
+
+  sizeDivs: function() {
+    Rico.writeDebugMsg('sizeDivs: '+this.tableId);
+    this.cancelMenu();
+    this.unhighlight();
+    this.baseSizeDivs();
+    if (this.pageSize == 0) return;
+    this.rowHeight = Math.round(this.dataHt/this.pageSize);
+    var scrHt=this.dataHt;
+    if (this.scrWi>0 || Prototype.Browser.IE || Prototype.Browser.WebKit)
+      scrHt+=this.options.scrollBarWidth;
+    this.scrollDiv.style.height=scrHt+'px';
+    this.innerDiv.style.width=(this.scrWi-this.options.scrollBarWidth+1)+'px';
+    this.resizeDiv.style.height=this.frozenTabs.style.height=this.innerDiv.style.height=(this.hdrHt+this.dataHt+1)+'px';
+    Rico.writeDebugMsg('sizeDivs scrHt='+scrHt+' innerHt='+this.innerDiv.style.height+' rowHt='+this.rowHeight+' pageSize='+this.pageSize);
+    pad=(this.scrWi-this.scrTabWi < this.options.scrollBarWidth) ? 2 : 0;
+    this.shadowDiv.style.width=(this.scrTabWi+pad)+'px';
+    this.outerDiv.style.height=(this.hdrHt+scrHt)+'px';
+    this.setHorizontalScroll();
+  },
+
+  setHorizontalScroll: function() {
+    var scrleft=this.scrollDiv.scrollLeft;
+    this.scrollTabs.style.left=(-scrleft)+'px';
+  },
+
+  remainingHt: function() {
+    var winHt=RicoUtil.windowHeight();
+    var margin=Prototype.Browser.IE ? 15 : 10;
+    switch (this.sizeTo) {
+      case 'window':
+      case 'data':
+        var divPos=Position.page(this.outerDiv);
+        var tabHt=Math.max(this.tabs[0].offsetHeight,this.tabs[1].offsetHeight);
+        Rico.writeDebugMsg("remainingHt, winHt="+winHt+' tabHt='+tabHt+' gridY='+divPos[1]);
+        return winHt-divPos[1]-tabHt-this.options.scrollBarWidth-margin;  // allow for scrollbar and some margin
+      case 'body':
+        //Rico.writeDebugMsg("remainingHt, document.height="+document.height);
+        //Rico.writeDebugMsg("remainingHt, body.offsetHeight="+document.body.offsetHeight);
+        //Rico.writeDebugMsg("remainingHt, body.scrollHeight="+document.body.scrollHeight);
+        //Rico.writeDebugMsg("remainingHt, documentElement.scrollHeight="+document.documentElement.scrollHeight);
+        var bodyHt=Prototype.Browser.IE ? document.body.scrollHeight : document.body.offsetHeight;
+        var remHt=winHt-bodyHt-margin;
+        if (!Prototype.Browser.WebKit) remHt-=this.options.scrollBarWidth;
+        Rico.writeDebugMsg("remainingHt, winHt="+winHt+' pageHt='+bodyHt+' remHt='+remHt);
+        return remHt;
+    }
+  },
+
+  adjustPageSize: function() {
+    var remHt=this.remainingHt();
+    Rico.writeDebugMsg('adjustPageSize remHt='+remHt+' lastRow='+this.lastRowPos);
+    if (remHt > this.rowHeight)
+      this.autoAppendRows(remHt);
+    else if (remHt < 0 || this.sizeTo=='data')
+      this.autoRemoveRows(-remHt);
+  },
+
+  pluginWindowResize: function() {
+    Event.observe(window, "resize", this.resizeWindow.bindAsEventListener(this), false);
+  },
+
+  resizeWindow: function() {
+    Rico.writeDebugMsg('resizeWindow '+this.tableId+' lastRow='+this.lastRowPos);
+    if (!this.sizeTo) {
+      this.sizeDivs();
+      return;
+    }
+    var oldSize=this.pageSize;
+    this.adjustPageSize();
+    if (this.pageSize > oldSize) {
+      this.isPartialBlank=true;
+      var adjStart=this.adjustRow(this.lastRowPos);
+      this.buffer.fetch(adjStart);
+    }
+    if (oldSize != this.pageSize)
+      setTimeout(this.finishResize.bind(this),50);
+    else
+      this.sizeDivs();
+    Rico.writeDebugMsg('resizeWindow complete. old size='+oldSize+' new size='+this.pageSize);
+  },
+
+  finishResize: function() {
+    this.sizeDivs();
+    this.updateHeightDiv();
+  },
+
+  topOfLastPage: function() {
+    return Math.max(this.buffer.totalRows-this.pageSize,0);
+  },
+
+  updateHeightDiv: function() {
+    var notdisp=this.topOfLastPage();
+    var ht = this.scrollDiv.clientHeight + this.rowHeight * notdisp;
+    //if (Prototype.Browser.Opera) ht+=this.options.scrollBarWidth-3;
+    Rico.writeDebugMsg("updateHeightDiv, ht="+ht+' scrollDiv.clientHeight='+this.scrollDiv.clientHeight+' rowsNotDisplayed='+notdisp);
+    this.shadowDiv.style.height=ht+'px';
+  },
+
+  autoRemoveRows: function(overage) {
+    var removeCnt=Math.ceil(overage / this.rowHeight);
+    if (this.sizeTo=='data')
+      removeCnt=Math.max(removeCnt,this.pageSize-this.buffer.totalRows);
+    Rico.writeDebugMsg("autoRemoveRows overage="+overage+" removeCnt="+removeCnt);
+    for (var i=0; i<removeCnt; i++)
+      this.removeRow();
+  },
+
+  removeRow: function() {
+    if (this.pageSize <= this.options.minPageRows) return;
+    this.pageSize--;
+    for( var c=0; c < this.headerColCnt; c++ ) {
+      var cell=this.columns[c].cell(this.pageSize);
+      this.columns[c].dataColDiv.removeChild(cell);
+    }
+  },
+
+  autoAppendRows: function(overage) {
+    var addCnt=Math.floor(overage / this.rowHeight);
+    Rico.writeDebugMsg("autoAppendRows overage="+overage+" cnt="+addCnt+" rowHt="+this.rowHeight);
+    for (var i=0; i<addCnt; i++) {
+      if (this.sizeTo=='data' && this.pageSize>=this.buffer.totalRows) break;
+      this.appendBlankRow();
+    }
+  },
+
+  // on older systems, this can be fairly slow
+  appendBlankRow: function() {
+    if (this.pageSize >= this.options.maxPageRows) return;
+    Rico.writeDebugMsg("appendBlankRow #"+this.pageSize);
+    var cls=this.defaultRowClass(this.pageSize);
+    for( var c=0; c < this.headerColCnt; c++ ) {
+      var newdiv = document.createElement("div");
+      newdiv.className = 'ricoLG_cell '+cls;
+      newdiv.id=this.tableId+'_'+this.pageSize+'_'+c;
+      this.columns[c].dataColDiv.appendChild(newdiv);
+      newdiv.innerHTML='&nbsp;';
+      if (this.columns[c]._create)
+        this.columns[c]._create(newdiv,this.pageSize);
+    }
+    this.pageSize++;
+  },
+
+  defaultRowClass: function(rownum) {
+    return (rownum % 2==0) ? 'ricoLG_evenRow' : 'ricoLG_oddRow';
+  },
+
+  handleMenuClick: function(e) {
+    //Event.stop(e);
+    if (!this.menu) return;
+    this.cancelMenu();
+    this.unhighlight(); // in case highlighting was invoked externally
+    var cell=Event.element(e);
+    if (cell.className=='ricoLG_highlightDiv') {
+      var idx=this.highlightIdx;
+    } else {
+      cell=RicoUtil.getParentByTagName(cell,'div','ricoLG_cell');
+      if (!cell) return;
+      var idx=this.winCellIndex(cell);
+      if ((this.options.highlightSection & (idx.tabIdx+1))==0) return;
+    }
+    this.highlight(idx);
+    this.highlightEnabled=false;
+    if (this.hideScroll) this.scrollDiv.style.overflow="hidden";
+    this.menuIdx=idx;
+    if (!this.menu.div) this.menu.createDiv();
+    this.menu.liveGrid=this;
+    if (this.menu.buildGridMenu) {
+      var showMenu=this.menu.buildGridMenu(idx.row, idx.column, idx.tabIdx);
+      if (!showMenu) return;
+    }
+    if (this.options.highlightElem=='selection' && !this.isSelected(idx.cell))
+      this.selectCell(idx.cell);
+    this.menu.showmenu(e,this.closeMenu.bind(this));
+  },
+
+  closeMenu: function() {
+    if (!this.menuIdx) return;
+    if (this.hideScroll) this.scrollDiv.style.overflow="";
+    this.unhighlight();
+    this.highlightEnabled=true;
+    this.menuIdx=null;
+  },
+
+/**
+ * @return index of cell within the window
+ */
+  winCellIndex: function(cell) {
+    var a=cell.id.split(/_/);
+    var l=a.length;
+    var r=parseInt(a[l-2]);
+    var c=parseInt(a[l-1]);
+    return {row:r, column:c, tabIdx:this.columns[c].tabIdx, cell:cell};
+  },
+
+/**
+ * @return index of cell within the buffer
+ */
+  bufCellIndex: function(cell) {
+    var idx=this.winCellIndex(cell);
+    idx.row+=this.buffer.windowPos;
+    if (idx.row >= this.buffer.size) idx.onBlankRow=true;
+    return idx;
+  },
+
+  attachHighlightEvents: function(tBody) {
+    switch (this.options.highlightElem) {
+      case 'selection':
+        Event.observe(tBody,"mousedown", this.options.mouseDownHandler, false);
+        tBody.ondrag = function () { return false; };
+        tBody.onselectstart = function () { return false; };
+        break;
+      case 'cursorRow':
+      case 'cursorCell':
+        Event.observe(tBody,"mouseover", this.options.rowOverHandler, false);
+        break;
+    }
+  },
+
+  getVisibleSelection: function() {
+    var cellList=[];
+    if (this.SelectIdxStart && this.SelectIdxEnd) {
+      var r1=Math.max(Math.min(this.SelectIdxEnd.row,this.SelectIdxStart.row),this.buffer.windowPos);
+      var r2=Math.min(Math.max(this.SelectIdxEnd.row,this.SelectIdxStart.row),this.buffer.windowEnd-1);
+      var c1=Math.min(this.SelectIdxEnd.column,this.SelectIdxStart.column);
+      var c2=Math.max(this.SelectIdxEnd.column,this.SelectIdxStart.column);
+      for (var r=r1; r<=r2; r++)
+        for (var c=c1; c<=c2; c++)
+          cellList.push({row:r-this.buffer.windowPos,column:c});
+    }
+    if (this.SelectCtrl) {
+      for (var i=0; i<this.SelectCtrl.length; i++) {
+        if (this.SelectCtrl[i].row>=this.buffer.windowPos && this.SelectCtrl[i].row<this.buffer.windowEnd)
+          cellList.push({row:this.SelectCtrl[i].row-this.buffer.windowPos,column:this.SelectCtrl[i].column});
+      }
+    }
+    return cellList;
+  },
+
+  updateSelectOutline: function() {
+    if (!this.SelectIdxStart || !this.SelectIdxEnd) return;
+    var r1=Math.max(Math.min(this.SelectIdxEnd.row,this.SelectIdxStart.row), this.buffer.windowStart);
+    var r2=Math.min(Math.max(this.SelectIdxEnd.row,this.SelectIdxStart.row), this.buffer.windowEnd-1);
+    if (r1 > r2) {
+      this.HideSelection();
+      return;
+    }
+    var c1=Math.min(this.SelectIdxEnd.column,this.SelectIdxStart.column);
+    var c2=Math.max(this.SelectIdxEnd.column,this.SelectIdxStart.column);
+    var top1=this.columns[c1].cell(r1-this.buffer.windowStart).offsetTop;
+    var cell2=this.columns[c1].cell(r2-this.buffer.windowStart);
+    var bottom2=cell2.offsetTop+cell2.offsetHeight;
+    var left1=this.columns[c1].dataCell.offsetLeft;
+    var left2=this.columns[c2].dataCell.offsetLeft;
+    var right2=left2+this.columns[c2].dataCell.offsetWidth;
+    //window.status='updateSelectOutline: '+r1+' '+r2+' top='+top1+' bot='+bottom2;
+    this.highlightDiv[0].style.top=this.highlightDiv[3].style.top=this.highlightDiv[1].style.top=(this.hdrHt+top1-1) + 'px';
+    this.highlightDiv[2].style.top=(this.hdrHt+bottom2-1)+'px';
+    this.highlightDiv[3].style.left=(left1-2)+'px';
+    this.highlightDiv[0].style.left=this.highlightDiv[2].style.left=(left1-1)+'px';
+    this.highlightDiv[1].style.left=(right2-1)+'px';
+    this.highlightDiv[0].style.width=this.highlightDiv[2].style.width=(right2-left1-1) + 'px';
+    this.highlightDiv[1].style.height=this.highlightDiv[3].style.height=(bottom2-top1) + 'px';
+    //this.highlightDiv[0].style.right=this.highlightDiv[2].style.right=this.highlightDiv[1].style.right=()+'px';
+    //this.highlightDiv[2].style.bottom=this.highlightDiv[3].style.bottom=this.highlightDiv[1].style.bottom=(this.hdrHt+bottom2) + 'px';
+    for (var i=0; i<4; i++)
+      this.highlightDiv[i].style.display='';
+  },
+
+  HideSelection: function(cellList) {
+    if (this.options.highlightMethod!='class') {
+      for (var i=0; i<4; i++)
+        this.highlightDiv[i].style.display='none';
+    }
+    if (this.options.highlightMethod!='outline') {
+      var cellList=this.getVisibleSelection();
+      for (var i=0; i<cellList.length; i++)
+        this.unhighlightCell(this.columns[cellList[i].column].cell(cellList[i].row));
+    }
+  },
+
+  ShowSelection: function() {
+    if (this.options.highlightMethod!='class')
+      this.updateSelectOutline();
+    if (this.options.highlightMethod!='outline') {
+      var cellList=this.getVisibleSelection();
+      for (var i=0; i<cellList.length; i++)
+        this.highlightCell(this.columns[cellList[i].column].cell(cellList[i].row));
+    }
+  },
+
+  ClearSelection: function() {
+    this.HideSelection();
+    this.SelectIdxStart=null;
+    this.SelectIdxEnd=null;
+    this.SelectCtrl=[];
+  },
+
+  selectCell: function(cell) {
+    this.ClearSelection();
+    this.SelectIdxStart=this.SelectIdxEnd=this.bufCellIndex(cell);
+    this.ShowSelection();
+  },
+
+  AdjustSelection: function(cell) {
+    var newIdx=this.bufCellIndex(cell);
+    if (this.SelectIdxStart.tabIdx != newIdx.tabIdx) return;
+    this.HideSelection();
+    this.SelectIdxEnd=newIdx;
+    this.ShowSelection();
+  },
+
+  RefreshSelection: function() {
+    var cellList=this.getVisibleSelection();
+    for (var i=0; i<cellList.length; i++)
+      this.columns[cellList[i].column].displayValue(cellList[i].row);
+  },
+
+  FillSelection: function(newVal,newStyle) {
+    if (this.SelectIdxStart && this.SelectIdxEnd) {
+      var r1=Math.min(this.SelectIdxEnd.row,this.SelectIdxStart.row);
+      var r2=Math.max(this.SelectIdxEnd.row,this.SelectIdxStart.row);
+      var c1=Math.min(this.SelectIdxEnd.column,this.SelectIdxStart.column);
+      var c2=Math.max(this.SelectIdxEnd.column,this.SelectIdxStart.column);
+      for (var r=r1; r<=r2; r++)
+        for (var c=c1; c<=c2; c++)
+          this.buffer.setValue(r,c,newVal,newStyle);
+    }
+    if (this.SelectCtrl) {
+      for (var i=0; i<this.SelectCtrl.length; i++)
+        this.buffer.setValue(this.SelectCtrl[i].row,this.SelectCtrl[i].column,newVal,newStyle);
+    }
+    this.RefreshSelection();
+  },
+
+  selectMouseDown: function(e) {
+    if (this.highlightEnabled==false) return true;
+    this.cancelMenu();
+    var cell=Event.element(e);
+    Event.stop(e);
+    if (!Event.isLeftClick(e)) return;
+    cell=RicoUtil.getParentByTagName(cell,'div','ricoLG_cell');
+    if (!cell) return;
+    var newIdx=this.bufCellIndex(cell);
+    if (newIdx.onBlankRow) return;
+    if (e.ctrlKey) {
+      if (!this.SelectIdxStart || this.options.highlightMethod!='class') return;
+      if (!this.isSelected(cell)) {
+        this.highlightCell(cell);
+        this.SelectCtrl.push(this.bufCellIndex(cell));
+      } else {
+        for (var i=0; i<this.SelectCtrl.length; i++) {
+          if (this.SelectCtrl[i].row==newIdx.row && this.SelectCtrl[i].column==newIdx.column) {
+            this.unhighlightCell(cell);
+            this.SelectCtrl.splice(i,1);
+            break;
+          }
+        }
+      }
+    } else if (e.shiftKey) {
+      if (!this.SelectIdxStart) return;
+      this.AdjustSelection(cell);
+    } else {
+      this.selectCell(cell);
+      this.pluginSelect();
+    }
+  },
+
+  pluginSelect: function() {
+    if (this.selectPluggedIn) return;
+    var tBody=this.tbody[this.SelectIdxStart.tabIdx];
+    Event.observe(tBody,"mouseover", this.options.mouseOverHandler, false);
+    Event.observe(this.outerDiv,"mouseup",  this.options.mouseUpHandler,  false);
+    if (this.options.highlightMethod!='class')
+    this.selectPluggedIn=true;
+  },
+
+  unplugSelect: function() {
+    var tBody=this.tbody[this.SelectIdxStart.tabIdx];
+    Event.stopObserving(tBody,"mouseover", this.options.mouseOverHandler , false);
+    Event.stopObserving(this.outerDiv,"mouseup", this.options.mouseUpHandler , false);
+    this.selectPluggedIn=false;
+  },
+
+  selectMouseUp: function(e) {
+    this.unplugSelect();
+    var cell=Event.element(e);
+    cell=RicoUtil.getParentByTagName(cell,'div','ricoLG_cell');
+    if (!cell) return;
+    if (this.SelectIdxStart && this.SelectIdxEnd)
+      this.AdjustSelection(cell);
+    else
+      this.ClearSelection();
+  },
+
+  selectMouseOver: function(e) {
+    var cell=Event.element(e);
+    cell=RicoUtil.getParentByTagName(cell,'div','ricoLG_cell');
+    if (!cell) return;
+    this.AdjustSelection(cell);
+    Event.stop(e);
+  },
+
+  isSelected: function(cell) {
+    return Element.hasClassName(cell,this.options.highlightClass);
+  },
+
+  highlightCell: function(cell) {
+    Element.addClassName(cell,this.options.highlightClass);
+  },
+
+  unhighlightCell: function(cell) {
+    if (cell==null) return;
+    Element.removeClassName(cell,this.options.highlightClass);
+  },
+
+  selectRow: function(r) {
+    for (var c=0; c<this.columns.length; c++)
+      this.highlightCell(this.columns[c].cell(r));
+  },
+
+  unselectRow: function(r) {
+    for (var c=0; c<this.columns.length; c++)
+      this.unhighlightCell(this.columns[c].cell(r));
+  },
+
+  rowMouseOver: function(e) {
+    if (!this.highlightEnabled) return;
+    var cell=Event.element(e);
+    cell=RicoUtil.getParentByTagName(cell,'div','ricoLG_cell');
+    if (!cell) return;
+    var newIdx=this.winCellIndex(cell);
+    if ((this.options.highlightSection & (newIdx.tabIdx+1))==0) return;
+    this.highlight(newIdx);
+  },
+
+  highlight: function(newIdx) {
+    if (this.options.highlightMethod!='outline') this.cursorSetClass(newIdx);
+    if (this.options.highlightMethod!='class') this.cursorOutline(newIdx);
+    this.highlightIdx=newIdx;
+  },
+
+  cursorSetClass: function(newIdx) {
+    switch (this.options.highlightElem) {
+      case 'menuCell':
+      case 'cursorCell':
+        if (this.highlightIdx) this.unhighlightCell(this.highlightIdx.cell);
+        this.highlightCell(newIdx.cell);
+        break;
+      case 'menuRow':
+      case 'cursorRow':
+        if (this.highlightIdx) this.unselectRow(this.highlightIdx.row);
+        var s1=this.options.highlightSection & 1;
+        var s2=this.options.highlightSection & 2;
+        var c0=s1 ? 0 : this.options.frozenColumns;
+        var c1=s2 ? this.columns.length : this.options.frozenColumns;
+        for (var c=c0; c<c1; c++)
+          this.highlightCell(this.columns[c].cell(newIdx.row));
+        break;
+      default: return;
+    }
+  },
+
+  cursorOutline: function(newIdx) {
+    switch (this.options.highlightElem) {
+      case 'menuCell':
+      case 'cursorCell':
+        var div=this.highlightDiv[newIdx.tabIdx];
+        div.style.left=(this.columns[newIdx.column].dataCell.offsetLeft-1)+'px';
+        div.style.width=this.columns[newIdx.column].colWidth;
+        this.highlightDiv[1-newIdx.tabIdx].style.display='none';
+        break;
+      case 'menuRow':
+      case 'cursorRow':
+        var div=this.highlightDiv[0];
+        var s1=this.options.highlightSection & 1;
+        var s2=this.options.highlightSection & 2;
+        div.style.left=s1 ? '0px' : this.frozenTabs.style.width;
+        div.style.width=((s1 ? this.frozenTabs.offsetWidth : 0) + (s2 ? this.innerDiv.offsetWidth : 0) - 4)+'px';
+        break;
+      default: return;
+    }
+    div.style.top=(this.hdrHt+newIdx.row*this.rowHeight-1)+'px';
+    div.style.height=(this.rowHeight-1)+'px';
+    div.style.display='';
+  },
+
+  unhighlight: function() {
+    switch (this.options.highlightElem) {
+      case 'menuCell':
+        this.highlightIdx=this.menuIdx;
+      case 'cursorCell':
+        if (this.highlightIdx) this.unhighlightCell(this.highlightIdx.cell);
+        if (!this.highlightDiv) return;
+        for (var i=0; i<2; i++)
+          this.highlightDiv[i].style.display='none';
+        break;
+      case 'menuRow':
+        this.highlightIdx=this.menuIdx;
+      case 'cursorRow':
+        if (this.highlightIdx) this.unselectRow(this.highlightIdx.row);
+        if (this.highlightDiv) this.highlightDiv[0].style.display='none';
+        break;
+    }
+  },
+
+  hideMsg: function() {
+    if (this.messageDiv.style.display=="none") return;
+    this.messageDiv.style.display="none";
+    this.messageShadow.hide();
+  },
+
+  showMsg: function(msg) {
+    this.messageDiv.innerHTML=RicoTranslate.getPhrase(msg);
+    this.messageDiv.style.display="";
+    var msgWidth=this.messageDiv.offsetWidth;
+    var msgHeight=this.messageDiv.offsetHeight;
+    var divwi=this.outerDiv.offsetWidth;
+    var divht=this.outerDiv.offsetHeight;
+    this.messageDiv.style.top=parseInt((divht-msgHeight)/2)+'px';
+    this.messageDiv.style.left=parseInt((divwi-msgWidth)/2)+'px';
+    this.messageShadow.show();
+    Rico.writeDebugMsg("showMsg: "+msg);
+  },
+
+  resetContents: function(resetHt) {
+    Rico.writeDebugMsg("resetContents("+resetHt+")");
+    this.buffer.clear();
+    this.clearRows();
+    if (typeof resetHt=='undefined' || resetHt==true) {
+      this.buffer.setTotalRows(0);
+    } else {
+      this.scrollToRow(0);
+    }
+    if (this.bookmark) this.bookmark.innerHTML="&nbsp;";
+  },
+
+  setImages: function() {
+    for (n=0; n<this.columns.length; n++)
+      this.columns[n].setImage();
+  },
+
+  // returns column index, or -1 if there are no sorted columns
+  findSortedColumn: function() {
+    for (var n=0; n<this.columns.length; n++)
+      if (this.columns[n].isSorted()) return n;
+    return -1;
+  },
+
+  findColumnName: function(name) {
+    for (var n=0; n<this.columns.length; n++)
+      if (this.columns[n].fieldName == name) return n;
+    return -1;
+  },
+
+/**
+ * Set initial sort
+ */
+  setSortUI: function( columnNameOrNum, sortDirection ) {
+    Rico.writeDebugMsg("setSortUI: "+columnNameOrNum+' '+sortDirection);
+    var colnum=this.findSortedColumn();
+    if (colnum >= 0) {
+      sortDirection=this.columns[colnum].getSortDirection();
+    } else {
+      if (typeof sortDirection!='string') {
+        sortDirection=Rico.TableColumn.SORT_ASC;
+      } else {
+        sortDirection=sortDirection.toUpperCase();
+        if (sortDirection != Rico.TableColumn.SORT_DESC) sortDirection=Rico.TableColumn.SORT_ASC;
+      }
+      switch (typeof columnNameOrNum) {
+        case 'string':
+          colnum=this.findColumnName(columnNameOrNum);
+          break;
+        case 'number':
+          colnum=columnNameOrNum;
+          break;
+      }
+    }
+    if (typeof(colnum)!='number' || colnum < 0) return;
+    this.clearSort();
+    this.columns[colnum].setSorted(sortDirection);
+    this.buffer.sortBuffer(colnum,sortDirection,this.columns[colnum].format.type,this.columns[colnum]._sortfunc);
+  },
+
+/**
+ * clear sort flag on all columns
+ */
+  clearSort: function() {
+    for (var x=0;x<this.columns.length;x++)
+      this.columns[x].setUnsorted();
+  },
+
+/**
+ * clear filters on all columns
+ */
+  clearFilters: function() {
+    for (var x=0;x<this.columns.length;x++)
+      this.columns[x].setUnfiltered(true);
+    if (this.options.filterHandler)
+      this.options.filterHandler();
+  },
+
+/**
+ * returns number of columns with a user filter set
+ */
+  filterCount: function() {
+    for (var x=0,cnt=0;x<this.columns.length;x++)
+      if (this.columns[x].isFiltered()) cnt++;
+    return cnt;
+  },
+
+  sortHandler: function() {
+    this.cancelMenu();
+    this.setImages();
+    var n=this.findSortedColumn();
+    if (n < 0) return;
+    Rico.writeDebugMsg("sortHandler: sorting column "+n);
+    this.buffer.sortBuffer(n,this.columns[n].getSortDirection(),this.columns[n].format.type,this.columns[n]._sortfunc);
+    this.clearRows();
+    this.scrollDiv.scrollTop = 0;
+    this.buffer.fetch(0);
+  },
+
+  filterHandler: function() {
+    Rico.writeDebugMsg("filterHandler");
+    this.cancelMenu();
+    this.ClearSelection();
+    this.setImages();
+    if (this.bookmark) this.bookmark.innerHTML="&nbsp;";
+    this.clearRows();
+    this.buffer.fetch(-1);
+  },
+
+  bookmarkHandler: function(firstrow,lastrow) {
+    if (isNaN(firstrow) || !this.bookmark) return;
+    var totrows=this.buffer.totalRows;
+    if (totrows < lastrow) lastrow=totrows;
+    if (totrows<=0) {
+      var newhtml = RicoTranslate.getPhrase("No matching records");
+    } else if (lastrow<0) {
+      var newhtml = RicoTranslate.getPhrase("No records");
+    } else {
+      var newhtml = RicoTranslate.getPhrase("Listing records")+" "+firstrow+" - "+lastrow;
+      var totphrase = this.buffer.foundRowCount ? "of" : "of about";
+      newhtml+=" "+RicoTranslate.getPhrase(totphrase)+" "+totrows;
+    }
+    this.bookmark.innerHTML = newhtml;
+  },
+
+/**
+ * @return array of column objects which have invisible status
+ */
+  listInvisible: function() {
+    var hiddenColumns=new Array();
+    for (var x=0;x<this.columns.length;x++)
+      if (this.columns[x].visible==false)
+        hiddenColumns.push(this.columns[x]);
+    return hiddenColumns;
+  },
+
+/**
+ * Show all columns
+ */
+  showAll: function() {
+    var invisible=this.listInvisible();
+    for (var x=0;x<invisible.length;x++)
+      invisible[x].showColumn();
+  },
+
+  clearRows: function() {
+    if (this.isBlank==true) return;
+    for (var c=0; c < this.columns.length; c++)
+      this.columns[c].clearColumn();
+    this.ClearSelection();
+    this.isBlank = true;
+  },
+
+  blankRow: function(r) {
+     for (var c=0; c < this.columns.length; c++)
+        this.columns[c].clearCell(r);
+  },
+
+  refreshContents: function(startPos) {
+    Rico.writeDebugMsg("refreshContents: startPos="+startPos+" lastRow="+this.lastRowPos+" PartBlank="+this.isPartialBlank+" pageSize="+this.pageSize);
+    this.hideMsg();
+    this.cancelMenu();
+    this.unhighlight(); // in case highlighting was manually invoked
+    this.highlightEnabled=this.options.highlightSection!='none';
+    if (startPos == this.lastRowPos && !this.isPartialBlank && !this.isBlank) return;
+    this.isBlank = false;
+    var viewPrecedesBuffer = this.buffer.startPos > startPos
+    var contentStartPos = viewPrecedesBuffer ? this.buffer.startPos: startPos;
+    var contentEndPos = Math.min(this.buffer.startPos + this.buffer.size, startPos + this.pageSize);
+    var onRefreshComplete = this.options.onRefreshComplete;
+
+    if ((startPos + this.pageSize < this.buffer.startPos)
+        || (this.buffer.startPos + this.buffer.size < startPos)
+        || (this.buffer.size == 0)) {
+      this.clearRows();
+      if (onRefreshComplete != null)
+          onRefreshComplete(contentStartPos+1,contentEndPos);
+      return;
+    }
+
+    Rico.writeDebugMsg('refreshContents: contentStartPos='+contentStartPos+' contentEndPos='+contentEndPos+' viewPrecedesBuffer='+viewPrecedesBuffer);
+    if (this.options.highlightElem=='selection') this.HideSelection();
+    var rowSize = contentEndPos - contentStartPos;
+    this.buffer.setWindow(contentStartPos, rowSize );
+    var blankSize = this.pageSize - rowSize;
+    var blankOffset = viewPrecedesBuffer ? 0: rowSize;
+    var contentOffset = viewPrecedesBuffer ? blankSize: 0;
+
+    for (var r=0; r < rowSize; r++) { //initialize what we have
+      for (var c=0; c < this.columns.length; c++)
+        this.columns[c].displayValue(r + contentOffset);
+    }
+    for (var i=0; i < blankSize; i++)     // blank out the rest
+      this.blankRow(i + blankOffset);
+    if (this.options.highlightElem=='selection') this.ShowSelection();
+    this.isPartialBlank = blankSize > 0;
+    this.lastRowPos = startPos;
+    Rico.writeDebugMsg("refreshContents complete, startPos="+startPos);
+    // Check if user has set a onRefreshComplete function
+    if (onRefreshComplete != null)
+      onRefreshComplete(contentStartPos+1,contentEndPos);
+  },
+
+  scrollToRow: function(rowOffset) {
+     var p=this.rowToPixel(rowOffset);
+     Rico.writeDebugMsg("scrollToRow, rowOffset="+rowOffset+" pixel="+p);
+     this.scrollDiv.scrollTop = p; // this causes a scroll event
+     if ( this.options.onscroll )
+        this.options.onscroll( this, rowOffset );
+  },
+
+  scrollUp: function() {
+     this.moveRelative(-1);
+  },
+
+  scrollDown: function() {
+     this.moveRelative(1);
+  },
+
+  pageUp: function() {
+     this.moveRelative(-this.pageSize);
+  },
+
+  pageDown: function() {
+     this.moveRelative(this.pageSize);
+  },
+
+  adjustRow: function(rowOffset) {
+     var notdisp=this.topOfLastPage();
+     if (notdisp == 0 || !rowOffset) return 0;
+     return Math.min(notdisp,rowOffset);
+  },
+
+  rowToPixel: function(rowOffset) {
+     return this.adjustRow(rowOffset) * this.rowHeight;
+  },
+
+/**
+ * @returns row to display at top of scroll div
+ */
+  pixeltorow: function(p) {
+     var notdisp=this.topOfLastPage();
+     if (notdisp == 0) return 0;
+     var prow=parseInt(p/this.rowHeight);
+     return Math.min(notdisp,prow);
+  },
+
+  moveRelative: function(relOffset) {
+     newoffset=Math.max(this.scrollDiv.scrollTop+relOffset*this.rowHeight,0);
+     newoffset=Math.min(newoffset,this.scrollDiv.scrollHeight);
+     //Rico.writeDebugMsg("moveRelative, newoffset="+newoffset);
+     this.scrollDiv.scrollTop=newoffset;
+  },
+
+  pluginScroll: function() {
+     if (this.scrollPluggedIn) return;
+     Rico.writeDebugMsg("pluginScroll: wheelEvent="+this.wheelEvent);
+     Event.observe(this.scrollDiv,"scroll",this.scrollEventFunc, false);
+     for (var t=0; t<2; t++)
+       Event.observe(this.tabs[t],this.wheelEvent,this.wheelEventFunc, false);
+     this.scrollPluggedIn=true;
+  },
+
+  unplugScroll: function() {
+     if (!this.scrollPluggedIn) return;
+     Rico.writeDebugMsg("unplugScroll");
+     Event.stopObserving(this.scrollDiv,"scroll", this.scrollEventFunc , false);
+     for (var t=0; t<2; t++)
+       Event.stopObserving(this.tabs[t],this.wheelEvent,this.wheelEventFunc, false);
+     this.scrollPluggedIn=false;
+  },
+
+  handleWheel: function(e) {
+    var delta = 0;
+    if (e.wheelDelta) {
+      if (Prototype.Browser.Opera)
+        delta = e.wheelDelta/120;
+      else if (Prototype.Browser.WebKit)
+        delta = -e.wheelDelta/12;
+      else
+        delta = -e.wheelDelta/120;
+    } else if (e.detail) {
+      delta = e.detail/3; /* Mozilla/Gecko */
+    }
+    if (delta) this.moveRelative(delta);
+    Event.stop(e);
+    return false;
+  },
+
+  handleScroll: function(e) {
+     if ( this.scrollTimeout )
+       clearTimeout( this.scrollTimeout );
+     this.setHorizontalScroll();
+     var scrtop=this.scrollDiv.scrollTop;
+     var vscrollDiff = this.lastScrollPos-scrtop;
+     if (vscrollDiff == 0.00) return;
+     var newrow=this.pixeltorow(scrtop);
+     if (newrow == this.lastRowPos && !this.isPartialBlank && !this.isBlank) return;
+     var stamp1 = new Date();
+     //Rico.writeDebugMsg("handleScroll, newrow="+newrow+" scrtop="+scrtop);
+     this.buffer.fetch(newrow);
+     if (this.options.onscroll) this.options.onscroll(this, newrow);
+     this.scrollTimeout = setTimeout(this.scrollIdle.bind(this), 1200 );
+     this.lastScrollPos = this.scrollDiv.scrollTop;
+     var stamp2 = new Date();
+     //Rico.writeDebugMsg("handleScroll, time="+(stamp2.getTime()-stamp1.getTime()));
+  },
+
+  scrollIdle: function() {
+     if ( this.options.onscrollidle )
+        this.options.onscrollidle();
+  },
+
+  printAll: function(exportType) {
+    this.exportStart();
+    this.buffer.exportAllRows(this.exportBuffer.bind(this),this.exportFinish.bind(this,exportType));
+  },
+
+/**
+ * Send all rows to print window
+ */
+  exportBuffer: function(rows) {
+    for(var r=0; r < rows.length; r++) {
+      this.exportText+="<tr>";
+      for (var c=0; c<this.columns.length; c++) {
+        if (this.columns[c].visible)
+          this.exportText+="<td>"+this.columns[c]._format(rows[r][c].content)+"</td>";
+      }
+      this.exportText+="</tr>";
+    }
+  }
+
+};
+
+
+Object.extend(Rico.TableColumn.prototype, {
+
+initialize: function(liveGrid,colIdx,hdrInfo,tabIdx) {
+  this.baseInit(liveGrid,colIdx,hdrInfo,tabIdx);
+  Rico.writeDebugMsg(" sortable="+this.sortable+" filterable="+this.filterable+" hideable="+this.hideable+" isNullable="+this.isNullable+' isText='+this.isText);
+  this.fixHeaders(this.liveGrid.tableId, this.options.hdrIconsFirst);
+  if (this.format.type=='control' && this.format.control) {
+    // copy all properties/methods that start with '_'
+    if (typeof this.format.control=='string')
+      this.format.control=eval(this.format.control);
+    for (var property in this.format.control)
+      if (property.charAt(0)=='_') {
+        Rico.writeDebugMsg("Copying control property "+property);
+        this[property] = this.format.control[property];
+      }
+  } else if (this['format_'+this.format.type]) {
+    this._format=this['format_'+this.format.type].bind(this);
+  }
+},
+
+sortAsc: function() {
+  this.setColumnSort(Rico.TableColumn.SORT_ASC);
+},
+
+sortDesc: function() {
+  this.setColumnSort(Rico.TableColumn.SORT_DESC);
+},
+
+setColumnSort: function(direction) {
+  this.liveGrid.clearSort();
+  this.setSorted(direction);
+  if (this.liveGrid.options.saveColumnInfo.sort)
+    this.liveGrid.setCookie();
+  if (this.options.sortHandler)
+    this.options.sortHandler();
+},
+
+isSortable: function() {
+  return this.sortable;
+},
+
+isSorted: function() {
+  return this.currentSort != Rico.TableColumn.UNSORTED;
+},
+
+getSortDirection: function() {
+  return this.currentSort;
+},
+
+toggleSort: function() {
+  if (this.liveGrid.buffer && this.liveGrid.buffer.totalRows==0) return;
+  if (this.currentSort == Rico.TableColumn.SORT_ASC)
+    this.sortDesc();
+  else
+    this.sortAsc();
+},
+
+setUnsorted: function() {
+  this.setSorted(Rico.TableColumn.UNSORTED);
+},
+
+/**
+ * direction must be one of Rico.TableColumn.UNSORTED, .SORT_ASC, or .SORT_DESC
+ */
+setSorted: function(direction) {
+  this.currentSort = direction;
+},
+
+canFilter: function() {
+  return this.filterable;
+},
+
+getFilterText: function() {
+  var vals=[];
+  for (var i=0; i<this.filterValues.length; i++) {
+    var v=this.filterValues[i];
+    if (v!=null && v.match(/<span\s+class=(['"]?)ricolookup\1>(.*)<\/span>/i))
+      vals.push(RegExp.leftContext);
+    else
+      vals.push(v);
+  }
+  switch (this.filterOp) {
+    case 'EQ':   return vals[0];
+    case 'NE':   return 'not: '+vals.join(', ');
+    case 'LE':   return '<= '+vals[0];
+    case 'GE':   return '>= '+vals[0];
+    case 'LIKE': return 'like: '+vals[0];
+    case 'NULL': return '<empty>';
+    case 'NOTNULL': return '<not empty>';
+  }
+  return '?';
+},
+
+getFilterQueryParm: function() {
+  if (this.filterType == Rico.TableColumn.UNFILTERED) return '';
+  var retval='&f['+this.index+'][op]='+this.filterOp;
+  retval+='&f['+this.index+'][len]='+this.filterValues.length
+  for (var i=0; i<this.filterValues.length; i++)
+    retval+='&f['+this.index+']['+i+']='+escape(this.filterValues[i]);
+  return retval;
+},
+
+setUnfiltered: function(skipHandler) {
+  this.filterType = Rico.TableColumn.UNFILTERED;
+  if (this.liveGrid.options.saveColumnInfo.filter)
+    this.liveGrid.setCookie();
+  if (this.removeFilterFunc)
+    this.removeFilterFunc();
+  if (this.options.filterHandler && !skipHandler)
+    this.options.filterHandler();
+},
+
+setFilterEQ: function() {
+  if (this.userFilter=='' && this.isNullable)
+    this.setUserFilter('NULL');
+  else
+    this.setUserFilter('EQ');
+},
+setFilterNE: function() {
+  if (this.userFilter=='' && this.isNullable)
+    this.setUserFilter('NOTNULL');
+  else
+    this.setUserFilter('NE');
+},
+addFilterNE: function() {
+  this.filterValues.push(this.userFilter);
+  if (this.liveGrid.options.saveColumnInfo.filter)
+    this.liveGrid.setCookie();
+  if (this.options.filterHandler)
+    this.options.filterHandler();
+},
+setFilterGE: function() { this.setUserFilter('GE'); },
+setFilterLE: function() { this.setUserFilter('LE'); },
+setFilterKW: function() {
+  var keyword=prompt(RicoTranslate.getPhrase("Enter keyword to search for")+RicoTranslate.getPhrase(" (use * as a wildcard):"),'');
+  if (keyword!='' && keyword!=null) {
+    if (keyword.indexOf('*')==-1) keyword='*'+keyword+'*';
+    this.setFilter('LIKE',keyword,Rico.TableColumn.USERFILTER);
+  } else {
+    this.liveGrid.cancelMenu();
+  }
+},
+
+setUserFilter: function(relop) {
+  this.setFilter(relop,this.userFilter,Rico.TableColumn.USERFILTER);
+},
+
+setSystemFilter: function(relop,filter) {
+  this.setFilter(relop,filter,Rico.TableColumn.SYSTEMFILTER);
+},
+
+setFilter: function(relop,filter,type,removeFilterFunc) {
+  this.filterValues = [filter];
+  this.filterType = type;
+  this.filterOp = relop;
+  if (type == Rico.TableColumn.USERFILTER && this.liveGrid.options.saveColumnInfo.filter)
+    this.liveGrid.setCookie();
+  this.removeFilterFunc=removeFilterFunc;
+  if (this.options.filterHandler)
+    this.options.filterHandler();
+},
+
+isFiltered: function() {
+  return this.filterType == Rico.TableColumn.USERFILTER;
+},
+
+format_text: function(v) {
+  if (typeof v!='string')
+    return '&nbsp;';
+  else
+    return v.stripTags();
+},
+
+format_showTags: function(v) {
+  if (typeof v!='string')
+    return '&nbsp;';
+  else
+    return v.replace(/&/g, '&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
+},
+
+format_number: function(v) {
+  if (typeof v=='undefined' || v=='' || v==null)
+    return '&nbsp;';
+  else
+    return v.formatNumber(this.format);
+},
+
+format_datetime: function(v) {
+  if (typeof v=='undefined' || v=='' || v==null)
+    return '&nbsp;';
+  else {
+    var d=new Date;
+    d.setISO8601(v);
+    return d.formatDate(this.format.dateFmt || 'translateDateTime');
+  }
+},
+
+format_date: function(v) {
+  if (typeof v=='undefined' || v==null || v=='')
+    return '&nbsp;';
+  else {
+    var d=new Date;
+    if (!d.setISO8601(v)) return v;
+    return d.formatDate(this.format.dateFmt || 'translateDate');
+  }
+},
+
+fixHeaders: function(prefix, iconsfirst) {
+  if (this.sortable) {
+    switch (this.options.headingSort) {
+      case 'link':
+        var a=RicoUtil.wrapChildren(this.hdrCellDiv,'ricoSort',undefined,'a')
+        a.href = "#";
+        a.onclick = this.toggleSort.bindAsEventListener(this);
+        break;
+      case 'hover':
+        this.hdrCellDiv.onclick = this.toggleSort.bindAsEventListener(this);
+        break;
+    }
+  }
+  this.imgFilter = document.createElement('img');
+  this.imgFilter.style.display='none';
+  this.imgFilter.src=Rico.imgDir+this.options.filterImg;
+  this.imgFilter.className='ricoLG_HdrIcon';
+  this.imgSort = document.createElement('img');
+  this.imgSort.style.display='none';
+  this.imgSort.className='ricoLG_HdrIcon';
+  if (iconsfirst) {
+    this.hdrCellDiv.insertBefore(this.imgSort,this.hdrCellDiv.firstChild);
+    this.hdrCellDiv.insertBefore(this.imgFilter,this.hdrCellDiv.firstChild);
+  } else {
+    this.hdrCellDiv.appendChild(this.imgFilter);
+    this.hdrCellDiv.appendChild(this.imgSort);
+  }
+},
+
+getValue: function(windowRow) {
+  return this.liveGrid.buffer.getWindowValue(windowRow,this.index);
+},
+
+getFormattedValue: function(windowRow) {
+  return this._format(this.getValue(windowRow));
+},
+
+getBufferCell: function(windowRow) {
+  return this.liveGrid.buffer.getWindowCell(windowRow,this.index);
+},
+
+setValue: function(windowRow,newval) {
+  this.liveGrid.buffer.setWindowValue(windowRow,this.index,newval);
+},
+
+_format: function(v) {
+  return v;
+},
+
+_display: function(v,gridCell) {
+  gridCell.innerHTML=this._format(v);
+},
+
+displayValue: function(windowRow) {
+  var bufCell=this.getBufferCell(windowRow);
+  if (!bufCell) {
+    this.clearCell(windowRow);
+    return;
+  }
+  var gridCell=this.cell(windowRow);
+  this._display(bufCell.content,gridCell,windowRow);
+  var acceptAttr=this.liveGrid.buffer.options.acceptAttr;
+  for (var k=0; k<acceptAttr.length; k++) {
+    var bufAttr=bufCell['_'+acceptAttr[k]] || '';
+    switch (acceptAttr[k]) {
+      case 'style': gridCell.style.cssText=bufAttr; break;
+      case 'class': gridCell.className=bufAttr; break;
+      default:      gridCell['_'+acceptAttr[k]]=bufAttr; break;
+    }
+  }
+}
+
+});
+
+Rico.TableColumn.checkbox = Class.create();
+
+Rico.TableColumn.checkbox.prototype = {
+
+  initialize: function(checkedValue, uncheckedValue, defaultValue, readOnly) {
+    this._checkedValue=checkedValue;
+    this._uncheckedValue=uncheckedValue;
+    this._defaultValue=defaultValue || false;
+    this._readOnly=readOnly || false;
+    this._checkboxes=[];
+  },
+
+  _create: function(gridCell,windowRow) {
+    this._checkboxes[windowRow]=RicoUtil.createFormField(gridCell,'input','checkbox',this.liveGrid.tableId+'_chkbox_'+this.index+'_'+windowRow);
+    this._clear(gridCell,windowRow);
+    if (this._readOnly)
+      this._checkboxes[windowRow].disabled=true;
+    else
+      Event.observe(this._checkboxes[windowRow], "click", this._onclick.bindAsEventListener(this), false);
+  },
+
+  _onclick: function(e) {
+    var elem=Event.element(e);
+    var windowRow=parseInt(elem.id.split(/_/).pop());
+    var newval=elem.checked ? this._checkedValue : this._uncheckedValue;
+    this.setValue(windowRow,newval);
+  },
+
+  _clear: function(gridCell,windowRow) {
+    this._checkboxes[windowRow].checked=this._defaultValue;
+  },
+
+  _display: function(v,gridCell,windowRow) {
+    this._checkboxes[windowRow].checked=(v==this._checkedValue);
+  }
+
+}
+
+Rico.TableColumn.link = Class.create();
+
+Rico.TableColumn.link.prototype = {
+
+  initialize: function(href,target) {
+    this._href=href;
+    this._target=target;
+    this._anchors=[];
+  },
+
+  _create: function(gridCell,windowRow) {
+    this._anchors[windowRow]=RicoUtil.createFormField(gridCell,'a',null,this.liveGrid.tableId+'_a_'+this.index+'_'+windowRow);
+    if (this._target) this._anchors[windowRow].target=this._target;
+    this._clear(gridCell,windowRow);
+  },
+
+  _clear: function(gridCell,windowRow) {
+    this._anchors[windowRow].href='';
+    this._anchors[windowRow].innerHTML='';
+  },
+
+  _display: function(v,gridCell,windowRow) {
+    this._anchors[windowRow].innerHTML=v;
+    var getWindowValue=this.liveGrid.buffer.getWindowValue.bind(this.liveGrid.buffer);
+    this._anchors[windowRow].href=this._href.replace(/\{\d+\}/g,
+      function ($1) {
+        var colIdx=parseInt($1.substr(1));
+        return getWindowValue(windowRow,colIdx);
+      }
+    );
+  }
+
+}
+
+Rico.TableColumn.lookup = Class.create();
+
+Rico.TableColumn.lookup.prototype = {
+
+  initialize: function(map, defaultCode, defaultDesc) {
+    this._map=map;
+    this._defaultCode=defaultCode || '';
+    this._defaultDesc=defaultDesc || '&nbsp;';
+    this._sortfunc=this._sortvalue.bind(this);
+    this._codes=[];
+    this._descriptions=[];
+  },
+
+  _create: function(gridCell,windowRow) {
+    this._descriptions[windowRow]=RicoUtil.createFormField(gridCell,'span',null,this.liveGrid.tableId+'_desc_'+this.index+'_'+windowRow);
+    this._codes[windowRow]=RicoUtil.createFormField(gridCell,'input','hidden',this.liveGrid.tableId+'_code_'+this.index+'_'+windowRow);
+    this._clear(gridCell,windowRow);
+  },
+
+  _clear: function(gridCell,windowRow) {
+    this._codes[windowRow].value=this._defaultCode;
+    this._descriptions[windowRow].innerHTML=this._defaultDesc;
+  },
+
+  _sortvalue: function(v) {
+    return this._getdesc(v).replace(/&amp;/g, '&').replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&nbsp;/g,' ');
+  },
+
+  _getdesc: function(v) {
+    var desc=this._map[v];
+    return (typeof desc=='string') ? desc : this._defaultDesc;
+  },
+
+  _display: function(v,gridCell,windowRow) {
+    this._codes[windowRow].value=v;
+    this._descriptions[windowRow].innerHTML=this._getdesc(v);
+  }
+
+}
+
+Rico.includeLoaded('ricoLiveGrid.js');
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoLiveGridAjax.js b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoLiveGridAjax.js
new file mode 100644 (file)
index 0000000..ab193ff
--- /dev/null
@@ -0,0 +1,412 @@
+if(typeof Rico=='undefined') throw("LiveGridAjax requires the Rico JavaScript framework");
+if(typeof RicoUtil=='undefined') throw("LiveGridAjax requires the RicoUtil object");
+if(typeof Rico.Buffer=='undefined') throw("LiveGridAjax requires the Rico.Buffer object");
+
+
+/**
+ * Data source is a static XML file located on the server
+ */
+Rico.Buffer.AjaxXML = Class.create();
+
+Rico.Buffer.AjaxXML.prototype = {
+
+  initialize: function(url,options,ajaxOptions) {
+    Object.extend(this, new Rico.Buffer.Base());
+    Object.extend(this, new Rico.Buffer.AjaxXMLMethods);
+    this.dataSource=url;
+    this.options.bufferTimeout = 20000;  // time to wait for ajax response (milliseconds)
+    this.options.requestParameters = [];
+    Object.extend(this.options, options || {});
+    this.ajaxOptions = { parameters: null, method : 'get' };
+    Object.extend(this.ajaxOptions, ajaxOptions || {});
+    this.requestCount=0;
+    this.processingRequest=false;
+    this.pendingRequest=-1;
+  }
+}
+
+Rico.Buffer.AjaxXMLMethods = function() {};
+
+Rico.Buffer.AjaxXMLMethods.prototype = {
+
+  fetch: function(offset) {
+    if ( this.isInRange(offset) ) {
+      Rico.writeDebugMsg("AjaxXML fetch: in buffer");
+      this.liveGrid.refreshContents(offset);
+      return;
+    }
+    this.processingRequest=true
+    Rico.writeDebugMsg("AjaxXML fetch, offset="+offset);
+    this.liveGrid.showMsg("Waiting for data...");
+    this.timeoutHandler = setTimeout( this.handleTimedOut.bind(this), this.options.bufferTimeout);
+    this.sendAjaxRequest(offset,0,this.ajaxUpdate.bind(this,offset));
+  },
+
+/**
+ * Server did not respond in time... assume that there could have been
+ * an error, and allow requests to be processed again.
+ */
+  handleTimedOut: function() {
+    Rico.writeDebugMsg("Request Timed Out");
+    this.liveGrid.showMsg("Request for data timed out!");
+  },
+
+  formQueryHash: function(startPos,fetchSize) {
+    if (typeof fetchSize!='number') fetchSize=this.totalRows;
+    var queryHash= {
+      id: this.liveGrid.tableId,
+      page_size: fetchSize,
+      offset: startPos
+    };
+    if (!this.foundRowCount) queryHash['get_total']='true';
+    if (this.options.requestParameters) {
+      for ( var i=0; i < this.options.requestParameters.length; i++ ) {
+        var anArg = this.options.requestParameters[i];
+        if ( anArg.name != undefined && anArg.value != undefined ) {
+          queryHash[anArg.name]=anArg.value;
+        } else {
+          var ePos  = anArg.indexOf('=');
+          var argName  = anArg.substring( 0, ePos );
+          var argValue = anArg.substring( ePos + 1 );
+          queryHash[argName]=argValue;
+        }
+      }
+    }
+    
+    // sort
+    Object.extend(queryHash,this.sortParm);
+
+    // filters
+    for (n=0; n<this.liveGrid.columns.length; n++) {
+      var c=this.liveGrid.columns[n];
+      if (c.filterType == Rico.TableColumn.UNFILTERED) continue;
+      queryHash['f['+c.index+'][op]']=c.filterOp;
+      queryHash['f['+c.index+'][len]']=c.filterValues.length
+      for (var i=0; i<c.filterValues.length; i++)
+        queryHash['f['+c.index+']['+i+']']=c.filterValues[i];
+    }
+      
+    return $H(queryHash);
+  },
+
+  sendAjaxRequest: function(startPos,fetchSize,onComplete) {
+    this.ajaxOptions.parameters = this.formQueryHash(startPos,fetchSize);
+    this.ajaxOptions.onComplete = onComplete;
+    this.requestCount++;
+    Rico.writeDebugMsg('req '+this.requestCount+':'+this.ajaxOptions.parameters.inspect());
+    new Ajax.Request(this.dataSource, this.ajaxOptions);
+  },
+  
+  clearTimer: function() {
+    if(typeof this.timeoutHandler != "number") return;
+    window.clearTimeout(this.timeoutHandler);
+    delete this.timeoutHandler;
+  },
+
+  ajaxUpdate: function(startPos,request) {
+    this.clearTimer();
+    this.processingRequest=false;
+    if (request.status != 200) {
+      Rico.writeDebugMsg("ajaxUpdate: received http error="+request.status);
+      this.liveGrid.showMsg('Received HTTP error: '+request.status);
+      return;
+    }
+    var response = request.responseXML.getElementsByTagName("ajax-response");
+    if (response == null || response.length != 1) return;
+    this.updateBuffer(response[0],startPos);
+    this.CheckRowCount(response[0],startPos);
+    if (this.options.TimeOut && this.timerMsg)
+      this.restartSessionTimer();
+    if (this.options.onAjaxUpdate)
+      this.options.onAjaxUpdate();
+    if (this.pendingRequest>=0) {
+      var offset=this.pendingRequest;
+      Rico.writeDebugMsg("ajaxUpdate: found pending request for offset="+offset);
+      this.pendingRequest=-1;
+      this.fetch(offset);
+    }
+  },
+
+  CheckRowCount: function(ajaxResponse,offset) {
+    //try {
+      Rico.writeDebugMsg("CheckRowCount, size="+this.size+' rcv cnt type='+typeof(this.rowcntContent));
+      if (this.rcvdRowCount==true) {
+        Rico.writeDebugMsg("found row cnt: "+this.rowcntContent);
+        var eofrow=parseInt(this.rowcntContent);
+        var lastTotalRows=this.totalRows;
+        if (!isNaN(eofrow) && eofrow!=lastTotalRows) {
+          this.setTotalRows(eofrow);
+          var newpos=Math.min(this.liveGrid.topOfLastPage(),offset);
+          Rico.writeDebugMsg("CheckRowCount: new rowcnt="+eofrow+" newpos="+newpos);
+          if (lastTotalRows==0 && this.liveGrid.sizeTo=='data')
+            this.liveGrid.adjustPageSize();
+          //this.lastRowPos=-1;
+          this.liveGrid.scrollToRow(newpos);
+          if ( this.isInRange(newpos) ) {
+            this.liveGrid.refreshContents(newpos);
+          } else {
+            this.fetch(newpos);
+          }
+          return;
+        }
+      } else {
+        var lastbufrow=offset+this.rcvdRows;
+        if (lastbufrow>this.totalRows) {
+          var newcnt=lastbufrow;
+          Rico.writeDebugMsg("extending totrows to "+newcnt);
+          this.setTotalRows(newcnt);
+        }
+      }
+      var newpos=this.liveGrid.pixeltorow(this.liveGrid.scrollDiv.scrollTop);
+      Rico.writeDebugMsg("CheckRowCount: newpos="+newpos);
+      this.liveGrid.refreshContents(newpos);
+    //}
+    //catch(err) {
+    //  alert("Error in CheckRowCount:"+err.message);
+    //}
+  },
+
+  updateBuffer: function(ajaxResponse, start) {
+    Rico.writeDebugMsg("updateBuffer: "+start);
+    this.rcvdRows = 0;
+    var newRows = this.loadRows(ajaxResponse);
+    if (newRows==null) return;
+    this.rcvdRows = newRows.length;
+    Rico.writeDebugMsg("updateBuffer: # of rows="+this.rcvdRows);
+    if (this.rows.length == 0) { // initial load
+      this.rows = newRows;
+      this.startPos = start;
+    } else if (start > this.startPos) { //appending
+      if (this.startPos + this.rows.length < start) {
+        this.rows =  newRows;
+        this.startPos = start;//
+      } else {
+        this.rows = this.rows.concat( newRows.slice(0, newRows.length));
+        if (this.rows.length > this.maxBufferSize) {
+          var fullSize = this.rows.length;
+          this.rows = this.rows.slice(this.rows.length - this.maxBufferSize, this.rows.length)
+          this.startPos = this.startPos +  (fullSize - this.rows.length);
+        }
+      }
+    } else { //prepending
+      if (start + newRows.length < this.startPos) {
+        this.rows =  newRows;
+      } else {
+        this.rows = newRows.slice(0, this.startPos).concat(this.rows);
+        if (this.maxBufferSize && this.rows.length > this.maxBufferSize)
+          this.rows = this.rows.slice(0, this.maxBufferSize)
+      }
+      this.startPos =  start;
+    }
+    this.size = this.rows.length;
+  },
+
+  loadRows: function(ajaxResponse) {
+    Rico.writeDebugMsg("loadRows");
+    this.rcvdRowCount = false;
+    var debugtags = ajaxResponse.getElementsByTagName('debug');
+    for (var i=0; i<debugtags.length; i++)
+      Rico.writeDebugMsg("loadRows, debug msg "+i+": "+RicoUtil.getContentAsString(debugtags[i],this.options.isEncoded));
+    var error = ajaxResponse.getElementsByTagName('error');
+    if (error.length > 0) {
+      var msg=RicoUtil.getContentAsString(error[0],this.options.isEncoded);
+      alert("Data provider returned an error:\n"+msg);
+      Rico.writeDebugMsg("Data provider returned an error:\n"+msg);
+      return null;
+    }
+    var rowsElement = ajaxResponse.getElementsByTagName('rows')[0];
+    var rowcnttags = ajaxResponse.getElementsByTagName('rowcount');
+    if (rowcnttags && rowcnttags.length==1) {
+      this.rowcntContent = RicoUtil.getContentAsString(rowcnttags[0],this.options.isEncoded);
+      this.rcvdRowCount = true;
+      this.foundRowCount = true;
+      Rico.writeDebugMsg("loadRows, found RowCount="+this.rowcntContent);
+    }
+    this.updateUI = rowsElement.getAttribute("update_ui") == "true";
+    this.rcvdOffset = rowsElement.getAttribute("offset");
+    Rico.writeDebugMsg("loadRows, rcvdOffset="+this.rcvdOffset);
+    return this.dom2jstable(rowsElement);
+  }
+
+};
+
+
+
+Rico.Buffer.AjaxSQL = Class.create();
+
+Rico.Buffer.AjaxSQL.prototype = {
+
+  initialize: function(url,options,ajaxOptions) {
+    Object.extend(this, new Rico.Buffer.AjaxXML());
+    Object.extend(this, new Rico.Buffer.AjaxSQLMethods());
+    this.dataSource=url;
+    this.options.canFilter=true;
+    this.options.largeBufferSize  = 7.0;   // 7 pages
+    this.options.nearLimitFactor  = 1.0;   // 1 page
+    Object.extend(this.options, options || {});
+    Object.extend(this.ajaxOptions, ajaxOptions || {});
+    this.sortParm={};
+  }
+}
+  
+Rico.Buffer.AjaxSQLMethods = function() {};
+
+Rico.Buffer.AjaxSQLMethods.prototype = {
+
+  registerGrid: function(liveGrid) {
+    this.liveGrid = liveGrid;
+    this.sessionExpired=false;
+    this.timerMsg=$(liveGrid.tableId+'_timer');
+    if (this.options.TimeOut && this.timerMsg) {
+      if (!this.timerMsg.title) this.timerMsg.title=RicoTranslate.getPhrase("minutes before your session expires")
+      this.restartSessionTimer();
+    }
+  },
+  
+  setBufferSize: function(pageSize) {
+    this.maxFetchSize = Math.max(50,parseInt(this.options.largeBufferSize * pageSize));
+    this.nearLimit = parseInt(this.options.nearLimitFactor * pageSize);
+    this.maxBufferSize = this.maxFetchSize * 3;
+  },
+
+  restartSessionTimer: function() {
+    if (this.sessionExpired==true) return;
+    this.timeRemaining=this.options.TimeOut+1;
+    if (this.sessionTimer) clearTimeout(this.sessionTimer);
+    this.updateSessionTimer();
+  },
+  
+  updateSessionTimer: function() {
+    if (--this.timeRemaining<=0) {
+      this.displaySessionTimer(RicoTranslate.getPhrase("EXPIRED"));
+      this.timerMsg.style.backgroundColor="red";
+      this.sessionExpired=true;
+    } else {
+      this.displaySessionTimer(this.timeRemaining);
+      this.sessionTimer=setTimeout(this.updateSessionTimer.bind(this),60000);
+    }
+  },
+  
+  displaySessionTimer: function(msg) {
+    this.timerMsg.innerHTML='&nbsp;'+msg+'&nbsp;';
+  },
+  
+  refresh: function() {
+    this.fetch(this.lastOffset);
+  },
+  
+  /**
+   * Fetch data from database.
+   * @param offset position (row) within the dataset (-1=clear existing buffer before issuing request)
+   */
+  fetch: function(offset) {
+    Rico.writeDebugMsg("AjaxSQL fetch, offset="+offset+' lastOffset='+this.lastOffset);
+    if (this.processingRequest) {
+      Rico.writeDebugMsg("AjaxSQL fetch: queue request");
+      this.pendingRequest=offset;
+      return;
+    }
+    if (offset < 0) {
+      this.clear();
+      this.setTotalRows(0);
+      this.foundRowCount = false;
+      offset=0;
+    }
+    var lastOffset = this.lastOffset;
+    this.lastOffset = offset;
+    var inRange=this.isInRange(offset);
+    if (inRange) {
+      Rico.writeDebugMsg("AjaxSQL fetch: in buffer");
+      this.liveGrid.refreshContents(offset);
+      if (offset > lastOffset) {
+        if (offset+this.liveGrid.pageSize < this.endPos()-this.nearLimit) return;
+        if (this.endPos()==this.totalRows && this.foundRowCount) return;
+      } else if (offset < lastOffset) {
+        if (offset > this.startPos+this.nearLimit) return;
+        if (this.startPos==0) return;
+      } else return;
+    }
+    if (offset >= this.totalRows && this.foundRowCount) return;
+    
+    this.processingRequest=true
+    Rico.writeDebugMsg("AjaxSQL fetch, processing offset="+offset);
+    var bufferStartPos = this.getFetchOffset(offset);
+    var fetchSize = this.getFetchSize(bufferStartPos);
+    var partialLoaded = false;
+
+    if (!inRange) this.liveGrid.showMsg("Waiting for data...");
+    this.timeoutHandler = setTimeout( this.handleTimedOut.bind(this), this.options.bufferTimeout);
+    this.sendAjaxRequest(bufferStartPos,fetchSize,this.ajaxUpdate.bind(this,bufferStartPos));
+  },
+
+  getFetchSize: function(adjustedOffset) {
+    var adjustedSize = 0;
+    if (adjustedOffset >= this.startPos) { //appending
+      var endFetchOffset = this.maxFetchSize + adjustedOffset;
+      adjustedSize = endFetchOffset - adjustedOffset;
+      if(adjustedOffset == 0 && adjustedSize < this.maxFetchSize)
+        adjustedSize = this.maxFetchSize;
+      Rico.writeDebugMsg("getFetchSize/append, adjustedSize="+adjustedSize+" adjustedOffset="+adjustedOffset+' endFetchOffset='+endFetchOffset);
+    } else { //prepending
+      adjustedSize = Math.min(this.startPos - adjustedOffset,this.maxFetchSize);
+    }
+    return adjustedSize;
+  },
+
+  getFetchOffset: function(offset) {
+    var adjustedOffset = offset;
+    if (offset > this.startPos)
+      adjustedOffset = Math.max(offset, this.endPos());  //appending
+    else if (offset + this.maxFetchSize >= this.startPos)
+      adjustedOffset = Math.max(this.startPos - this.maxFetchSize, 0);  //prepending
+    return adjustedOffset;
+  },
+
+  sortBuffer: function(colnum,sortdir,coltype) {
+    this.sortParm={};
+    if (this.options.sortParmFmt && this.options.sortParmFmt=='displayName') {
+      this.sortParm['sort_col']=this.liveGrid.columns[colnum].displayName.toLowerCase();
+      this.sortParm['sort_dir']=sortdir;
+    }else{
+     this.sortParm['s'+colnum]=sortdir;
+    }
+    this.clear();
+  },
+  
+  exportAllRows: function(populate,finish) {
+    this.exportPopulate=populate;
+    this.exportFinish=finish;
+    this.liveGrid.showMsg("Waiting for data...");
+    this.sendExportRequest(0);
+  },
+  
+/**
+ * Make ajax request for print window data
+ */
+  sendExportRequest: function(offset) {
+    this.timeoutHandler = setTimeout(this.exportTimedOut.bind(this), this.options.bufferTimeout);
+    this.sendAjaxRequest(offset,200,this.exportAppend.bind(this,offset));
+  },
+
+  exportTimedOut: function() {
+    Rico.writeDebugMsg("Print Request Timed Out");
+    this.liveGrid.showMsg("Request for data timed out!");
+    this.exportFinish();
+  },
+
+  exportAppend: function(startPos,request) {
+    this.clearTimer();
+    var response = request.responseXML.getElementsByTagName("ajax-response");
+    if (response == null || response.length != 1) return;
+    var rowsElement = response[0].getElementsByTagName('rows')[0];
+    var rows=this.dom2jstable(rowsElement);
+    this.exportPopulate(rows);
+    if (rows.length==0)
+      this.exportFinish();
+    else
+      this.sendExportRequest(startPos+rows.length);
+  }
+
+};
+
+Rico.includeLoaded('ricoLiveGridAjax.js');
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoLiveGridForms.js b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoLiveGridForms.js
new file mode 100644 (file)
index 0000000..3e60c45
--- /dev/null
@@ -0,0 +1,830 @@
+if(typeof Rico=='undefined') throw("LiveGridForms requires the Rico JavaScript framework");\r
+if(typeof RicoUtil=='undefined') throw("LiveGridForms requires the RicoUtil object");\r
+if(typeof RicoTranslate=='undefined') throw("LiveGridForms requires the RicoTranslate object");\r
+\r
+\r
+Rico.TableEdit = Class.create();\r
+\r
+Rico.TableEdit.prototype = {\r
+\r
+  initialize: function(liveGrid) {\r
+    Rico.writeDebugMsg('Rico.TableEdit initialize: '+liveGrid.tableId);\r
+    this.grid=liveGrid;\r
+    this.options = {\r
+      maxDisplayLen    : 20,    // max displayed text field length\r
+      panelHeight      : 200,   // size of tabbed panels\r
+      panelWidth       : 500,\r
+      hoverClass       : 'tabHover',\r
+      selectedClass    : 'tabSelected',\r
+      compact          : false,    // compact corners\r
+      RecordName       : 'record',\r
+      readOnlyColor    : '#AAA',   // read-only fields displayed using this color\r
+      showSaveMsg      : 'errors'  // disposition of database update responses (full - show full response, errors - show full response for errors and short response otherwise)
+    }
+    Object.extend(this.options, liveGrid.options);
+    this.menu=liveGrid.menu;\r
+    this.menu.options.dataMenuHandler=this.editMenu.bind(this);\r
+    this.menu.ignoreClicks();\r
+    RicoEditControls.atLoad();\r
+    this.createEditDiv();\r
+    this.saveMsg=$(liveGrid.tableId+'_savemsg');\r
+    Event.observe(document,"click", this.clearSaveMsg.bindAsEventListener(this), false);\r
+    this.TEerror=false;\r
+    this.extraMenuItems=new Array();\r
+    this.responseHandler=this.processResponse.bind(this);\r
+  },\r
+  \r
+  createEditDiv: function() {\r
+\r
+    // create editDiv (form)\r
+    \r
+    this.requestCount=1;\r
+    this.editDiv = this.grid.createDiv('edit',document.body);\r
+    this.editDiv.style.display='none';\r
+    if (this.options.canEdit || this.options.canAdd) {\r
+      this.startForm();\r
+      this.createForm(this.form);\r
+    } else {\r
+      var button=this.createButton("Close");\r
+      Event.observe(button,"click", this.cancelEdit.bindAsEventListener(this), false);
+      this.createForm(this.editDiv);\r
+    }\r
+    this.editDivCreated=true;\r
+    this.formPopup=new Rico.Popup({ignoreClicks:true},this.editDiv);\r
+\r
+    // create responseDialog\r
+    \r
+    this.responseDialog = this.grid.createDiv('editResponse',document.body);\r
+    this.responseDialog.style.display='none';\r
+    \r
+    var button = document.createElement('button');\r
+       button.appendChild(document.createTextNode('OK'));\r
+    button.onclick=this.ackResponse.bindAsEventListener(this);
+    this.responseDialog.appendChild(button);
+\r
+    this.responseDiv = this.grid.createDiv('editResponseText',this.responseDialog);\r
+\r
+    if (this.panelGroup) {\r
+      Rico.writeDebugMsg("createEditDiv complete, requestCount="+this.requestCount);
+      setTimeout(this.initPanelGroup.bind(this),50);\r
+    }\r
+  },\r
+  \r
+  initPanelGroup: function() {\r
+    this.requestCount--;\r
+    Rico.writeDebugMsg("initPanelGroup: "+this.requestCount);
+    if (this.requestCount>0) return;\r
+    var wi=parseInt(this.options.panelWidth);\r
+    this.form.style.width=(wi+10)+'px';
+    if (Prototype.Browser.WebKit) this.editDiv.style.display='block';  // this causes display to flash briefly\r
+    this.options.bgColor = Rico.Color.createColorFromBackground(this.form);\r
+    this.editDiv.style.display='none';\r
+    this.options.panelHdrWidth=(Math.floor(wi / this.options.panels.length)-4)+'px';\r
+    this.Accordion=new Rico.TabbedPanel(this.panelHdr.findAll(this.notEmpty), this.panelContent.findAll(this.notEmpty), this.options);\r
+  },\r
+  \r
+  notEmpty: function(v) {\r
+    return typeof(v)!='undefined';\r
+  },\r
+  \r
+  startForm: function() {\r
+    this.form = document.createElement('form');\r
+    this.form.onsubmit=function() {return false;};
+    this.editDiv.appendChild(this.form);\r
+\r
+    var tab = document.createElement('table');\r
+    var row = tab.insertRow(-1);
+    var cell = row.insertCell(-1);
+    var button=cell.appendChild(this.createButton("Save \t"+this.options.RecordName));\r
+    Event.observe(button,"click", this.TESubmit.bindAsEventListener(this), false);
+    var cell = row.insertCell(-1);
+    var button=cell.appendChild(this.createButton("Cancel"));\r
+    Event.observe(button,"click", this.cancelEdit.bindAsEventListener(this), false);
+    this.form.appendChild(tab);
+\r
+    // hidden fields\r
+    this.hiddenFields = document.createElement('div');
+    this.hiddenFields.style.display='none';\r
+    this.action = this.appendHiddenField(this.grid.tableId+'__action','');\r
+    for (var i=0; i<this.grid.columns.length; i++) {\r
+      var fldSpec=this.grid.columns[i].format;\r
+      if (fldSpec && fldSpec.FormView && fldSpec.FormView=="hidden")\r
+        this.appendHiddenField(fldSpec.FieldName,fldSpec.ColData);\r
+    }\r
+    this.form.appendChild(this.hiddenFields);
+  },\r
+  \r
+  createButton: function(buttonLabel) {\r
+    var button = document.createElement('button');\r
+    buttonLabel=RicoTranslate.getPhrase(buttonLabel);\r
+       button.innerHTML="<span style='text-decoration:underline;'>"+buttonLabel.charAt(0)+"</span>"+buttonLabel.substr(1);\r
+    button.accessKey=buttonLabel.charAt(0);\r
+    return button;
+  },\r
+  \r
+  createPanel: function(i) {\r
+    var hasFields=false;\r
+    for (var j=0; j<this.grid.columns.length; j++) {\r
+      var fldSpec=this.grid.columns[j].format;\r
+      if (!fldSpec) continue;\r
+      if (!fldSpec.EntryType) continue\r
+      if (fldSpec.EntryType=='H') continue;\r
+      var panelIdx=fldSpec.panelIdx || 0;\r
+      if (panelIdx==i) {\r
+        hasFields=true;\r
+        break;\r
+      }\r
+    }\r
+    if (!hasFields) return false;\r
+    this.panelHdr[i] = document.createElement('div');\r
+    this.panelHdr[i].className='tabHeader';\r
+    this.panelHdr[i].innerHTML=this.options.panels[i];
+    this.panelHdrs.appendChild(this.panelHdr[i]);
+    this.panelContent[i] = document.createElement('div');
+    this.panelContent[i].className='tabContent';\r
+    this.panelContents.appendChild(this.panelContent[i]);\r
+    return true;\r
+  },\r
+  \r
+  createForm: function(parentDiv) {\r
+    var tables=[];\r
+    this.panelHdr=[];\r
+    this.panelContent=[];\r
+    if (this.options.panels) {\r
+      this.panelGroup = document.createElement('div');\r
+      this.panelGroup.className='tabPanelGroup';
+      this.panelHdrs = document.createElement('div');\r
+      this.panelGroup.appendChild(this.panelHdrs);\r
+      this.panelContents = document.createElement('div');\r
+      this.panelContents.className='tabContentContainer';
+      this.panelGroup.appendChild(this.panelContents);\r
+      parentDiv.appendChild(this.panelGroup);\r
+      if (this.grid.direction=='rtl') {\r
+        for (var i=this.options.panels.length-1; i>=0; i--)\r
+          if (this.createPanel(i))\r
+            tables[i]=this.createFormTable(this.panelContent[i],'tabContent');\r
+      } else {\r
+        for (var i=0; i<this.options.panels.length; i++)\r
+          if (this.createPanel(i))\r
+            tables[i]=this.createFormTable(this.panelContent[i],'tabContent');\r
+      }\r
+      parentDiv.appendChild(this.panelGroup);\r
+    } else {\r
+      var div=document.createElement('div');
+      div.className='noTabContent';\r
+      tables[0]=this.createFormTable(div);\r
+      parentDiv.appendChild(div);\r
+    }\r
+    for (var i=0; i<this.grid.columns.length; i++) {\r
+      var fldSpec=this.grid.columns[i].format;\r
+      if (!fldSpec) continue;\r
+      var panelIdx=fldSpec.panelIdx || 0;\r
+      if (tables[panelIdx]) this.appendFormField(this.grid.columns[i],tables[panelIdx]);\r
+    }\r
+  },\r
+  \r
+  createFormTable: function(div) {\r
+    var tab=document.createElement('table');\r
+    tab.border=0;
+    div.appendChild(tab);
+    return tab;
+  },\r
+  \r
+  appendHiddenField: function(name,value) {\r
+    var field=RicoUtil.createFormField(this.hiddenFields,'input','hidden',name,name);\r
+    field.value=value;
+    return field;
+  },\r
+  \r
+  appendFormField: function(column, table) {\r
+    if (!column.format.EntryType) return;\r
+    if (column.format.EntryType=="H") return;\r
+    if (column.format.FormView) return;\r
+    Rico.writeDebugMsg('appendFormField: '+column.format.Hdg+' - '+column.format.EntryType);\r
+    var row = table.insertRow(-1);\r
+    var hdr = row.insertCell(-1);
+    column.formLabel=hdr;
+    if (hdr.noWrap) hdr.noWrap=true;
+    var entry = row.insertCell(-1);\r
+    if (entry.noWrap) entry.noWrap=true;
+    hdr.innerHTML=column.format.Hdg;\r
+    hdr.className='ricoEditLabel';\r
+    if (column.format.Help) {\r
+      hdr.title=column.format.Help;\r
+      hdr.className='ricoEditLabelWithHelp';\r
+    }\r
+    var field, name=column.format.FieldName;\r
+    switch (column.format.EntryType) {\r
+      case 'TA','tinyMCE':\r
+        field=RicoUtil.createFormField(entry,'textarea',null,name);\r
+        field.cols=column.format.TxtAreaCols;\r
+        field.rows=column.format.TxtAreaRows;\r
+        field.innerHTML=column.format.ColData;\r
+        hdr.style.verticalAlign='top';\r
+        break;\r
+      case 'R':\r
+      case 'RL':\r
+        field=RicoUtil.createFormField(entry,'div',null,name);\r
+        if (column.format.isNullable)\r
+          this.addSelectOption(field,this.options.TableSelectNone,"(none)");\r
+        this.selectValuesRequest(field,column.format);\r
+        break;\r
+      case 'N':\r
+        field=RicoUtil.createFormField(entry,'select',null,name);\r
+        if (column.format.isNullable)\r
+          this.addSelectOption(field,this.options.TableSelectNone,"(none)");\r
+        field.onchange=this.checkSelectNew.bindAsEventListener(this);\r
+        this.selectValuesRequest(field,column.format);\r
+        field=document.createElement('span');\r
+        field.className='ricoEditLabel';\r
+        field.id='labelnew__'+column.format.FieldName;\r
+        field.innerHTML='&nbsp;&nbsp;&nbsp;New&nbsp;value:';\r
+        entry.appendChild(field);\r
+        name='textnew__'+column.format.FieldName;\r
+        field=RicoUtil.createFormField(entry,'input','text',name,name);\r
+        break;\r
+      case 'S':\r
+      case 'SL':\r
+        field=RicoUtil.createFormField(entry,'select',null,name);\r
+        if (column.format.isNullable)\r
+          this.addSelectOption(field,this.options.TableSelectNone,"(none)");\r
+        this.selectValuesRequest(field,column.format);\r
+        break;\r
+      default:\r
+        field=RicoUtil.createFormField(entry,'input','text',name,name);\r
+        if (column.format.Length) {\r
+          field.maxLength=column.format.Length;\r
+          field.size=Math.min(column.format.Length, this.options.maxDisplayLen);\r
+        }\r
+        field.value=column.format.ColData;\r
+        break;\r
+    }\r
+    if (field) {\r
+      if (column.format.SelectCtl)\r
+        RicoEditControls.applyTo(column,field);\r
+    }\r
+  },\r
+  \r
+  checkSelectNew: function(e) {\r
+    this.updateSelectNew(Event.element(e));\r
+  },\r
+  \r
+  updateSelectNew: function(SelObj) {\r
+    var vis=(SelObj.value==this.options.TableSelectNew) ? "" : "hidden";\r
+    $("labelnew__" + SelObj.id).style.visibility=vis\r
+    $("textnew__" + SelObj.id).style.visibility=vis\r
+  },\r
+\r
+  selectValuesRequest: function(elem,fldSpec) {\r
+    if (fldSpec.SelectValues) {\r
+      var valueList=fldSpec.SelectValues.split(',');\r
+      for (var i=0; i<valueList.length; i++)\r
+        this.addSelectOption(elem,valueList[i],valueList[i],i);\r
+    } else {\r
+      this.requestCount++;\r
+      var options={};\r
+      Object.extend(options, this.grid.buffer.ajaxOptions);\r
+      options.parameters = 'id='+fldSpec.FieldName+'&offset=0&page_size=-1';
+      options.onComplete = this.selectValuesUpdate.bind(this);
+      new Ajax.Request(this.grid.buffer.dataSource, options);
+      Rico.writeDebugMsg("selectValuesRequest: "+options.parameters);
+    }\r
+  },\r
+  \r
+  selectValuesUpdate: function(request) {\r
+    var response = request.responseXML.getElementsByTagName("ajax-response");
+    Rico.writeDebugMsg("selectValuesUpdate: "+request.status);
+    if (response == null || response.length != 1) return;
+    response=response[0];
+    var error = response.getElementsByTagName('error');
+    if (error.length > 0) {
+      Rico.writeDebugMsg("Data provider returned an error:\n"+RicoUtil.getContentAsString(error[0],this.grid.buffer.isEncoded));
+      alert(RicoTranslate.getPhrase("The request returned an error")+":\n"+RicoUtil.getContentAsString(error[0],this.grid.buffer.isEncoded));
+      return null;
+    }\r
+    response=response.getElementsByTagName('response')[0];\r
+    var id = response.getAttribute("id").slice(0,-8);
+    var rowsElement = response.getElementsByTagName('rows')[0];\r
+    var rows = this.grid.buffer.dom2jstable(rowsElement);\r
+    var elem=$(id);\r
+    //alert('selectValuesUpdate:'+id+' '+elem.tagName);
+    Rico.writeDebugMsg("selectValuesUpdate: id="+id+' rows='+rows.length);
+    for (var i=0; i<rows.length; i++) {\r
+      if (rows[i].length>0) {\r
+        var c0=rows[i][0].content;\r
+        var c1=(rows[i].length>1) ? rows[i][1].content : c0;\r
+        this.addSelectOption(elem,c0,c1,i);\r
+      }\r
+    }
+    if ($('textnew__'+id))\r
+      this.addSelectOption(elem,this.options.TableSelectNew,"(new value)");\r
+    if (this.panelGroup)\r
+      setTimeout(this.initPanelGroup.bind(this),50);\r
+  },\r
+  \r
+  addSelectOption: function(elem,value,text,idx) {\r
+    switch (elem.tagName.toLowerCase()) {\r
+      case 'div':\r
+        var opt=RicoUtil.createFormField(elem,'input','radio',elem.id+'_'+idx,elem.id);\r
+        opt.value=value;\r
+        var lbl=document.createElement('label');\r
+        lbl.innerHTML=text;\r
+        lbl.htmlFor=opt.id;\r
+        elem.appendChild(lbl);\r
+        break;\r
+      case 'select':\r
+        var opt=document.createElement('option');\r
+        opt.value=value;\r
+        opt.text=text;\r
+        //elem.options.add(opt);\r
+        if (Prototype.Browser.IE)\r
+          elem.add(opt);\r
+        else\r
+          elem.add(opt,null);\r
+        break;\r
+    }\r
+  },\r
+  \r
+  clearSaveMsg: function() {\r
+    if (this.saveMsg) this.saveMsg.innerHTML="";\r
+  },\r
+  \r
+  addMenuItem: function(menuText,menuAction,enabled) {\r
+    this.extraMenuItems.push({menuText:menuText,menuAction:menuAction,enabled:enabled});\r
+  },\r
+\r
+  editMenu: function(grid,r,c,onBlankRow) {\r
+    this.clearSaveMsg();\r
+    if (this.grid.buffer.sessionExpired==true || this.grid.buffer.startPos<0) return;\r
+    this.rowIdx=r;\r
+    var elemTitle=$('pageTitle');\r
+    var pageTitle=elemTitle ? elemTitle.innerHTML : document.title;\r
+    this.menu.addMenuHeading(pageTitle);\r
+    for (var i=0; i<this.extraMenuItems.length; i++) {\r
+      this.menu.addMenuItem(this.extraMenuItems[i].menuText,this.extraMenuItems[i].menuAction,this.extraMenuItems[i].enabled);\r
+    }\r
+    if (onBlankRow==false) {\r
+      this.menu.addMenuItem("Edit\t this "+this.options.RecordName,this.editRecord.bindAsEventListener(this),this.options.canEdit);\r
+      this.menu.addMenuItem("Delete\t this "+this.options.RecordName,this.deleteRecord.bindAsEventListener(this),this.options.canDelete);\r
+    }\r
+    this.menu.addMenuItem("Add\t new "+this.options.RecordName,this.addRecord.bindAsEventListener(this),this.options.canAdd);\r
+    return true;\r
+  },\r
+\r
+  cancelEdit: function(e) {\r
+    Event.stop(e);\r
+    for (var i=0; i<this.grid.columns.length; i++)\r
+      if (this.grid.columns[i].format && this.grid.columns[i].format.SelectCtl)\r
+        RicoEditControls.close(this.grid.columns[i].format.SelectCtl);\r
+    this.makeFormInvisible();\r
+    this.grid.highlightEnabled=true;\r
+    this.menu.cancelmenu();\r
+    return false;\r
+  },\r
+\r
+  setField: function(fldSpec,fldvalue) {\r
+    var e=$(fldSpec.FieldName);\r
+    if (!e) return;\r
+    //alert('setField: '+fldSpec.FieldName+'='+fldvalue);\r
+    switch (e.tagName.toUpperCase()) {\r
+      case 'DIV':\r
+        var elems=e.getElementsByTagName('INPUT');\r
+        var fldcode=this.getLookupValue(fldvalue)[0];\r
+        for (var i=0; i<elems.length; i++)\r
+          elems[i].checked=(elems[i].value==fldcode);\r
+        break;\r
+      case 'INPUT':\r
+        if (fldSpec.SelectCtl)\r
+          fldvalue=this.getLookupValue(fldvalue)[0];\r
+        switch (e.type.toUpperCase()) {\r
+          case 'HIDDEN':\r
+          case 'TEXT':\r
+            e.value=fldvalue;\r
+            break;\r
+        }\r
+        break;\r
+      case 'SELECT':\r
+        var opts=e.options;\r
+        var fldcode=this.getLookupValue(fldvalue)[0];\r
+        //alert('setField SELECT: id='+e.id+'\nvalue='+fldcode+'\nopt cnt='+opts.length)\r
+        for (var i=0; i<opts.length; i++) {\r
+          if (opts[i].value==fldcode) {\r
+            e.selectedIndex=i;\r
+            break;\r
+          }\r
+        }\r
+        if (fldSpec.EntryType=='N') {\r
+          var txt=$('textnew__'+e.id);\r
+          if (!txt) alert('Warning: unable to find id "textnew__'+e.id+'"');\r
+          txt.value=fldvalue;\r
+          if (e.selectedIndex!=i) e.selectedIndex=opts.length-1;\r
+          this.updateSelectNew(e);\r
+        }\r
+        return;\r
+      case 'TEXTAREA':\r
+        e.value=fldvalue;\r
+        if (fldSpec.EntryType=='tinyMCE' && typeof(tinyMCE)!='undefined' && this.initialized)\r
+          tinyMCE.updateContent(e.id);\r
+        return;\r
+    }\r
+  },\r
+  \r
+  getLookupValue: function(value) {\r
+    if (typeof value!='string')\r
+      return ['',''];\r
+    else if (value.match(/<span\s+class=(['"]?)ricolookup\1>(.*)<\/span>/i))\r
+      return [RegExp.$2,RegExp.leftContext];\r
+    else\r
+      return [value,value];\r
+  },\r
+  \r
+  // use with care: Prototype 1.5 does not include disabled fields in the post-back\r
+  setReadOnly: function(addFlag) {\r
+    for (var i=0; i<this.grid.columns.length; i++) {\r
+      var fldSpec=this.grid.columns[i].format;\r
+      if (!fldSpec) continue;\r
+      var e=$(fldSpec.FieldName);\r
+      if (!e) continue;\r
+      var ro=!fldSpec.Writeable || fldSpec.ReadOnly || (fldSpec.InsertOnly && !addFlag) || (fldSpec.UpdateOnly && addFlag);\r
+      var color=ro ? this.options.readOnlyColor : '';\r
+      switch (e.tagName.toUpperCase()) {\r
+        case 'DIV':\r
+          var elems=e.getElementsByTagName('INPUT');\r
+          for (var j=0; j<elems.length; j++)\r
+            elems[j].disabled=ro;\r
+          break;\r
+        case 'SELECT':\r
+          if (fldSpec.EntryType=='N') {\r
+            var txt=$('textnew__'+e.id);\r
+            txt.disabled=ro;\r
+          }\r
+          e.disabled=ro;\r
+          break;\r
+        case 'TEXTAREA':\r
+        case 'INPUT':\r
+          e.readOnly=ro;\r
+          e.style.color=color;\r
+          if (fldSpec.selectIcon) fldSpec.selectIcon.style.display=ro ? 'none' : '';\r
+          break;\r
+      }\r
+    }\r
+  },\r
+  \r
+  hideResponse: function(msg) {\r
+    this.responseDiv.innerHTML=msg;\r
+    this.responseDialog.style.display='none';\r
+  },\r
+  \r
+  showResponse: function() {\r
+    var offset=Position.page(this.grid.outerDiv);\r
+    offset[1]+=RicoUtil.docScrollTop();\r
+    this.responseDialog.style.top=offset[1]+"px";\r
+    this.responseDialog.style.display='';\r
+  },\r
+  \r
+  processResponse: function() {\r
+    var ch=this.responseDiv.childNodes;\r
+    for (var i=ch.length-1; i>=0; i--) {\r
+      if (ch[i].nodeType==1 && ch[i].nodeName!='P' && ch[i].nodeName!='DIV' && ch[i].nodeName!='BR')\r
+        this.responseDiv.removeChild(ch[i]);\r
+    }\r
+    var responseText=this.responseDiv.innerHTML;\r
+    if (responseText.toLowerCase().indexOf('error')==-1 && this.options.showSaveMsg!='full') {\r
+      this.hideResponse('');\r
+      this.grid.resetContents();\r
+      this.grid.buffer.foundRowCount = false;\r
+      this.grid.buffer.fetch(this.grid.lastRowPos || 0);\r
+      if (this.saveMsg) this.saveMsg.innerHTML='&nbsp;'+responseText.stripTags()+'&nbsp;';\r
+    }\r
+    this.processCallback(this.options.onSubmitResponse);\r
+  },\r
+  \r
+  processCallback: function(callback) {\r
+    switch (typeof callback) {\r
+      case 'string': eval(callback); break;\r
+      case 'function': callback(); break;\r
+    }\r
+  },\r
+  \r
+  // called when ok pressed on error response message\r
+  ackResponse: function() {\r
+    this.hideResponse('');\r
+    this.grid.highlightEnabled=true;\r
+  },\r
+\r
+  editRecord: function(e) {\r
+    this.grid.highlightEnabled=false;\r
+    this.menu.hidemenu();\r
+    this.hideResponse('Saving...');\r
+    this.grid.outerDiv.style.cursor = 'auto';\r
+    this.action.value="upd";\r
+    for (var i=0; i<this.grid.columns.length; i++) {\r
+      if (this.grid.columns[i].format) {\r
+        var v=this.grid.columns[i].getValue(this.rowIdx);\r
+        this.setField(this.grid.columns[i].format,v);\r
+        if (this.grid.columns[i].format.selectDesc)\r
+          this.grid.columns[i].format.selectDesc.innerHTML=this.grid.columns[i]._format(v);\r
+      }\r
+    }\r
+    this.setReadOnly(false);\r
+    this.key=this.getKey();\r
+    this.makeFormVisible(this.rowIdx);\r
+  },\r
+\r
+  addRecord: function() {\r
+    this.menu.hidemenu();\r
+    this.hideResponse('Saving...');\r
+    this.setReadOnly(true);\r
+    this.form.reset();\r
+    this.action.value="ins";\r
+    for (var i=0; i<this.grid.columns.length; i++) {\r
+      if (this.grid.columns[i].format) {\r
+        this.setField(this.grid.columns[i].format,this.grid.columns[i].format.ColData);\r
+        if (this.grid.columns[i].format.SelectCtl)\r
+          RicoEditControls.resetValue(this.grid.columns[i]);\r
+      }\r
+    }\r
+    this.key='';\r
+    this.makeFormVisible(-1);\r
+    if (this.Accordion) this.Accordion.selectionSet.selectIndex(0);\r
+  },\r
+  \r
+  drillDown: function(e,masterColNum,detailColNum) {\r
+    var cell=Event.element(e || window.event);\r
+    cell=RicoUtil.getParentByTagName(cell,'div','ricoLG_cell');\r
+    if (!cell) return;\r
+    this.grid.unhighlight();\r
+    var idx=this.grid.winCellIndex(cell);\r
+    this.grid.menuIdx=idx;  // ensures selection gets cleared when menu is displayed\r
+    this.grid.highlight(idx);\r
+    var drillValue=this.grid.columns[masterColNum].getValue(idx.row);\r
+    for (var i=3; i<arguments.length; i++)\r
+      arguments[i].setDetailFilter(detailColNum,drillValue);\r
+    return idx.row;\r
+  },\r
+  \r
+  // set filter on a detail grid that is in a master-detail relationship\r
+  setDetailFilter: function(colNumber,filterValue) {\r
+    var c=this.grid.columns[colNumber];\r
+    c.format.ColData=filterValue;\r
+    c.setSystemFilter('EQ',filterValue);\r
+  },\r
+  \r
+  makeFormVisible: function(row) {\r
+    this.editDiv.style.display='block';\r
+\r
+    // set left position\r
+    var editWi=this.editDiv.offsetWidth;\r
+    var odOffset=Position.page(this.grid.outerDiv);\r
+    var winWi=RicoUtil.windowWidth();\r
+    if (editWi+odOffset[0] > winWi)\r
+      this.editDiv.style.left=(winWi-editWi)+'px';\r
+    else\r
+      this.editDiv.style.left=(odOffset[0]+1)+'px';\r
+\r
+    // set top position\r
+    var scrTop=RicoUtil.docScrollTop();\r
+    var editHt=this.editDiv.offsetHeight;\r
+    var newTop=odOffset[1]+this.grid.hdrHt+scrTop;\r
+    var bottom=RicoUtil.windowHeight()+scrTop;\r
+    if (row >= 0) {\r
+      newTop+=(row+1)*this.grid.rowHeight;\r
+      if (newTop+editHt>bottom) newTop-=(editHt+this.grid.rowHeight);\r
+    } else {\r
+      if (newTop+editHt>bottom) newTop=bottom-editHt;\r
+    }\r
+    this.processCallback(this.options.formOpen);\r
+    this.formPopup.openPopup(null,Math.max(newTop,scrTop));\r
+    this.editDiv.style.visibility='visible';\r
+    if (this.initialized) return;\r
+    for (i = 0; i < this.grid.columns.length; i++) {\r
+      spec=this.grid.columns[i].format;\r
+      if (!spec || !spec.EntryType || !spec.FieldName) continue;\r
+      switch (spec.EntryType) {\r
+        case 'tinyMCE':\r
+          if (typeof tinyMCE!='undefined') tinyMCE.execCommand('mceAddControl', true, spec.FieldName);\r
+          break;\r
+      }\r
+    }\r
+    this.formPopup.openPopup();  // tinyMCE may have changed the dimensions of the form\r
+    this.initialized=true;\r
+  },\r
+\r
+  makeFormInvisible: function() {\r
+    this.editDiv.style.visibility='hidden';\r
+    this.formPopup.closePopup();\r
+    this.processCallback(this.options.formClose);\r
+  },\r
+  \r
+  getConfirmDesc: function(rowIdx) {\r
+    var desc=this.grid.columns[this.options.ConfirmDeleteCol].cell(rowIdx).innerHTML;\r
+    desc=this.getLookupValue(desc)[1];\r
+    return desc.stripTags();\r
+  },\r
+\r
+  deleteRecord: function() {\r
+    this.menu.hidemenu();\r
+    var desc;\r
+    if (this.options.ConfirmDeleteCol < 0) {\r
+      desc=RicoTranslate.getPhrase("this "+this.options.RecordName);\r
+    } else {\r
+      desc=this.getConfirmDesc(this.rowIdx);\r
+      if (desc.length>50) desc=desc.substring(0,50)+'...';\r
+      desc='\"' + desc + '\"'\r
+    }\r
+    if (!this.options.ConfirmDelete.valueOf || confirm(RicoTranslate.getPhrase("Are you sure you want to delete ") + desc + " ?")) {\r
+      this.hideResponse('Deleting...');\r
+      this.showResponse();\r
+      var parms=this.action.name+"=del"+this.getKey();\r
+      //alert(parms);\r
+      new Ajax.Updater(this.responseDiv, window.location.pathname, {parameters:parms,onComplete:this.processResponse.bind(this)});\r
+    }\r
+    this.menu.cancelmenu();\r
+  },\r
+  \r
+  getKey: function() {\r
+    var key='';\r
+    for (var i=0; i<this.grid.columns.length; i++) {\r
+      if (this.grid.columns[i].format && this.grid.columns[i].format.isKey) {\r
+        var value=this.grid.columns[i].getValue(this.rowIdx);\r
+        value=this.getLookupValue(value)[0];\r
+        key+='&_k'+i+'='+value;\r
+      }\r
+    }\r
+    return key;\r
+  },\r
+\r
+  TESubmit: function(e) {\r
+    var i,lbl,spec,elem,entrytype;\r
+    \r
+    if (!e) e=window.event;\r
+    Event.stop(e);\r
+\r
+    // check fields that are supposed to be non-blank\r
+\r
+    for (i = 0; i < this.grid.columns.length; i++) {\r
+      spec=this.grid.columns[i].format;\r
+      if (!spec || !spec.EntryType || !spec.FieldName) continue;\r
+      entrytype=spec.EntryType.charAt(0).toLowerCase();\r
+      if (!entrytype.match(/d|i|b/)) continue;\r
+      if (spec.isNullable==true && entrytype!='b') continue;\r
+      elem=$(spec.FieldName);\r
+      if (!elem) continue;\r
+      //alert("nonblank check: " + spec.FieldName);\r
+      if (elem.tagName.toLowerCase()!='input') continue;\r
+      if (elem.type.toLowerCase()!='text') continue;\r
+      if (elem.value.length == 0) {\r
+        alert(RicoTranslate.getPhrase("Please enter\t a value for")+" \"" + this.grid.columns[i].formLabel.innerHTML + "\"");\r
+        //setTimeout("FocusField(document." + this.form.name + "." + this.options.NonBlanks[i] + ")",2000);\r
+        return false;\r
+      }\r
+    }\r
+\r
+    // recheck any elements on the form with an onchange event\r
+\r
+    var InputFields = this.form.getElementsByTagName("input");\r
+    this.TEerror=false;\r
+    for (i=0; i < InputFields.length; i++) {\r
+      if (InputFields[i].type=="text" && InputFields[i].onchange) {\r
+        InputFields[i].onchange();\r
+        if (this.TEerror) return false;\r
+      }\r
+    }\r
+    if (typeof tinyMCE!='undefined') tinyMCE.triggerSave();\r
+    this.makeFormInvisible();\r
+    this.showResponse();\r
+    var parms=Form.serialize(this.form)+this.key\r
+    Rico.writeDebugMsg("TESubmit:"+parms);\r
+    new Ajax.Updater(this.responseDiv, window.location.pathname, {parameters:parms,onComplete:this.responseHandler});\r
+    this.menu.cancelmenu();\r
+    return false;\r
+  },\r
+  \r
+  FocusField: function(elem) {\r
+    elem.focus();\r
+    elem.select();\r
+  },\r
+\r
+  TableEditCheckInt: function(TxtObj) {\r
+    var val=TxtObj.value;\r
+    if (val=='') return;\r
+    if (val!=parseInt(val)) {\r
+      alert(RicoTranslate.getPhrase("Please enter\t an integer value for")+" \"" + $("lbl_"+TxtObj.id).innerHTML + "\"");\r
+      setTimeout(this.FocusField.bind(this,TxtObj),0);\r
+      this.TEerror=true;\r
+    }\r
+  },\r
+\r
+  TableEditCheckPosInt: function(TxtObj) {\r
+    var val=TxtObj.value;\r
+    if (val=='') return;\r
+    if (val!=parseInt(val) || val<0) {\r
+      alert(RicoTranslate.getPhrase("Please enter\t a positive integer value for")+" \"" + $("lbl_"+TxtObj.id).innerHTML + "\"");\r
+      setTimeout(this.FocusField.bind(this,TxtObj),0);\r
+      this.TEerror=true;\r
+    }\r
+  }\r
+}\r
+\r
+\r
+// Registers custom popup widgets to fill in a text box (e.g. ricoCalendar and ricoTree)\r
+//\r
+// Custom widget must implement:\r
+//   open() method (make control visible)\r
+//   close() method (hide control)\r
+//   container property (div element that contains the control)\r
+//   id property (uniquely identifies the widget class)\r
+//\r
+// widget calls returnValue method to return a value to the caller\r
+//\r
+// this object handles clicks on the control's icon and positions the control appropriately.\r
+var RicoEditControls = {\r
+  widgetList:$H(),\r
+  elemList:$H(),\r
+  \r
+  register: function(widget, imgsrc) {\r
+    var tmp={};\r
+    tmp[widget.id]={imgsrc:imgsrc, widget:widget, currentEl:''};\r
+    this.widgetList=this.widgetList.merge(tmp);\r
+    widget.returnValue=this.setValue.bind(this,widget);\r
+    Rico.writeDebugMsg("RicoEditControls.register:"+widget.id);\r
+  },\r
+  \r
+  atLoad: function() {\r
+    var k=this.widgetList.keys();\r
+    for (var i=0; i<k.length; i++) {\r
+      var w=this.widgetList[k[i]].widget;\r
+      if (w.atLoad) w.atLoad();\r
+    }\r
+  },\r
+  \r
+  applyTo: function(column,inputCtl) {\r
+    var wInfo=this.widgetList[column.format.SelectCtl];\r
+    if (!wInfo) return null;\r
+    Rico.writeDebugMsg('RicoEditControls.applyTo: '+column.displayName+' : '+column.format.SelectCtl);\r
+    var descSpan = document.createElement('span');
+    var newimg = document.createElement('img');
+    newimg.style.paddingLeft='4px';
+    newimg.style.cursor='pointer';\r
+    newimg.align='top';
+    newimg.src=wInfo.imgsrc;\r
+    newimg.id=this.imgId(column.format.FieldName);\r
+    newimg.onclick=this.processClick.bindAsEventListener(this);\r
+    inputCtl.parentNode.appendChild(descSpan);
+    inputCtl.parentNode.appendChild(newimg);
+    inputCtl.style.display='none';    // comment out this line for debugging\r
+    var tmp=new Object();\r
+    tmp[newimg.id]={descSpan:descSpan, inputCtl:inputCtl, widget:wInfo.widget, listObj:wInfo, column:column};\r
+    this.elemList=this.elemList.merge(tmp);\r
+    column.format.selectIcon=newimg;\r
+    column.format.selectDesc=descSpan;\r
+  },\r
+\r
+  processClick: function(e) {\r
+    var elem=Event.element(e);\r
+    var el=this.elemList[elem.id];\r
+    if (!el) return;\r
+    if (el.listObj.currentEl==elem.id && el.widget.container.style.display!='none') {\r
+      el.widget.close();\r
+      el.listObj.currentEl='';\r
+    } else {\r
+      el.listObj.currentEl=elem.id;\r
+      Rico.writeDebugMsg('RicoEditControls.processClick: '+el.widget.id+' : '+el.inputCtl.value);\r
+      RicoUtil.positionCtlOverIcon(el.widget.container,elem);\r
+      el.widget.open(el.inputCtl.value);\r
+    }\r
+  },\r
+  \r
+  imgId: function(fieldname) {\r
+    return 'icon_'+fieldname;\r
+  },\r
+  \r
+  resetValue: function(column) {\r
+    var el=this.elemList[this.imgId(column.format.FieldName)];\r
+    if (!el) return;\r
+    el.inputCtl.value=column.format.ColData;\r
+    el.descSpan.innerHTML=column._format(column.format.ColData);\r
+  },\r
+  \r
+  setValue: function(widget,newVal,newDesc) {\r
+    var wInfo=this.widgetList[widget.id];\r
+    if (!wInfo) return null;\r
+    var id=wInfo.currentEl;\r
+    if (!id) return null;\r
+    var el=this.elemList[id];\r
+    if (!el) return null;\r
+    el.inputCtl.value=newVal;\r
+    if (!newDesc) newDesc=el.column._format(newVal);\r
+    el.descSpan.innerHTML=newDesc;\r
+    //alert(widget.id+':'+id+':'+el.inputCtl.id+':'+el.inputCtl.value+':'+newDesc);\r
+  },\r
+  \r
+  close: function(id) {\r
+    var wInfo=this.widgetList[id];\r
+    if (!wInfo) return;\r
+    if (wInfo.widget.container.style.display!='none')\r
+      wInfo.widget.close();\r
+  }\r
+}\r
+\r
+Rico.includeLoaded('ricoLiveGridForms.js');
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoLiveGridMenu.js b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoLiveGridMenu.js
new file mode 100644 (file)
index 0000000..6815fa7
--- /dev/null
@@ -0,0 +1,110 @@
+if(typeof Rico=='undefined')\r
+  throw("GridMenu requires the Rico JavaScript framework");\r
+\r
+\r
+/**\r
+ * Standard menu for LiveGrid\r
+ */\r
+Rico.GridMenu = Class.create();
+
+Rico.GridMenu.prototype = {
+
+initialize: function(options) {
+  this.options = {\r
+    width           : '20em',\r
+    dataMenuHandler : null          // put custom items on the menu\r
+  };\r
+  Object.extend(this.options, options || {});\r
+  Object.extend(this, new Rico.Menu(this.options));\r
+  this.sortmenu = new Rico.Menu(this.options);\r
+  this.filtermenu = new Rico.Menu(this.options);\r
+  this.exportmenu = new Rico.Menu(this.options);\r
+  this.hideshowmenu = new Rico.Menu(this.options);\r
+  this.createDiv();\r
+  this.sortmenu.createDiv();\r
+  this.filtermenu.createDiv();\r
+  this.exportmenu.createDiv();\r
+  this.hideshowmenu.createDiv();\r
+},\r
+\r
+// Build context menu for grid\r
+buildGridMenu: function(r,c) {\r
+  this.clearMenu();\r
+  var totrows=this.liveGrid.buffer.totalRows;\r
+  var onBlankRow=r >= totrows;\r
+  var column=this.liveGrid.columns[c];\r
+  if (this.options.dataMenuHandler) {\r
+     var showMenu=this.options.dataMenuHandler(this.liveGrid,r,c,onBlankRow);\r
+     if (!showMenu) return false;\r
+  }\r
+\r
+  // menu items for sorting\r
+  if (column.sortable && totrows>0) {\r
+    this.sortmenu.clearMenu();\r
+    this.addSubMenuItem(RicoTranslate.getPhrase("Sort by")+": "+column.displayName, this.sortmenu, false);\r
+    this.sortmenu.addMenuItem("Ascending", column.sortAsc.bind(column), true);\r
+    this.sortmenu.addMenuItem("Descending", column.sortDesc.bind(column), true);\r
+  }\r
+\r
+  // menu items for filtering\r
+  if (column.canFilter() && (!onBlankRow || column.filterType == Rico.TableColumn.USERFILTER)) {\r
+    this.filtermenu.clearMenu();\r
+    this.addSubMenuItem(RicoTranslate.getPhrase("Filter by")+": "+column.displayName, this.filtermenu, false);    \r
+    column.userFilter=column.getValue(r);\r
+    if (column.filterType == Rico.TableColumn.USERFILTER) {\r
+      this.filtermenu.addMenuItem("Remove filter", column.setUnfiltered.bind(column), true);\r
+      this.filtermenu.addMenuItem("Refresh", this.liveGrid.filterHandler.bind(this.liveGrid), true);\r
+      if (column.filterOp=='LIKE')\r
+        this.filtermenu.addMenuItem("Change keyword...", column.setFilterKW.bind(column), true);\r
+      if (column.filterOp=='NE' && !onBlankRow)\r
+        this.filtermenu.addMenuItem("Exclude this value also", column.addFilterNE.bind(column), true);\r
+    } else if (!onBlankRow) {\r
+      this.filtermenu.addMenuItem("Include only this value", column.setFilterEQ.bind(column), true);\r
+      this.filtermenu.addMenuItem("Greater than or equal to this value", column.setFilterGE.bind(column), column.userFilter!='');\r
+      this.filtermenu.addMenuItem("Less than or equal to this value", column.setFilterLE.bind(column), column.userFilter!='');\r
+      if (column.isText)\r
+        this.filtermenu.addMenuItem("Contains keyword...", column.setFilterKW.bind(column), true);\r
+      this.filtermenu.addMenuItem("Exclude this value", column.setFilterNE.bind(column), true);\r
+    }\r
+    if (this.liveGrid.filterCount() > 0)\r
+      this.filtermenu.addMenuItem("Remove all filters", this.liveGrid.clearFilters.bind(this.liveGrid), true);\r
+  }\r
+\r
+  // menu items for Print/Export\r
+  if (this.liveGrid.options.maxPrint > 0 && totrows>0) {\r
+    this.exportmenu.clearMenu();\r
+    this.addSubMenuItem('Print\t/Export',this.exportmenu);\r
+    this.exportmenu.addMenuItem("Visible rows to web page", this.liveGrid.printVisible.bind(this.liveGrid,'plain'), true);\r
+    this.exportmenu.addMenuItem("All rows to web page", this.liveGrid.printAll.bind(this.liveGrid,'plain'), this.liveGrid.buffer.totalRows <= this.liveGrid.options.maxPrint);\r
+    if (Prototype.Browser.IE) {\r
+      this.exportmenu.addMenuBreak();\r
+      this.exportmenu.addMenuItem("Visible rows to spreadsheet", this.liveGrid.printVisible.bind(this.liveGrid,'owc'), true);\r
+      this.exportmenu.addMenuItem("All rows to spreadsheet", this.liveGrid.printAll.bind(this.liveGrid,'owc'), this.liveGrid.buffer.totalRows <= this.liveGrid.options.maxPrint);\r
+    }\r
+  }\r
+\r
+  // menu items for hide/unhide\r
+  var hiddenCols=this.liveGrid.listInvisible();\r
+  for (var showableCnt=0,x=0; x<hiddenCols.length; x++)\r
+    if (hiddenCols[x].canHideShow()) showableCnt++;\r
+  if (showableCnt > 0 || column.canHideShow()) {\r
+    this.hideshowmenu.clearMenu();\r
+    this.addSubMenuItem('Hide\t/Show',this.hideshowmenu);\r
+    var visibleCnt=this.liveGrid.columns.length-hiddenCols.length;\r
+    var enabled=(visibleCnt>1 && column.visible && column.canHideShow());\r
+    this.hideshowmenu.addMenuItem(RicoTranslate.getPhrase('Hide')+': '+column.displayName, column.hideColumn.bind(column), enabled);\r
+    for (var cnt=0,x=0; x<hiddenCols.length; x++) {\r
+      if (hiddenCols[x].canHideShow()) {\r
+        if (cnt++==0) this.hideshowmenu.addMenuBreak();\r
+        this.hideshowmenu.addMenuItem(RicoTranslate.getPhrase('Show')+': '+hiddenCols[x].displayName, hiddenCols[x].showColumn.bind(hiddenCols[x]));\r
+      }\r
+    }\r
+    if (hiddenCols.length > 1)\r
+      this.hideshowmenu.addMenuItem(RicoTranslate.getPhrase('Show All'), this.liveGrid.showAll.bind(this.liveGrid));\r
+  }\r
+  return true;\r
+}\r
+\r
+}\r
+\r
+Rico.includeLoaded('ricoLiveGridMenu.js');
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoMenu.js b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoMenu.js
new file mode 100644 (file)
index 0000000..544d3e8
--- /dev/null
@@ -0,0 +1,197 @@
+Rico.Menu = Class.create();
+
+Rico.Menu.prototype = {
+
+  initialize: function(options) {
+    Object.extend(this, new Rico.Popup());
+    Object.extend(this.options, {
+      width        : "15em",
+      margin       : 6   // account for shadow
+    });
+    if (typeof options=='string')
+      this.options.width=options;
+    else
+      Object.extend(this.options, options || {});
+    this.hideFunc=null;
+    this.highlightElem=null;
+    new Image().src = Rico.imgDir+'left.gif';
+    new Image().src = Rico.imgDir+'right.gif';
+  },
+  
+  createDiv: function(parentNode) {
+    if (this.div) return;
+    this.div = document.createElement('div');
+    this.div.className = Prototype.Browser.WebKit ? 'ricoMenuSafari' : 'ricoMenu';
+    this.div.style.position="absolute";
+    this.div.style.width=this.options.width;
+    if (!parentNode) parentNode = document.getElementsByTagName("body")[0];
+    parentNode.appendChild(this.div);
+    this.width=this.div.offsetWidth
+    this.setDiv(this.div,this.cancelmenu.bindAsEventListener(this));
+    this.direction=Element.getStyle(this.div,'direction') || 'ltr';
+    this.direction=this.direction.toLowerCase();  // ltr or rtl
+    this.hidemenu();
+    this.itemCount=0;
+  },
+  
+  showmenu: function(e,hideFunc){
+    Event.stop(e);
+    this.hideFunc=hideFunc;
+    if (this.div.childNodes.length==0) {
+      this.cancelmenu();
+      return false;
+    }
+    this.openmenu(e.clientX,e.clientY,0,0);
+  },
+  
+  openmenu: function(x,y,clickItemWi,clickItemHt) {
+    var newLeft=RicoUtil.docScrollLeft()+x;
+    //window.status='openmenu: newLeft='+newLeft+' width='+this.width+' windowWi='+RicoUtil.windowWidth();
+    if (this.direction == 'rtl') {
+      if (newLeft > this.width+clickItemWi) newLeft-=this.width+clickItemWi;
+    } else {
+      if (x+this.width+this.options.margin > RicoUtil.windowWidth()) newLeft-=this.width+clickItemWi;
+    }
+    var newTop=RicoUtil.docScrollTop()+y;
+    this.div.style.visibility="hidden";
+    this.div.style.display="block";
+    var contentHt=this.div.offsetHeight;
+    if (y+contentHt+this.options.margin > RicoUtil.windowHeight())
+      newTop=Math.max(newTop-contentHt+clickItemHt,0);
+    this.openPopup(newLeft,newTop);
+    this.div.style.visibility ="visible";
+    return false;
+  },
+
+  clearMenu: function() {
+    this.div.innerHTML="";
+    this.defaultAction=null;
+    this.itemCount=0;
+  },
+
+  addMenuHeading: function(hdg,translate) {
+    var el=document.createElement('div')
+    el.innerHTML =(translate==null || translate==true) ? RicoTranslate.getPhrase(hdg) : hdg;
+    el.className='ricoMenuHeading';
+    this.div.appendChild(el);
+  },
+
+  addMenuBreak: function() {
+    var brk=document.createElement('div');
+    brk.className="ricoMenuBreak";
+    this.div.appendChild(brk);
+  },
+
+  addSubMenuItem: function(menutext, submenu, translate) {
+    var dir=this.direction=='rtl' ? 'left' : 'right';
+    var a=this.addMenuItem(menutext,null,true,null,translate);
+    a.className='ricoSubMenu';
+    a.style.backgroundImage='url('+Rico.imgDir+dir+'.gif)';
+    a.style.backgroundRepeat='no-repeat';
+    a.style.backgroundPosition=dir;
+    a.onmouseover=this.showSubMenu.bind(this,a,submenu);
+    a.onmouseout=this.subMenuOut.bindAsEventListener(this);
+  },
+  
+  showSubMenu: function(a,submenu) {
+    if (this.openSubMenu) this.hideSubMenu();
+    this.openSubMenu=submenu;
+    this.openMenuAnchor=a;
+    var pos=Position.page(a);
+    if (a.className=='ricoSubMenu') a.className='ricoSubMenuOpen';
+    submenu.openmenu(pos[0]+a.offsetWidth, pos[1], a.offsetWidth-2, a.offsetHeight+2);
+  },
+  
+  subMenuOut: function(e) {
+    if (!this.openSubMenu) return;
+    Event.stop(e);
+    var elem=Event.element(e);
+    var reltg = (e.relatedTarget) ? e.relatedTarget : e.toElement;
+    try {
+      while (reltg != null && reltg != this.openSubMenu.div)
+        reltg=reltg.parentNode;
+    } catch(err) {}
+    if (reltg == this.openSubMenu.div) return;
+    this.hideSubMenu();
+  },
+  
+  hideSubMenu: function() {
+    if (this.openMenuAnchor) {
+      this.openMenuAnchor.className='ricoSubMenu';
+      this.openMenuAnchor=null;
+    }
+    if (this.openSubMenu) {
+      this.openSubMenu.hidemenu();
+      this.openSubMenu=null;
+    }
+  },
+
+  addMenuItem: function(menutext,action,enabled,title,translate,target) {
+    this.itemCount++;
+    if (translate==null) translate=true;
+    var a = document.createElement(typeof action=='string' ? 'a' : 'div');
+    if ( arguments.length < 3 || enabled ) {
+      switch (typeof action) {
+        case 'function': 
+          a.onclick = action; 
+          break;
+        case 'string'  : 
+          a.href = action; 
+          if (target) a.target = target; 
+          break
+      }
+      a.className = 'enabled';
+      if (this.defaultAction==null) this.defaultAction=action;
+    } else {
+      a.disabled = true;
+      a.className = 'disabled';
+    }
+    a.innerHTML = translate ? RicoTranslate.getPhrase(menutext) : menutext;
+    if (typeof title=='string')
+      a.title = translate ? RicoTranslate.getPhrase(title) : title;
+    a=this.div.appendChild(a);
+    Event.observe(a,"mouseover", this.mouseOver.bindAsEventListener(this));
+    Event.observe(a,"mouseout", this.mouseOut.bindAsEventListener(this));
+    return a;
+  },
+  
+  mouseOver: function(e) {
+    if (this.highlightElem && this.highlightElem.className=='enabled-hover') {
+      // required for Safari
+      this.highlightElem.className='enabled';
+      this.highlightElem=null;
+    }
+    var elem=Event.element(e);
+    if (this.openMenuAnchor && this.openMenuAnchor!=elem)
+      this.hideSubMenu();
+    if (elem.className=='enabled') {
+      elem.className='enabled-hover';
+      this.highlightElem=elem;
+    }
+  },
+
+  mouseOut: function(e) {
+    var elem=Event.element(e);
+    if (elem.className=='enabled-hover') elem.className='enabled';
+    if (this.highlightElem==elem) this.highlightElem=null;
+  },
+
+  isVisible: function() {
+    return this.div && Element.visible(this.div);
+  },
+  
+  cancelmenu: function() {
+    if (this.hideFunc) this.hideFunc();
+    this.hideFunc=null;
+    this.hidemenu();
+  },
+
+  hidemenu: function() {
+    if (!this.div) return;
+    if (this.openSubMenu) this.openSubMenu.hidemenu();
+    this.closePopup();
+  }
+
+};
+
+Rico.includeLoaded('ricoMenu.js');
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoSheet.js b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoSheet.js
new file mode 100644 (file)
index 0000000..97faef6
--- /dev/null
@@ -0,0 +1,1577 @@
+Object.extend(Rico.SimpleGrid.prototype, {
+
+initSheet: function() {
+  this.highlightDiv=[];
+  for (var i=0; i<4; i++) {
+    this.highlightDiv[i] = this.createDiv("highlight",this.scrollDiv);
+    this.highlightDiv[i].style.display="none";
+    this.highlightDiv[i].id+=i;
+    this.highlightDiv[i].style[i % 2==0 ? 'height' : 'width']="0px";
+  }
+  for (var c=1; c<this.columns.length; c++) {
+    var col=this.columns[c];
+    for (var r=0; r<col.numRows(); r++) {
+      var cell=col.cell(r);
+      cell.RicoRow=r+1;
+      cell.RicoCol=c;
+      cell.RicoValue=null;
+    }
+  }
+  if (this.menu) {
+    if (!this.menu.grid) this.registerScrollMenu(this.menu);
+    this.menu.showmenu=this.menu.showSheetMenu;
+  }
+  this.inputArea=RicoUtil.createFormField(this.scrollDiv,'textarea',null,'inputArea');
+  this.inputArea.style.position='absolute';
+  this.inputArea.style.display='none';
+  this.inputArea.style.zIndex=2;
+  this.inputArea.cols=30;
+  this.inputArea.rows=4;
+  this.inputArea.blur();
+  this.clipBox=RicoUtil.createFormField(this.innerDiv,'textarea',null,'clipBox');
+  this.clipBox.style.position='absolute';
+  this.clipBox.style.display='none';
+  this.clipBox.cols=80;
+  this.clipBox.rows=10;
+  this.clipBox.style.top='0px';
+  this.clipBox.style.left='0px';
+  this.selectCellRC(0,1);
+  this.mouseOverHandler = this.selectMouseOver.bindAsEventListener(this);
+  this.mouseUpHandler  = this.selectMouseUp.bindAsEventListener(this);
+  Event.observe(this.inputArea,'keydown',this.inputKeydown.bindAsEventListener(this),false);
+  Event.observe(Prototype.Browser.IE ? document.body : window,'keydown',this.gridKeydown.bindAsEventListener(this),false);
+  Event.observe(this.tbody[1],"mousedown", this.selectMouseDown.bindAsEventListener(this), false);
+
+  // disable drag & select events in IE
+  this.outerDiv.ondrag = this.disableEvent;
+  this.outerDiv.onselectstart = this.disableEvent;
+  this.tbody[1].ondrag = this.disableEvent;
+  this.tbody[1].onselectstart = this.disableEvent;
+},
+
+disableEvent: function(e) {
+  e=e || event;
+  Event.stop(e);
+  return false;
+},
+
+cellIndex: function(cell) {
+  var a=cell.id.split(/_/);
+  var l=a.length;
+  var r=parseInt(a[l-2]);
+  var c=parseInt(a[l-1]);
+  return {row:r, column:c, tabIdx:this.columns[c].tabIdx, cell:cell};
+},
+
+AdjustSelection: function(cell) {
+  var newIdx=this.cellIndex(cell);
+  if (this.SelectIdxStart.tabIdx != newIdx.tabIdx) return;
+  this.HideSelection();
+  this.SelectIdxEnd=newIdx;
+  this.ShowSelection();
+},
+
+selectMouseDown: function(e) {
+  if (this.highlightEnabled==false) return true;
+  this.cancelMenu();
+  var cell=Event.element(e);
+  Event.stop(e);
+  if (!Event.isLeftClick(e)) return;
+  cell=RicoUtil.getParentByTagName(cell,'div','ricoLG_cell');
+  if (!cell) return;
+  var newIdx=this.cellIndex(cell);
+  if (e.shiftKey) {
+    if (!this.SelectIdxStart) return;
+    this.selectCellRC(newIdx.row,newIdx.column,true);
+  } else {
+    this.selectCellRC(newIdx.row,newIdx.column,false);
+    this.pluginSelect();
+  }
+},
+
+pluginSelect: function() {
+  if (this.selectPluggedIn) return;
+  var tBody=this.tbody[this.SelectIdxStart.tabIdx];
+  Event.observe(tBody,"mouseover", this.mouseOverHandler, false);
+  Event.observe(this.outerDiv,"mouseup",  this.mouseUpHandler,  false);
+  this.selectPluggedIn=true;
+},
+
+unplugSelect: function() {
+  var tBody=this.tbody[this.SelectIdxStart.tabIdx];
+  Event.stopObserving(tBody,"mouseover", this.mouseOverHandler , false);
+  Event.stopObserving(this.outerDiv,"mouseup", this.mouseUpHandler , false);
+  this.selectPluggedIn=false;
+},
+
+selectMouseUp: function(e) {
+  this.unplugSelect();
+  var cell=Event.element(e);
+  cell=RicoUtil.getParentByTagName(cell,'div','ricoLG_cell');
+  if (!cell) return;
+  this.AdjustSelection(cell);
+},
+
+selectMouseOver: function(e) {
+  var cell=Event.element(e);
+  cell=RicoUtil.getParentByTagName(cell,'div','ricoLG_cell');
+  if (!cell) return;
+  this.AdjustSelection(cell);
+  Event.stop(e);
+},
+
+getSelection: function() {
+  if (!this.SelectIdxStart || !this.SelectIdxEnd) return false;
+  var r1=Math.min(this.SelectIdxEnd.row,this.SelectIdxStart.row);
+  var r2=Math.max(this.SelectIdxEnd.row,this.SelectIdxStart.row);
+  var c1=Math.min(this.SelectIdxEnd.column,this.SelectIdxStart.column);
+  var c2=Math.max(this.SelectIdxEnd.column,this.SelectIdxStart.column);
+  return {r1:r1,c1:c1,r2:r2,c2:c2};
+},
+
+updateSelectOutline: function() {
+  var s=this.getSelection();
+  if (!s || s.r1 > s.r2) {
+    this.HideSelection();
+    return;
+  }
+  var top1=this.columns[s.c1].cell(s.r1).offsetTop;
+  var cell2=this.columns[s.c1].cell(s.r2);
+  var bottom2=cell2.offsetTop+cell2.offsetHeight;
+  var left1=this.columns[s.c1].dataCell.offsetLeft;
+  var left2=this.columns[s.c2].dataCell.offsetLeft;
+  var right2=left2+this.columns[s.c2].dataCell.offsetWidth;
+  //window.status='updateSelectOutline: '+s.r1+' '+s.r2+' top='+top1+' bot='+bottom2;
+  this.highlightDiv[0].style.top=this.highlightDiv[3].style.top=this.highlightDiv[1].style.top=(top1-3) + 'px';
+  this.highlightDiv[2].style.top=(bottom2-2)+'px';
+  this.highlightDiv[3].style.left=(left1-2)+'px';
+  this.highlightDiv[0].style.left=this.highlightDiv[2].style.left=(left1-1)+'px';
+  this.highlightDiv[1].style.left=(right2-1)+'px';
+  this.highlightDiv[0].style.width=this.highlightDiv[2].style.width=(right2-left1-1) + 'px';
+  this.highlightDiv[1].style.height=this.highlightDiv[3].style.height=(bottom2-top1) + 'px';
+  for (var i=0; i<4; i++)
+    this.highlightDiv[i].style.display='';
+},
+
+isSelected: function(r,c) {
+  var s=this.getSelection();
+  return s ? (s.r1 <= r) && (r <= s.r2) && (s.c1 <= c) && (c <= s.c2) : false;
+},
+
+HideSelection: function(cellList) {
+  for (var i=0; i<4; i++)
+    this.highlightDiv[i].style.display='none';
+},
+
+ShowSelection: function() {
+  this.updateSelectOutline();
+},
+
+/*
+ * @param what valid values are: null, 'all', 'formats', 'formulas', 'values'
+ */
+clearSelection: function() {
+  var s=this.getSelection();
+  if (!s) return;
+  var args=$A(arguments);
+  var what=args.shift();
+  if (typeof what=='object') what=args.shift();  // in case first arg is an event object
+  var v=(!what || what=='all') ? 1 : 0;
+  var whatobj={formats:v,formulas:v,values:v};
+  if (typeof what=='string') whatobj[what]=1;
+  if (whatobj.values) whatobj.formulas=1;
+  for (var r=s.r1; r<=s.r2; r++) {
+    for (var c=s.c1; c<=s.c2; c++) {
+      var gridcell=this.columns[c].cell(r);
+      if (whatobj.formats) {
+        gridcell.style.cssText='';
+        gridcell.RicoFormat={};
+      }
+      if (whatobj.formulas) gridcell.RicoFormula=null;
+      if (whatobj.values) gridcell.RicoValue=null;
+      this.formatCell(gridcell);
+    }
+  }
+},
+
+selectCellRC: function(r,c,adjFlag) {
+  if (r < 0 || r >= this.columns[0].numRows()) return;
+  this.HideSelection();
+  if (adjFlag) {
+    if (this.SelectIdxStart.tabIdx == this.columns[c].tabIdx)
+      this.SelectIdxEnd={row:r, column:c, tabIdx:this.columns[c].tabIdx};
+  } else {
+    this.SelectIdxStart=this.SelectIdxEnd={row:r, column:c, tabIdx:this.columns[c].tabIdx};
+    this.columns[c].cell(r).focus(); // causes IE to scroll cell into view (but not FF)
+  }
+  this.ShowSelection();
+},
+
+moveSelection: function(dr,dc,adjFlag,e) {
+  var selIdx=adjFlag ? this.SelectIdxEnd : this.SelectIdxStart;
+  var newr=selIdx.row+dr;
+  var newc=selIdx.column+dc;
+  if (newr>=0 && newr<this.columns[0].numRows() && newc>=1 && newc<this.columns.length)
+    this.selectCellRC(newr,newc,adjFlag);
+  if (e) Event.stop(e);
+},
+
+formatCell: function(cell) {
+  // TO DO: add currency/date formatting here
+  var v=cell.RicoValue;
+  if (v==null)
+    v='';
+  else if (typeof(v)=='number')
+    v = isNaN(v) ? '#VALUE' : cell.RicoFormat ? v.formatNumber(cell.RicoFormat) : v.toString();
+  else if (typeof v!='string')
+    v=v.toString();
+  v=v.replace(/^(\s*)/, '');
+  cell.style.paddingLeft=(RegExp.$1.length/2)+'em';
+  cell.innerHTML = v;
+},
+
+// action='add' or 'remove'
+updateDependencies: function(formulaCell,action) {
+  if (!formulaCell.RicoFormula) return;
+  //alert('updateDependencies '+action+': '+formulaCell.RicoRow+','+formulaCell.RicoCol);
+  var ranges=formulaCell.RicoFormula.getRanges();
+  for (var i=0; i<ranges.length; i++) {
+    if (!ranges[i]) continue;
+    var r1=Math.min(ranges[i][0],ranges[i][2]);
+    var r2=Math.max(ranges[i][0],ranges[i][2]);
+    var c1=Math.min(ranges[i][1],ranges[i][3]);
+    var c2=Math.max(ranges[i][1],ranges[i][3]);
+    for (var c=c1; c<=c2; c++) {
+      var col=this.columns[c];
+      for (var r=r1; r<=r2; r++) {
+        var cell=col.cell(r-1);
+        if (!cell.RicoDependencies) cell.RicoDependencies=new Rico.Formula.f_dependencies();
+        //alert('updateDependencies '+action+': '+formulaCell.RicoRow+','+formulaCell.RicoCol+' is dependent on '+cell.RicoRow+','+cell.RicoCol);
+        cell.RicoDependencies[action](formulaCell);
+      }
+    }
+  }
+},
+
+checkDependencies: function(cell) {
+  if (!cell.RicoDependencies) return;
+  var depcells=cell.RicoDependencies.items;
+  for (var i=0; i<depcells.length; i++) {
+    depcells[i].RicoValue=depcells[i].RicoFormula.eval();
+    this.formatCell(depcells[i]);
+    this.checkDependencies(depcells[i]);
+  }
+},
+
+showInputArea: function(clear,e) {
+  this.unplugScroll();
+  this.inputIdx=this.SelectIdxStart;
+  var col=this.columns[this.inputIdx.column];
+  this.inputIdx.cell=col.cell(this.inputIdx.row);
+  this.inputArea.style.top=(this.inputIdx.cell.offsetTop+col.dataCell.offsetTop)+'px';
+  this.inputArea.style.left=col.dataCell.offsetLeft+'px';
+  this.inputArea.style.display='';
+  this.inputArea.focus();
+  if (clear) {
+    if (Prototype.Browser.WebKit) {
+      // Safari does not bubble the event to the inputArea, so force it
+      this.inputArea.value=String.fromCharCode(e.charCode);
+      this.inputArea.setSelectionRange(1,1);
+      Event.stop(e);
+    } else this.inputArea.value='';
+  } else {
+    if (this.inputIdx.cell.RicoFormula)
+      this.inputArea.value=this.inputIdx.cell.RicoFormula.toEditString();
+    else
+      this.inputArea.value=this.inputIdx.cell.RicoValue || '';
+  }
+},
+
+closeInputArea: function(dr,dc,e) {
+  var newVal=this.inputArea.value;
+  var cell=this.inputIdx.cell;
+  if (this.options.checkEntry)
+    newVal=this.options.checkEntry(newVal,this.inputIdx.cell);
+  this.updateDependencies(cell,'remove');
+  cell.RicoFormula=null;
+  if (!this.options.noFormulas && newVal.charAt(0) == '=') {
+    // parse formula
+    cell.RicoFormula = new Rico.Formula(grid,cell);
+    cell.RicoFormula.parse(newVal);
+    cell.RicoValue = cell.RicoFormula.eval();
+    this.updateDependencies(cell,'add');
+  } else if (newVal=='') {
+    cell.RicoValue = null;
+  } else if (newVal.match(/^(true|false)$/i)) {
+    cell.RicoValue = eval(newVal.toLowerCase());
+  } else if (newVal.match(/^-?\d+(.\d*)?$/)) {
+    // parse number
+    cell.RicoValue = parseFloat(newVal);
+  } else {
+    cell.RicoValue=newVal;
+  }
+  this.formatCell(cell);
+  this.inputArea.blur();
+  this.inputArea.style.display='none';
+  this.checkDependencies(cell);
+  this.pluginScroll();
+  this.moveSelection(dr,dc,false,e);
+},
+
+inputKeydown: function(e) {
+  //window.status='inputKeydown keyCode='+e.keyCode;
+  switch (e.keyCode) {
+    case 13:
+      Event.stop(e);
+      this.closeInputArea(1,0,e);
+      return false;
+    case 9:
+      Event.stop(e);
+      this.closeInputArea(0,e.shiftKey ? -1 : 1,e);
+      return false;
+    case 27:
+      Event.stop(e);
+      this.inputArea.blur();
+      this.inputArea.style.display='none';
+      return false;
+  }
+  return true;
+},
+
+copyToClipbox: function() {
+  var s=this.getSelection();
+  if (!s) return;
+  var clipstr='';
+  for (var r=s.r1; r<=s.r2; r++) {
+    for (var c=s.c1; c<=s.c2; c++) {
+      if (c>s.c1) clipstr+="\t";
+      clipstr+=this.columns[c].cell(r).RicoValue;
+    }
+    clipstr+="\r\n";
+  }
+  this.clipBox.style.display='block';
+  this.clipBox.value=clipstr;
+  this.clipBox.select();
+},
+
+copySelection: function() {
+  var s=this.getSelection();
+  if (!s) return;
+  var clipArray=[];
+  for (var r=s.r1; r<=s.r2; r++) {
+    var cliprow=[];
+    for (var c=s.c1; c<=s.c2; c++) {
+      var clipcell={};
+      var gridcell=this.columns[c].cell(r);
+      clipcell.value=gridcell.RicoValue;
+      clipcell.style=gridcell.style.cssText;
+      if (gridcell.RicoFormat)
+        clipcell.format=Object.extend({}, gridcell.RicoFormat || {});
+      if (gridcell.RicoFormula)
+        clipcell.formula=Object.extend({}, gridcell.RicoFormula);
+      cliprow[c-s.c1]=clipcell;
+    }
+    clipArray[r-s.r1]=cliprow;
+  }
+  return clipArray;
+},
+
+pasteSelection: function(clipArray,pasteType) {
+  var s=this.getSelection();
+  if (!s || !clipArray) return;
+  pasteType=pasteType || 'all';
+  var clipclen=clipArray[0].length;
+  if (s.r1==s.r2 && s.c1==s.c2) {
+    s.r2=Math.min(s.r1+clipArray.length,this.columns[0].numRows())-1;
+    s.c2=Math.min(s.c1+clipclen,this.columns.length)-1;
+  }
+  for (var r=s.r1,clipr=0; r<=s.r2; r++) {
+    var arow=clipArray[clipr];
+    for (var c=s.c1,clipc=0; c<=s.c2; c++) {
+      var clipcell=arow[clipc];
+      var gridcell=this.columns[c].cell(r);
+      this.updateDependencies(gridcell,'remove');
+      gridcell.RicoFormula=null;
+      if (clipcell.formula) {
+        gridcell.RicoFormula=Object.extend({}, clipcell.formula);
+        gridcell.RicoFormula.cell=gridcell;
+        gridcell.RicoValue = gridcell.RicoFormula.eval();
+        this.updateDependencies(gridcell,'add');
+      } else {
+        gridcell.RicoValue=clipcell.value;
+      }
+      gridcell.style.cssText=clipcell.style;
+      if (clipcell.format)
+        gridcell.RicoFormat=Object.extend({}, clipcell.format);
+      this.formatCell(gridcell);
+      this.checkDependencies(gridcell);
+      clipc=(clipc+1) % clipclen;
+    }
+    clipr=(clipr+1) % clipArray.length;
+  }
+},
+
+formatSelection: function(newFormat) {\r
+  var s=this.getSelection();
+  if (!s) return;\r
+  for (var r=s.r1; r<=s.r2; r++) {
+    for (var c=s.c1; c<=s.c2; c++) {
+      var gridcell=this.cell(r,c);
+      gridcell.RicoFormat=newFormat;
+      this.formatCell(gridcell);
+    }
+  }\r
+},\r
+\r
+handleCtrlKey: function(e) {
+  switch (e.keyCode) {
+    // Ctrl-C
+    case 67:
+      this.clip=this.copySelection();
+      window.status='copy: '+this.clip.length;
+      Event.stop(e);
+      break;
+
+    // Ctrl-X
+    case 88:
+      this.clip=this.copySelection();
+      this.clearSelection();
+      Event.stop(e);
+      break;
+
+    // Ctrl-V
+    case 86:
+      window.status='paste: '+this.clip.length;
+      this.pasteSelection(this.clip);
+      Event.stop(e);
+      break;
+
+    // Ctrl-B
+    case 66:
+      this.toggleAttr('font-weight','normal','bold');
+      Event.stop(e);
+      break;
+
+    // Ctrl-I
+    case 73:
+      this.toggleAttr('font-style','normal','italic');
+      Event.stop(e);
+      break;
+  }
+},
+
+handleNormalKey: function(e) {
+  switch (e.keyCode) {
+    case 91:
+    case 16:
+    case 17:
+    case 18:
+    case 20:
+    case 27: return;
+
+    // tab
+    case 9:  this.moveSelection(0,e.shiftKey ? -1 : 1,false,e); break;
+    // enter/return
+    case 13: this.moveSelection(1,0,false,e); break;
+    // arrow keys
+    case 37: this.moveSelection(0,-1,e.shiftKey,e); break;
+    case 38: this.moveSelection(-1,0,e.shiftKey,e); break;
+    case 39: this.moveSelection(0,1,e.shiftKey,e); break;
+    case 40: this.moveSelection(1,0,e.shiftKey,e); break;
+    // home
+    case 36: this.selectCellRC(this.SelectIdxStart.row,1); Event.stop(e); break;
+    // F2
+    case 113: this.showInputArea(false,e); break;
+
+    default: this.showInputArea(true,e); break;
+  }
+  return false;
+},
+
+gridKeydown: function(e) {
+  if (e.altKey) return;
+  var elem=Event.element(e);
+  if (elem.id=='inputArea') return true;
+  //window.status='gridKeydown keyCode='+e.keyCode;
+  if (e.ctrlKey)
+    this.handleCtrlKey(e);
+  else
+    this.handleNormalKey(e);
+},
+
+toggleAttr: function(attr,v1,v2) {
+  var v=this.getStyle(this.SelectIdxStart.row,this.SelectIdxStart.column,attr);
+  v=v==v2 ? v1 : v2;
+  this.updateSelectionStyle(attr,v);
+},
+
+getStyle: function(row,col,attr) {
+  var csstxt=this.columns[col].cell(row).style.cssText;
+  if (!csstxt) return;
+  if (csstxt.charAt(csstxt.length-1)!=';') csstxt+=';';   // opera
+  csstxt=' '+csstxt;
+  var re=new RegExp("[ ;]"+attr+"\\s*:\\s*([^ ;]*)\\s*;","i");
+  if (re.test(csstxt))
+    return RegExp.$1;
+  else
+    return;
+},
+
+updateStyleText: function(csstxt,attr,value) {
+  var newval=attr+':'+value+';';
+  if (!csstxt) return newval;
+  csstxt=' '+csstxt.strip();
+  if (csstxt.charAt(csstxt.length-1)!=';') csstxt+=';';   // opera
+  var re=new RegExp("([ ;])"+attr+"\\s*:\\s*([^ ;]*)\\s*;","i");
+  // safari must process the regexp twice, everyone else can run it once
+  if (re.test(csstxt))
+    return Prototype.Browser.WebKit ? csstxt.replace(re,"$1"+newval) : RegExp.leftContext+RegExp.$1+newval+RegExp.rightContext;
+  else
+    return csstxt+newval;
+},
+
+updateSelectionStyle: function(attr,newVal) {
+  var s=this.getSelection();
+  if (!s) return;
+  for (var c=s.c1; c<=s.c2; c++) {
+    var col=this.columns[c];
+    for (var r=s.r1; r<=s.r2; r++)
+      col.cell(r).style.cssText=this.updateStyleText(col.cell(r).style.cssText,attr,newVal);
+  }
+},
+
+showHelp: function() {
+  var msg="Rico Spreadsheet\n\n";
+  msg+="Ctrl-C = copy, Ctrl-X = cut, Ctrl-V = paste (only from/to cells on this grid)\n\n";
+  msg+="Formulas starting with '=' are supported\n";
+  msg+="Formulas may contain parentheses and the following operators: + - * / & % = > < <= >= <>\n";
+  msg+="'+' follows javascript rules regarding type conversion (which are slightly different from Excel)\n";
+  msg+="Formulas may refer to cells using 'A1' notation (and 'A1:B2' for ranges).\n";
+  msg+="The following functions are supported in formulas:\n\n";
+  var funclist=[];
+  for (var funcname in Rico.Formula.prototype)
+    if (funcname.substring(0,5)=='eval_') funclist.push(funcname.substring(5));
+  funclist.sort();
+  var funcstr=funclist.join(', ');
+  var i=funcstr.indexOf(' ',Math.floor(funcstr.length/2));
+  msg+=funcstr.substring(0,i)+"\n"+funcstr.substring(i+1);
+  msg+="\n\nFormula parsing based on code originally published by E. W. Bachtal at http://ewbi.blogs.com/develops/";
+  msg+="\nFuture functionality may include copy/paste from external applications, load/save, number & date formatting, and support for additional functions.";
+  alert(msg);
+}
+
+});
+
+
+Rico.Formula = Class.create();
+
+Rico.Formula.TOK_TYPE_NOOP      = "noop";
+Rico.Formula.TOK_TYPE_OPERAND   = "operand";
+Rico.Formula.TOK_TYPE_FUNCTION  = "function";
+Rico.Formula.TOK_TYPE_SUBEXPR   = "subexpression";
+Rico.Formula.TOK_TYPE_ARGUMENT  = "argument";
+Rico.Formula.TOK_TYPE_OP_PRE    = "operator-prefix";
+Rico.Formula.TOK_TYPE_OP_IN     = "operator-infix";
+Rico.Formula.TOK_TYPE_OP_POST   = "operator-postfix";
+Rico.Formula.TOK_TYPE_WSPACE    = "white-space";
+Rico.Formula.TOK_TYPE_UNKNOWN   = "unknown";
+
+Rico.Formula.TOK_SUBTYPE_START       = "start";
+Rico.Formula.TOK_SUBTYPE_STOP        = "stop";
+
+Rico.Formula.TOK_SUBTYPE_TEXT        = "text";
+Rico.Formula.TOK_SUBTYPE_NUMBER      = "number";
+Rico.Formula.TOK_SUBTYPE_LOGICAL     = "logical";
+Rico.Formula.TOK_SUBTYPE_ERROR       = "error";
+Rico.Formula.TOK_SUBTYPE_RANGE       = "range";
+
+Rico.Formula.TOK_SUBTYPE_MATH        = "math";
+Rico.Formula.TOK_SUBTYPE_CONCAT      = "concatenate";
+Rico.Formula.TOK_SUBTYPE_INTERSECT   = "intersect";
+Rico.Formula.TOK_SUBTYPE_UNION       = "union";
+
+Rico.Formula.prototype = {
+
+initialize: function(grid,cell) {
+  this.grid=grid;
+  this.cell=cell;
+},
+
+// 'A' -> 1, 'AA' -> 27
+colLetter2Num: function(colstr) {
+  colstr=colstr.toUpperCase();
+  switch (colstr.length) {
+    case 1: return colstr.charCodeAt(0)-64;
+    case 2: return (colstr.charCodeAt(0)-64) * 26 + colstr.charCodeAt(1)-64;
+    default: return -1;
+  }
+},
+
+// 1 -> 'A', 27 -> 'AA'
+colNum2Letter: function(colnum) {
+  if (colnum <= 26) return String.fromCharCode(64+colnum);
+  colnum-=1;
+  return String.fromCharCode(64+Math.floor(colnum / 26),65+(colnum % 26));
+},
+
+
+toHTML: function() {
+  var indentCount = 0;
+
+  var indent = function() {
+    var s = "|";
+    for (var i = 0; i < indentCount; i++) {
+      s += "&nbsp;&nbsp;&nbsp;|";
+    }
+    return s;
+  };
+
+  var tokensHtml = "<table cellspacing='0'>";
+  tokensHtml += "<tr>";
+  tokensHtml += "<td class='token' style='font-weight: bold; width: 50px'>index</td>";
+  tokensHtml += "<td class='token' style='font-weight: bold; width: 125px'>type</td>";
+  tokensHtml += "<td class='token' style='font-weight: bold; width: 125px'>subtype</td>";
+  tokensHtml += "<td class='token' style='font-weight: bold; width: 150px'>token</td>";
+  tokensHtml += "<td class='token' style='font-weight: bold; width: 300px'>token tree</td></tr>";
+
+  this.tokens.reset();
+  while (this.tokens.moveNext()) {
+
+    var token = this.tokens.current();
+
+    if (token.subtype == Rico.Formula.TOK_SUBTYPE_STOP)
+      indentCount -= ((indentCount > 0) ? 1 : 0);
+
+    tokensHtml += "<tr>";
+
+    tokensHtml += "<td class='token'>" + (this.tokens.index + 1) + "</td>";
+    tokensHtml += "<td class='token'>" + token.type + "</td>";
+    tokensHtml += "<td class='token'>" + ((token.subtype.length == 0) ? "&nbsp;" : token.subtype) + "</td>";
+    tokensHtml += "<td class='token'>" + ((token.value.length == 0) ? "&nbsp;" : token.value).split(" ").join("&nbsp;") + "</td>";
+    tokensHtml += "<td class='token'>" + indent() + ((token.value.length == 0) ? "&nbsp;" : token.value).split(" ").join("&nbsp;") + "</td>";
+
+    tokensHtml += "</tr>";
+
+    if (token.subtype == Rico.Formula.TOK_SUBTYPE_START) indentCount++;
+  }
+  tokensHtml += "</table>";
+  return tokensHtml;
+},
+
+
+parseCellRef: function(refString) {
+  if (!refString) return null;
+  if (!refString.match(/^(\$?)([a-z]*)(\$?)(\d*)$/i)) return null;
+  var abscol=(RegExp.$1=='$');
+  var absrow=(RegExp.$3=='$');
+  var r=null,c=null;
+  if (RegExp.$2) {
+    c=this.colLetter2Num(RegExp.$2);
+    if (c<0 || c>=this.grid.columns.length) return null;
+    if (!abscol) c-=this.cell.RicoCol;
+  }
+  if (RegExp.$4) {
+    r=parseInt(RegExp.$4);
+    if (!absrow) r-=this.cell.RicoRow;
+  }
+  //alert('parseCellRef: '+refString+"\n"+'r='+r+' c='+c+' absrow='+absrow+' abscol='+abscol);
+  return {row:r, col:c, absRow:absrow, absCol:abscol};
+},
+
+
+resolveCellRef: function(cellRef) {
+  var r=cellRef.row;
+  var c=cellRef.col;
+  if (!cellRef.absRow) r+=this.cell.RicoRow;
+  if (!cellRef.absCol) c+=this.cell.RicoCol;
+  return {row:r, col:c};
+},
+
+
+resolveRange: function(token) {
+  if (!token.rangeStart) return null;
+  var a1=this.resolveCellRef(token.rangeStart);
+  var a2=this.resolveCellRef(token.rangeEnd);
+  //alert('resolveRange: '+a1.row+','+a1.col+' '+a2.row+','+a2.col);
+  var r1=Math.min(a1.row,a2.row);
+  var r2=Math.max(a1.row,a2.row);
+  var c1=Math.min(a1.col,a2.col) || 0;
+  var c2=Math.max(a1.col,a2.col) || this.grid.columns.length-1;
+  return [r1,c1,r2,c2];
+},
+
+
+range2evalstr: function(token) {
+  var rng=this.resolveRange(token);
+  return rng ? rng.join(',') : '';
+},
+
+
+cellref2str: function(cellRef) {
+  var ref=this.resolveCellRef(cellRef);
+  var c=this.colNum2Letter(ref.col);
+  if (cellRef.absCol) c='$'+c;
+  var r=ref.row.toString();
+  if (cellRef.absRow) r='$'+r;
+  return c+r;
+},
+
+
+range2str: function(token) {
+  var s1=this.cellref2str(token.rangeStart);
+  var s2=this.cellref2str(token.rangeEnd);
+  return (s1==s2) ? s1 : s1+':'+s2;
+},
+
+
+GetRange: function(r1,c1,r2,c2) {
+  if (typeof r1=='undefined' || typeof c1=='undefined') return NaN;
+  if (r1==r2 && c1==c2) return this.grid.columns[c1].cell(r1-1).RicoValue;
+  var result=[];
+  for (var r=r1; r<=r2; r++) {
+    var newRow=[];
+    for (var c=c1; c<=c2; c++)
+      newRow.push(this.grid.columns[c].cell(r-1).RicoValue);
+    result.push(newRow);
+  }
+  return result;
+},
+
+
+getRanges: function() {
+  var result=[];
+  this.tokens.reset();
+  while (this.tokens.moveNext()) {
+    var token = this.tokens.current();
+    if (token.subtype=='range') result.push(this.resolveRange(token));
+  }
+  return result;
+},
+
+
+eval_sum: function() {
+  var result=0;
+  for (var i=0; i<arguments.length; i++) {
+    arg=arguments[i];
+    if (arg==null) continue;
+    switch (typeof arg) {
+      case 'number':
+        result+=arg;
+        break;
+      case 'object':
+        for (var r=0; r<arg.length; r++)
+          for (var c=0; c<arg[r].length; c++)
+            if (typeof arg[r][c]=='number') result+=arg[r][c];
+        break;
+    }
+  }
+  return result;
+},
+
+
+eval_count: function() {
+  var result=0;
+  for (var i=0; i<arguments.length; i++) {
+    arg=arguments[i];
+    if (arg==null) continue;
+    switch (typeof arg) {
+      case 'object':
+        for (var r=0; r<arg.length; r++)
+          for (var c=0; c<arg[r].length; c++)
+            if (arg[r][c] || typeof arg[r][c]=='number') result++;
+        break;
+      default:
+        if (arg || typeof arg=='number') result++;
+        break;
+    }
+  }
+  return result;
+},
+
+
+eval_t: function(arg) {
+  return (typeof arg=='string') ? arg : '';
+},
+
+
+eval_trim: function(arg) {
+  arg=this.argString(arg);
+  return arg.strip();
+},
+
+
+eval_lower: function(arg) {
+  arg=this.argString(arg);
+  return arg.toLowerCase();
+},
+
+
+eval_upper: function(arg) {
+  arg=this.argString(arg);
+  return arg.toUpperCase();
+},
+
+
+eval_len: function(arg) {
+  arg=this.argString(arg);
+  return arg.length;
+},
+
+
+eval_value: function(arg) {
+  arg=this.argString(arg);
+  return parseFloat(arg);
+},
+
+
+eval_left: function(arg,numchars) {
+  arg=this.argString(arg);
+  if (typeof numchars!='number') numchars=1;
+  if (numchars<0) return NaN;
+  return arg.slice(0,numchars);
+},
+
+
+eval_right: function(arg,numchars) {
+  arg=this.argString(arg);
+  if (typeof numchars!='number') numchars=1;
+  if (numchars<0) return NaN;
+  if (numchars==0) return '';
+  return arg.slice(-numchars);
+},
+
+
+eval_mid: function(arg,start,numchars) {
+  arg=this.argString(arg);
+  if (typeof start!='number' || start<1) return NaN;
+  if (typeof numchars!='number' || numchars<0) return NaN;
+  return arg.substr(start-1,numchars);
+},
+
+
+eval_if: function(logical_test, value_true, value_false) {
+  var v=this.argBool(logical_test);
+  if (v==null) return NaN;
+  return v ? value_true : value_false;
+},
+
+
+eval_not: function(arg) {
+  var v=this.argBool(arg);
+  return (v==null) ? NaN : !v;
+},
+
+
+eval_and: function() {
+  var args = $A(arguments);
+  args.unshift(function(a,b) { return a&&b; });
+  return this.or_and.apply(this, args);
+},
+
+
+eval_or: function() {
+  var args = $A(arguments);
+  args.unshift(function(a,b) { return a||b; });
+  return this.or_and.apply(this, args);
+},
+
+
+or_and: function() {
+  var result;
+  var func=arguments[0];
+  for (var i=1; i<arguments.length; i++) {
+    arg=arguments[i];
+    if (arg==null) continue;
+    switch (typeof arg) {
+      case 'object':
+        for (var r=0; r<arg.length; r++)
+          for (var c=0; c<arg[r].length; c++) {
+            var v=this.argBool(arg[r][c])
+            if (v!=null) result=(typeof result=='undefined') ? v : func(result,v);
+          }
+        break;
+      default:
+        var v=this.argBool(arg)
+        if (v!=null) result=(typeof result=='undefined') ? v : func(result,v);
+        break;
+    }
+  }
+  return (typeof result=='undefined') ? NaN : result;
+},
+
+
+eval_abs:     function(arg) { return Math.abs(this.argNumber(arg)); },
+eval_acos:    function(arg) { return Math.acos(this.argNumber(arg)); },
+eval_asin:    function(arg) { return Math.asin(this.argNumber(arg)); },
+eval_atan:    function(arg) { return Math.atan(this.argNumber(arg)); },
+eval_atan2:   function(argx,argy) { return Math.atan2(this.argNumber(argy),this.argNumber(argx)); },
+eval_ceiling: function(arg) { return Math.ceil(this.argNumber(arg)); },
+eval_cos:     function(arg) { return Math.cos(this.argNumber(arg)); },
+eval_exp:     function(arg) { return Math.exp(this.argNumber(arg)); },
+eval_floor:   function(arg) { return Math.floor(this.argNumber(arg)); },
+eval_ln:      function(arg) { return Math.log(this.argNumber(arg)); },
+eval_mod:     function(num,divisor) { return this.argNumber(num) % this.argNumber(divisor); },
+eval_pi:      function() { return Math.PI; },
+eval_power:   function(argx,argy) { return Math.pow(this.argNumber(argx),this.argNumber(argy)); },
+eval_rand:    function() { return Math.random(); },
+eval_round:   function(arg) { return Math.round(this.argNumber(arg)); },
+eval_sin:     function(arg) { return Math.sin(this.argNumber(arg)); },
+eval_sqrt:    function(arg) { return Math.sqrt(this.argNumber(arg)); },
+eval_tan:     function(arg) { return Math.tan(this.argNumber(arg)); },
+
+
+argNumber: function(arg) {
+  switch (typeof arg) {
+    case 'boolean': return arg;
+    case 'number': return arg;
+    case 'string': return parseFloat(arg);
+    default: return null;
+  }
+},
+
+
+argBool: function(arg) {
+  switch (typeof arg) {
+    case 'boolean': return arg;
+    case 'number': return arg!=0;
+    default: return null;
+  }
+},
+
+
+argString: function(arg) {
+  switch (typeof arg) {
+    case 'string': return arg;
+    case 'boolean':
+    case 'number': return arg.toString();
+    default: return '';
+  }
+},
+
+
+eval: function() {
+  var evalstr='';
+  this.tokens.reset();
+  while (this.tokens.moveNext()) {
+    var token = this.tokens.current();
+    switch (token.type) {
+      case 'function':
+        if (token.subtype=='start') {
+          var funcname='eval_'+token.value.toLowerCase();
+          if (typeof this[funcname]!='function') {
+            alert('Unknown function: '+token.value);
+            return '#ERROR';
+          }
+          evalstr+='this.'+funcname+'(';
+        } else
+          evalstr+=')';
+        break;
+      case 'subexpression':
+        if (token.subtype=='start')
+          evalstr+='(';
+        else
+          evalstr+=')';
+        break;
+      case 'operator-infix':
+        if (token.value=='&')
+          evalstr+='+';
+        else if (token.value=='=')
+          evalstr+='==';
+        else if (token.value=='<>')
+          evalstr+='!=';
+        else
+          evalstr+=token.value;
+        break;
+      case 'operator-postfix':
+        if (token.value=='%')
+          evalstr+='/100';
+        else
+          evalstr+=token.value;
+        break;
+      case 'operand':
+        if (token.subtype=='range')
+          evalstr+='this.GetRange('+this.range2evalstr(token)+')';
+        else if (token.subtype=='text')
+          evalstr+='"'+token.value+'"';
+        else
+          evalstr+=token.value;
+        break;
+      default:
+        evalstr+=token.value;
+        break;
+    }
+  }
+  this.lastEval=evalstr;
+  //window.status=evalstr;
+  try {
+    var result=eval(evalstr)
+    return result;
+  } catch(e) { alert(e.message); return '#ERROR'; }
+},
+
+
+toEditString: function() {
+  var s='=';
+  this.tokens.reset();
+  while (this.tokens.moveNext()) {
+    var token = this.tokens.current();
+    switch (token.type) {
+      case 'function':
+        if (token.subtype=='start')
+          s+=token.value+'(';
+        else
+          s+=')';
+        break;
+      case 'subexpression':
+        if (token.subtype=='start')
+          s+='(';
+        else
+          s+=')';
+        break;
+      case 'operand':
+        if (token.subtype=='range')
+          s+=this.range2str(token);
+        else if (token.subtype=='text')
+          s+='"'+token.value+'"';
+        else
+          s+=token.value;
+        break;
+      default:
+        s+=token.value;
+    }
+  }
+  return s;
+},
+
+
+// Excel formula parser
+// from http://ewbi.blogs.com/develops/2004/12/excel_formula_p.html
+parse: function(formula) {
+  var tokens = new Rico.Formula.f_tokens();
+  var tokenStack = new Rico.Formula.f_tokenStack();
+
+  var offset = 0;
+
+  var currentChar = function() { return formula.substr(offset, 1); };
+  var doubleChar  = function() { return formula.substr(offset, 2); };
+  var nextChar    = function() { return formula.substr(offset + 1, 1); };
+  var EOF         = function() { return (offset >= formula.length); };
+
+  var token = "";
+
+  var inString = false;
+  var inPath = false;
+  var inRange = false;
+  var inError = false;
+
+  while (formula.length > 0) {
+    if (formula.substr(0, 1) == " ")
+      formula = formula.substr(1);
+    else {
+      if (formula.substr(0, 1) == "=")
+        formula = formula.substr(1);
+      break;
+    }
+  }
+
+  while (!EOF()) {
+
+    // state-dependent character evaluation (order is important)
+
+    // double-quoted strings
+    // embeds are doubled
+    // end marks token
+
+    if (inString) {
+      if (currentChar() == "\"") {
+        if (nextChar() == "\"") {
+          token += "\"";
+          offset += 1;
+        } else {
+          inString = false;
+          tokens.add(token, Rico.Formula.TOK_TYPE_OPERAND, Rico.Formula.TOK_SUBTYPE_TEXT);
+          token = "";
+        }
+      } else {
+        token += currentChar();
+      }
+      offset += 1;
+      continue;
+    }
+
+    // single-quoted strings (links)
+    // embeds are double
+    // end does not mark a token
+
+    if (inPath) {
+      if (currentChar() == "'") {
+        if (nextChar() == "'") {
+          token += "'";
+          offset += 1;
+        } else {
+          inPath = false;
+        }
+      } else {
+        token += currentChar();
+      }
+      offset += 1;
+      continue;
+    }
+
+    // bracked strings (range offset or linked workbook name)
+    // no embeds (changed to "()" by Excel)
+    // end does not mark a token
+
+    if (inRange) {
+      if (currentChar() == "]") {
+        inRange = false;
+      }
+      token += currentChar();
+      offset += 1;
+      continue;
+    }
+
+    // error values
+    // end marks a token, determined from absolute list of values
+
+    if (inError) {
+      token += currentChar();
+      offset += 1;
+      if ((",#NULL!,#DIV/0!,#VALUE!,#REF!,#NAME?,#NUM!,#N/A,").indexOf("," + token + ",") != -1) {
+        inError = false;
+        tokens.add(token, Rico.Formula.TOK_TYPE_OPERAND, Rico.Formula.TOK_SUBTYPE_ERROR);
+        token = "";
+      }
+      continue;
+    }
+
+    // independent character evaulation (order not important)
+
+    // establish state-dependent character evaluations
+
+    if (currentChar() == "\"") {
+      if (token.length > 0) {
+        // not expected
+        tokens.add(token, Rico.Formula.TOK_TYPE_UNKNOWN);
+        token = "";
+      }
+      inString = true;
+      offset += 1;
+      continue;
+    }
+
+    if (currentChar() == "'") {
+      if (token.length > 0) {
+        // not expected
+        tokens.add(token, Rico.Formula.TOK_TYPE_UNKNOWN);
+        token = "";
+      }
+      inPath = true;
+      offset += 1;
+      continue;
+    }
+
+    if (currentChar() == "[") {
+      inRange = true;
+      token += currentChar();
+      offset += 1;
+      continue;
+    }
+
+    if (currentChar() == "#") {
+      if (token.length > 0) {
+        // not expected
+        tokens.add(token, Rico.Formula.TOK_TYPE_UNKNOWN);
+        token = "";
+      }
+      inError = true;
+      token += currentChar();
+      offset += 1;
+      continue;
+    }
+
+    // mark start and end of arrays and array rows
+
+    if (currentChar() == "{") {
+      if (token.length > 0) {
+        // not expected
+        tokens.add(token, Rico.Formula.TOK_TYPE_UNKNOWN);
+        token = "";
+      }
+      tokenStack.push(tokens.add("ARRAY", Rico.Formula.TOK_TYPE_FUNCTION, Rico.Formula.TOK_SUBTYPE_START));
+      tokenStack.push(tokens.add("ARRAYROW", Rico.Formula.TOK_TYPE_FUNCTION, Rico.Formula.TOK_SUBTYPE_START));
+      offset += 1;
+      continue;
+    }
+
+    if (currentChar() == ";") {
+      if (token.length > 0) {
+        tokens.add(token, Rico.Formula.TOK_TYPE_OPERAND);
+        token = "";
+      }
+      tokens.addRef(tokenStack.pop());
+      tokens.add(",", Rico.Formula.TOK_TYPE_ARGUMENT);
+      tokenStack.push(tokens.add("ARRAYROW", Rico.Formula.TOK_TYPE_FUNCTION, Rico.Formula.TOK_SUBTYPE_START));
+      offset += 1;
+      continue;
+    }
+
+    if (currentChar() == "}") {
+      if (token.length > 0) {
+        tokens.add(token, Rico.Formula.TOK_TYPE_OPERAND);
+        token = "";
+      }
+      tokens.addRef(tokenStack.pop());
+      tokens.addRef(tokenStack.pop());
+      offset += 1;
+      continue;
+    }
+
+    // trim white-space
+
+    if (currentChar() == " ") {
+      if (token.length > 0) {
+        tokens.add(token, Rico.Formula.TOK_TYPE_OPERAND);
+        token = "";
+      }
+      tokens.add("", Rico.Formula.TOK_TYPE_WSPACE);
+      offset += 1;
+      while ((currentChar() == " ") && (!EOF())) {
+        offset += 1;
+      }
+      continue;
+    }
+
+    // multi-character comparators
+
+    if ((",>=,<=,<>,").indexOf("," + doubleChar() + ",") != -1) {
+      if (token.length > 0) {
+        tokens.add(token, Rico.Formula.TOK_TYPE_OPERAND);
+        token = "";
+      }
+      tokens.add(doubleChar(), Rico.Formula.TOK_TYPE_OP_IN, Rico.Formula.TOK_SUBTYPE_LOGICAL);
+      offset += 2;
+      continue;
+    }
+
+    // standard infix operators
+
+    if (("+-*/^&=><").indexOf(currentChar()) != -1) {
+      if (token.length > 0) {
+        tokens.add(token, Rico.Formula.TOK_TYPE_OPERAND);
+        token = "";
+      }
+      tokens.add(currentChar(), Rico.Formula.TOK_TYPE_OP_IN);
+      offset += 1;
+      continue;
+    }
+
+    // standard postfix operators
+
+    if (("%").indexOf(currentChar()) != -1) {
+      if (token.length > 0) {
+        tokens.add(token, Rico.Formula.TOK_TYPE_OPERAND);
+        token = "";
+      }
+      tokens.add(currentChar(), Rico.Formula.TOK_TYPE_OP_POST);
+      offset += 1;
+      continue;
+    }
+
+    // start subexpression or function
+
+    if (currentChar() == "(") {
+      if (token.length > 0) {
+        tokenStack.push(tokens.add(token, Rico.Formula.TOK_TYPE_FUNCTION, Rico.Formula.TOK_SUBTYPE_START));
+        token = "";
+      } else {
+        tokenStack.push(tokens.add("", Rico.Formula.TOK_TYPE_SUBEXPR, Rico.Formula.TOK_SUBTYPE_START));
+      }
+      offset += 1;
+      continue;
+    }
+
+    // function, subexpression, array parameters
+
+    if (currentChar() == ",") {
+      if (token.length > 0) {
+        tokens.add(token, Rico.Formula.TOK_TYPE_OPERAND);
+        token = "";
+      }
+      if (!(tokenStack.type() == Rico.Formula.TOK_TYPE_FUNCTION)) {
+        tokens.add(currentChar(), Rico.Formula.TOK_TYPE_OP_IN, Rico.Formula.TOK_SUBTYPE_UNION);
+      } else {
+        tokens.add(currentChar(), Rico.Formula.TOK_TYPE_ARGUMENT);
+      }
+      offset += 1;
+      continue;
+    }
+
+    // stop subexpression
+
+    if (currentChar() == ")") {
+      if (token.length > 0) {
+        tokens.add(token, Rico.Formula.TOK_TYPE_OPERAND);
+        token = "";
+      }
+      tokens.addRef(tokenStack.pop());
+      offset += 1;
+      continue;
+    }
+
+    // token accumulation
+
+    token += currentChar();
+    offset += 1;
+
+  }
+
+  // dump remaining accumulation
+
+  if (token.length > 0) tokens.add(token, Rico.Formula.TOK_TYPE_OPERAND);
+
+  // move all tokens to a new collection, excluding all unnecessary white-space tokens
+
+  var tokens2 = new Rico.Formula.f_tokens();
+
+  while (tokens.moveNext()) {
+
+    token = tokens.current();
+
+    if (token.type == Rico.Formula.TOK_TYPE_WSPACE) {
+      if ((tokens.BOF()) || (tokens.EOF())) {}
+      else if (!(
+                 ((tokens.previous().type == Rico.Formula.TOK_TYPE_FUNCTION) && (tokens.previous().subtype == Rico.Formula.TOK_SUBTYPE_STOP)) ||
+                 ((tokens.previous().type == Rico.Formula.TOK_TYPE_SUBEXPR) && (tokens.previous().subtype == Rico.Formula.TOK_SUBTYPE_STOP)) ||
+                 (tokens.previous().type == Rico.Formula.TOK_TYPE_OPERAND)
+                )
+              ) {}
+      else if (!(
+                 ((tokens.next().type == Rico.Formula.TOK_TYPE_FUNCTION) && (tokens.next().subtype == Rico.Formula.TOK_SUBTYPE_START)) ||
+                 ((tokens.next().type == Rico.Formula.TOK_TYPE_SUBEXPR) && (tokens.next().subtype == Rico.Formula.TOK_SUBTYPE_START)) ||
+                 (tokens.next().type == Rico.Formula.TOK_TYPE_OPERAND)
+                 )
+               ) {}
+      else
+        tokens2.add(token.value, Rico.Formula.TOK_TYPE_OP_IN, Rico.Formula.TOK_SUBTYPE_INTERSECT);
+      continue;
+    }
+
+    tokens2.addRef(token);
+
+  }
+
+  // switch infix "-" operator to prefix when appropriate, switch infix "+" operator to noop when appropriate, identify operand
+  // and infix-operator subtypes, pull "@" from in front of function names
+
+  while (tokens2.moveNext()) {
+
+    token = tokens2.current();
+
+    if ((token.type == Rico.Formula.TOK_TYPE_OP_IN) && (token.value == "-")) {
+      if (tokens2.BOF())
+        token.type = Rico.Formula.TOK_TYPE_OP_PRE;
+      else if (
+               ((tokens2.previous().type == Rico.Formula.TOK_TYPE_FUNCTION) && (tokens2.previous().subtype == Rico.Formula.TOK_SUBTYPE_STOP)) ||
+               ((tokens2.previous().type == Rico.Formula.TOK_TYPE_SUBEXPR) && (tokens2.previous().subtype == Rico.Formula.TOK_SUBTYPE_STOP)) ||
+               (tokens2.previous().type == Rico.Formula.TOK_TYPE_OP_POST) ||
+               (tokens2.previous().type == Rico.Formula.TOK_TYPE_OPERAND)
+              )
+        token.subtype = Rico.Formula.TOK_SUBTYPE_MATH;
+      else
+        token.type = Rico.Formula.TOK_TYPE_OP_PRE;
+      continue;
+    }
+
+    if ((token.type == Rico.Formula.TOK_TYPE_OP_IN) && (token.value == "+")) {
+      if (tokens2.BOF())
+        token.type = Rico.Formula.TOK_TYPE_NOOP;
+      else if (
+               ((tokens2.previous().type == Rico.Formula.TOK_TYPE_FUNCTION) && (tokens2.previous().subtype == Rico.Formula.TOK_SUBTYPE_STOP)) ||
+               ((tokens2.previous().type == Rico.Formula.TOK_TYPE_SUBEXPR) && (tokens2.previous().subtype == Rico.Formula.TOK_SUBTYPE_STOP)) ||
+               (tokens2.previous().type == Rico.Formula.TOK_TYPE_OP_POST) ||
+               (tokens2.previous().type == Rico.Formula.TOK_TYPE_OPERAND)
+              )
+        token.subtype = Rico.Formula.TOK_SUBTYPE_MATH;
+      else
+        token.type = Rico.Formula.TOK_TYPE_NOOP;
+      continue;
+    }
+
+    if ((token.type == Rico.Formula.TOK_TYPE_OP_IN) && (token.subtype.length == 0)) {
+      if (("<>=").indexOf(token.value.substr(0, 1)) != -1)
+        token.subtype = Rico.Formula.TOK_SUBTYPE_LOGICAL;
+      else if (token.value == "&")
+        token.subtype = Rico.Formula.TOK_SUBTYPE_CONCAT;
+      else
+        token.subtype = Rico.Formula.TOK_SUBTYPE_MATH;
+      continue;
+    }
+
+    if ((token.type == Rico.Formula.TOK_TYPE_OPERAND) && (token.subtype.length == 0)) {
+      if (isNaN(parseFloat(token.value)))
+        if ((token.value == 'TRUE') || (token.value == 'FALSE'))
+          token.subtype = Rico.Formula.TOK_SUBTYPE_LOGICAL;
+        else {
+          token.subtype = Rico.Formula.TOK_SUBTYPE_RANGE;
+          var a=token.value.split(':');
+          token.rangeStart=this.parseCellRef(a[0]);
+          token.rangeEnd=a.length>1 ? this.parseCellRef(a[1]) : token.rangeStart;
+        }
+      else
+        token.subtype = Rico.Formula.TOK_SUBTYPE_NUMBER;
+      continue;
+    }
+
+    if (token.type == Rico.Formula.TOK_TYPE_FUNCTION) {
+      if (token.value.substr(0, 1) == "@")
+        token.value = token.value.substr(1);
+      continue;
+    }
+
+  }
+
+  tokens2.reset();
+
+  // move all tokens to a new collection, excluding all noops
+
+  this.tokens = new Rico.Formula.f_tokens();
+
+  while (tokens2.moveNext()) {
+    if (tokens2.current().type != Rico.Formula.TOK_TYPE_NOOP)
+      this.tokens.addRef(tokens2.current());
+  }
+}
+
+}
+
+
+Rico.Formula.f_token = Class.create();
+Rico.Formula.f_token.prototype = {
+  initialize: function(value, type, subtype) {
+    this.value = value;
+    this.type = type;
+    this.subtype = subtype;
+  }
+}
+
+
+Rico.Formula.f_tokens = Class.create();
+Rico.Formula.f_tokens.prototype = {
+  initialize: function() {
+    this.items = new Array();
+    this.index = -1;
+  },
+
+  addRef: function(token) {
+    this.items.push(token);
+  },
+
+  add: function(value, type, subtype) {
+    if (!subtype) subtype = "";
+    var token = new Rico.Formula.f_token(value, type, subtype);
+    this.addRef(token);
+    return token;
+  },
+
+  reset: function() {
+    this.index = -1;
+  },
+
+  BOF: function() {
+    return (this.index <= 0);
+  },
+
+  EOF: function() {
+    return (this.index >= (this.items.length - 1));
+  },
+
+  moveNext: function() {
+    if (this.EOF()) return false; this.index++; return true;
+  },
+
+  current: function() {
+    if (this.index == -1) return null; return (this.items[this.index]);
+  },
+
+  next: function() {
+    if (this.EOF()) return null; return (this.items[this.index + 1]);
+  },
+
+  previous: function() {
+    if (this.index < 1) return null; return (this.items[this.index - 1]);
+  }
+}
+
+
+Rico.Formula.f_tokenStack = Class.create();
+Rico.Formula.f_tokenStack.prototype = {
+  initialize: function() {
+    this.items = new Array();
+  },
+
+  push: function(token) {
+    this.items.push(token);
+  },
+
+  pop: function() {
+    var token = this.items.pop();
+    return (new Rico.Formula.f_token("", token.type, Rico.Formula.TOK_SUBTYPE_STOP));
+  },
+
+  token: function() {
+    return ((this.items.length > 0) ? this.items[this.items.length - 1] : null);
+  },
+
+  value: function() {
+    return ((this.token()) ? this.token().value : "");
+  },
+
+  type: function() {
+    return ((this.token()) ? this.token().type : "");
+  },
+
+  subtype: function() {
+    return ((this.token()) ? this.token().subtype : "");
+  }
+}
+
+
+Rico.Formula.f_dependencies = Class.create();
+Rico.Formula.f_dependencies.prototype = {
+  initialize: function() {
+    this.items = [];
+  },
+
+  add: function(cell) {
+    if (!this.items.include(cell)) this.items.push(cell);
+  },
+
+  remove: function(cell) {
+    this.items=this.items.select(function(item) { return (item != cell); });
+  },
+
+  find: function(cell) {
+    return this.items.detect(function(item) { return (item==cell); });
+  },
+
+  clear: function() {
+    this.items.clear();
+  }
+}
+
+
+Object.extend(Rico.Menu.prototype, {
+
+showSheetMenu: function(e,hideFunc) {
+  var elem=this.showSimpleMenu(e,hideFunc);
+  if (!this.grid) return;
+  var newIdx=this.grid.cellIndex(elem);
+  if (!this.grid.isSelected(newIdx.row,newIdx.column))
+    this.grid.selectCellRC(newIdx.row,newIdx.column,false);
+}
+
+});
+
+
+Rico.includeLoaded('ricoSheet.js');
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoSimpleGrid.js b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoSimpleGrid.js
new file mode 100644 (file)
index 0000000..a6b407e
--- /dev/null
@@ -0,0 +1,160 @@
+/**
+  *  (c) 2005-2007 Richard Cowin (http://openrico.org)
+  *  (c) 2005-2007 Matt Brown (http://dowdybrown.com)
+  *
+  *  Rico is 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
+  **/
+
+
+if(typeof Rico=='undefined') throw("SimpleGrid requires the Rico JavaScript framework");
+if(typeof RicoUtil=='undefined') throw("SimpleGrid requires the RicoUtil Library");
+if(typeof RicoTranslate=='undefined') throw("SimpleGrid requires the RicoTranslate Library");
+
+Rico.SimpleGrid = Class.create();
+
+Rico.SimpleGrid.prototype = {
+
+  initialize: function( tableId, options ) {
+    Object.extend(this, new Rico.GridCommon);
+    this.baseInit(tableId);
+    Rico.setDebugArea(tableId+"_debugmsgs");    // if used, this should be a textarea
+    Object.extend(this.options, options || {});
+    this.tableId = tableId;
+    this.createDivs();
+    this.hdrTabs=new Array(2);
+    for (var i=0; i<2; i++) {
+      this.tabs[i]=$(tableId+'_tab'+i);
+      this.hdrTabs[i]=$(tableId+'_tab'+i+'h');
+      if (i==0) this.tabs[i].style.position='absolute';
+      if (i==0) this.tabs[i].style.left='0px';
+      this.hdrTabs[i].style.position='absolute';
+      this.hdrTabs[i].style.top='0px';
+      this.hdrTabs[i].style.zIndex=1;
+      this.thead[i]=this.hdrTabs[i];
+      this.tbody[i]=this.tabs[i];
+      this.headerColCnt = this.getColumnInfo(this.hdrTabs[i].rows);
+      if (i==0) this.options.frozenColumns=this.headerColCnt;
+    }
+    if (this.headerColCnt==0) {
+      alert('ERROR: no columns found in "'+this.tableId+'"');
+      return;
+    }
+    this.hdrHt=Math.max(RicoUtil.nan2zero(this.hdrTabs[0].offsetHeight),this.hdrTabs[1].offsetHeight);
+    for (var i=0; i<2; i++)
+      if (i==0) this.tabs[i].style.top=this.hdrHt+'px';
+    this.createColumnArray();
+    this.pageSize=this.columns[0].dataColDiv.childNodes.length;
+    this.sizeDivs();
+    this.attachMenuEvents();
+    this.scrollEventFunc=this.handleScroll.bindAsEventListener(this);
+    this.pluginScroll();
+    if (this.options.windowResize)
+      Event.observe(window,"resize", this.sizeDivs.bindAsEventListener(this), false);
+  },
+
+  /**
+   * Register a menu that will only be used in the scrolling part of the grid.
+   * If submenus are used, they must be registered after the main menu.
+   */
+  registerScrollMenu: function(menu) {
+    if (!this.menu) this.menu=menu;
+    menu.grid=this;
+    menu.showmenu=menu.showSimpleMenu;
+    menu.showSubMenu=menu.showSimpleSubMenu;
+    menu.createDiv(this.scrollDiv);
+  },
+  
+  handleMenuClick: function(e) {
+    this.cancelMenu();
+    this.menuCell=RicoUtil.getParentByTagName(Event.element(e),'div');
+    this.highlightEnabled=false;
+    if (this.hideScroll) this.scrollDiv.style.overflow="hidden";
+    if (this.menu.buildGridMenu) this.menu.buildGridMenu(this.menuCell);
+    this.menu.showmenu(e,this.closeMenu.bind(this));
+  },
+
+  closeMenu: function() {
+    if (this.hideScroll) this.scrollDiv.style.overflow="";
+    this.highlightEnabled=true;
+  },
+
+  sizeDivs: function() {
+    if (this.outerDiv.offsetParent.style.display=='none') return;
+    this.baseSizeDivs();
+    var maxHt=Math.max(this.options.maxHt || this.availHt(), 50);
+    var totHt=Math.min(this.hdrHt+this.dataHt, maxHt);
+    Rico.writeDebugMsg('sizeDivs '+this.tableId+': hdrHt='+this.hdrHt+' dataHt='+this.dataHt);
+    this.dataHt=totHt-this.hdrHt;
+    if (this.scrWi>0) this.dataHt+=this.options.scrollBarWidth;
+    this.scrollDiv.style.height=this.dataHt+'px';
+    var divAdjust=2;
+    this.innerDiv.style.width=(this.scrWi-this.options.scrollBarWidth+divAdjust)+'px';
+    this.innerDiv.style.height=this.hdrHt+'px';
+    totHt+=divAdjust;
+    this.resizeDiv.style.height=this.frozenTabs.style.height=totHt+'px';
+    this.outerDiv.style.height=(totHt+this.options.scrollBarWidth)+'px';
+    this.setHorizontalScroll();
+  }
+
+};
+
+if (Rico.Menu) {
+Object.extend(Rico.Menu.prototype, {
+
+showSimpleMenu: function(e,hideFunc) {
+  Event.stop(e);
+  this.hideFunc=hideFunc;
+  if (this.div.childNodes.length==0) {
+    this.cancelmenu();
+    return false;
+  }
+  this.clientX=Event.pointerX(e);
+  this.clientY=Event.pointerY(e);
+  var elem=Event.element(e);
+  while (elem && !Element.hasClassName(elem,'ricoLG_cell'))
+    elem=elem.parentNode;
+  if (!elem) return false;
+  var td=RicoUtil.getParentByTagName(elem,'td');
+
+  var newLeft=Math.floor(td.offsetLeft+td.offsetWidth/2);
+  if (this.direction == 'rtl') {
+    if (newLeft > this.width) newLeft-=this.width;
+  } else {
+    if (newLeft+this.width+this.options.margin > this.grid.scrollDiv.scrollLeft+this.grid.scrollDiv.clientWidth) newLeft-=this.width;
+  }
+  this.div.style.visibility="hidden";
+  this.div.style.display="block";
+  var contentHt=this.div.offsetHeight;
+  var newTop=Math.floor(elem.offsetTop+elem.offsetHeight/2);
+  if (newTop+contentHt+this.options.margin > this.grid.scrollDiv.scrollTop+this.grid.scrollDiv.clientHeight)
+    newTop=Math.max(newTop-contentHt,0);
+  this.openPopup(newLeft,newTop);
+  this.div.style.visibility ="visible";
+  return elem;
+},
+
+showSimpleSubMenu: function(a,submenu) {
+  if (this.openSubMenu) this.hideSubMenu();
+  this.openSubMenu=submenu;
+  this.openMenuAnchor=a;
+  if (a.className=='ricoSubMenu') a.className='ricoSubMenuOpen';
+  var top=parseInt(this.div.style.top);
+  var left=parseInt(this.div.style.left);
+  submenu.openPopup(left+a.offsetWidth,top+a.offsetTop);
+  submenu.div.style.visibility ="visible";
+}
+    
+});
+}
+
+Object.extend(Rico.TableColumn.prototype, {
+
+initialize: function(grid,colIdx,hdrInfo,tabIdx) {
+  this.baseInit(grid,colIdx,hdrInfo,tabIdx);
+}
+
+});
+
+Rico.includeLoaded('ricoSimpleGrid.js');
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoSimpleGrid.xsl b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoSimpleGrid.xsl
new file mode 100644 (file)
index 0000000..f3c0dc2
--- /dev/null
@@ -0,0 +1,285 @@
+<?xml version="1.0"?>\r
+\r
+<xsl:stylesheet version="1.0"\r
+  xmlns:xhtml="http://www.w3.org/1999/xhtml"\r
+  xmlns="http://www.w3.org/1999/xhtml"\r
+  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"\r
+  xmlns:xs="http://www.w3.org/2001/XMLSchema"\r
+  xmlns:fn="http://www.w3.org/2005/02/xpath-functions"\r
+  xmlns:xdt="http://www.w3.org/2005/02/xpath-datatypes"\r
+exclude-result-prefixes="xhtml xsl fn xs xdt">\r
+\r
+<xsl:output\r
+omit-xml-declaration="yes"\r
+method="html"\r
+doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"\r
+doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"/>\r
+\r
+<xsl:attribute-set name="ricoTable"> \r
+  <xsl:attribute name="cellspacing">0</xsl:attribute> \r
+  <xsl:attribute name="cellpadding">0</xsl:attribute> \r
+</xsl:attribute-set> \r
+\r
+<!-- the identity template -->\r
+\r
+<xsl:template match="*">\r
+  <xsl:copy>\r
+  <xsl:copy-of select="@*"/>\r
+  <xsl:apply-templates/>\r
+  </xsl:copy>\r
+</xsl:template>\r
+\r
+\r
+<!-- Transform head section -->\r
+\r
+<xsl:template match="xhtml:head">\r
+  <xsl:copy>\r
+  <xsl:apply-templates mode="head"/>\r
+<script type="text/javascript">\r
+//<![CDATA[\r
+if (typeof ricoInit!='undefined') {\r
+  if (window.addEventListener)\r
+    window.addEventListener('load', ricoInit, false);\r
+  else if (window.attachEvent)\r
+    window.attachEvent('onload', ricoInit);\r
+}\r
+// ]]>\r
+</script>\r
+  </xsl:copy>\r
+</xsl:template>\r
+\r
+<xsl:template match="*[name()!='script']" mode="head">\r
+  <xsl:copy>\r
+  <xsl:copy-of select="@*|node()"/>\r
+  </xsl:copy>\r
+</xsl:template>\r
+\r
+<xsl:template match="xhtml:script" mode="head">\r
+  <xsl:copy>\r
+  <xsl:copy-of select="@*"/>\r
+  <xsl:value-of select="." disable-output-escaping="yes"/>\r
+  </xsl:copy>\r
+</xsl:template>\r
+\r
+\r
+<!-- Transform tables with class ricoSimpleGrid -->\r
+  \r
+<xsl:template match="xhtml:table[@class='ricoSimpleGrid']">\r
+<xsl:choose>\r
+\r
+<xsl:when test="xhtml:thead">\r
+<xsl:call-template name="processTable">\r
+<xsl:with-param name="id" select="@id"/>\r
+<xsl:with-param name="headRows" select="xhtml:thead/xhtml:tr"/>\r
+<xsl:with-param name="bodyRows" select="xhtml:tbody/xhtml:tr"/>\r
+</xsl:call-template>\r
+</xsl:when>\r
+\r
+<xsl:when test="xhtml:tbody">\r
+<xsl:call-template name="processTable">\r
+<xsl:with-param name="id" select="@id"/>\r
+<xsl:with-param name="headRows" select="xhtml:tbody/xhtml:tr[1]"/>\r
+<xsl:with-param name="bodyRows" select="xhtml:tbody/xhtml:tr[position() &gt; 1]"/>\r
+</xsl:call-template>\r
+</xsl:when>\r
+\r
+<xsl:otherwise>\r
+<xsl:call-template name="processTable">\r
+<xsl:with-param name="id" select="@id"/>\r
+<xsl:with-param name="headRows" select="xhtml:tr[1]"/>\r
+<xsl:with-param name="bodyRows" select="xhtml:tr[position() &gt; 1]"/>\r
+</xsl:call-template>\r
+</xsl:otherwise>\r
+\r
+</xsl:choose>\r
+</xsl:template>\r
+\r
+\r
+<!-- Perform the actual table transformation -->\r
+  \r
+<xsl:template name="processTable">\r
+<xsl:param name="id" />\r
+<xsl:param name="headRows" />\r
+<xsl:param name="bodyRows" />\r
+\r
+<xsl:variable name="headIdx">\r
+<xsl:choose>\r
+<xsl:when test="$headRows[@class='ricoHeading']">\r
+<xsl:value-of select="count($headRows[@class='ricoHeading']/preceding-sibling::*)+1"/>\r
+</xsl:when>\r
+<xsl:otherwise>\r
+<xsl:value-of select="count($headRows)"/>\r
+</xsl:otherwise>\r
+</xsl:choose>\r
+</xsl:variable>\r
+\r
+<xsl:variable name="headMain" select="$headRows[position()=$headIdx]"/>\r
+<xsl:variable name="headCols" select="$headMain/xhtml:th | $headMain/xhtml:td"/>\r
+\r
+<!--\r
+<p><xsl:value-of select="$id"/>\r
+<br />headRowCnt: <xsl:value-of select="count($headRows)"/>\r
+<br />headIdx: <xsl:value-of select="$headIdx"/>\r
+<br />bodyRowCnt: <xsl:value-of select="count($bodyRows)"/>\r
+</p>\r
+-->\r
+\r
+<xsl:element name="div">\r
+<xsl:attribute name="id"><xsl:value-of select="concat($id,'_outerDiv')"/></xsl:attribute>\r
+<xsl:attribute name="class">ricoLG_outerDiv</xsl:attribute>\r
+<xsl:attribute name="onload"></xsl:attribute>\r
+\r
+<!-- Create frozen (left) pane -->\r
+\r
+<xsl:element name="div">\r
+<xsl:attribute name="id"><xsl:value-of select="concat($id,'_frozenTabsDiv')"/></xsl:attribute>\r
+<xsl:attribute name="class">ricoLG_frozenTabsDiv</xsl:attribute>\r
+\r
+<xsl:call-template name="convertTHead">\r
+<xsl:with-param name="rows" select="$headRows"/>\r
+<xsl:with-param name="headIdx" select="$headIdx"/>\r
+<xsl:with-param name="frozen" select="1"/>\r
+<xsl:with-param name="id" select="concat($id,'_tab0h')"/>\r
+</xsl:call-template>\r
+\r
+<xsl:call-template name="convertTBody">\r
+<xsl:with-param name="rows" select="$bodyRows"/>\r
+<xsl:with-param name="cols" select="$headCols"/>\r
+<xsl:with-param name="id" select="concat($id,'_tab0')"/>\r
+<xsl:with-param name="frozen" select="1"/>\r
+</xsl:call-template>\r
+\r
+</xsl:element>\r
+\r
+<xsl:element name="div">\r
+<xsl:attribute name="id"><xsl:value-of select="concat($id,'_innerDiv')"/></xsl:attribute>\r
+<xsl:attribute name="class">ricoLG_innerDiv</xsl:attribute>\r
+\r
+<xsl:element name="div">\r
+<xsl:attribute name="id"><xsl:value-of select="concat($id,'_scrollTabsDiv')"/></xsl:attribute>\r
+<xsl:attribute name="class">ricoLG_scrollTabsDiv</xsl:attribute>\r
+\r
+<xsl:call-template name="convertTHead">\r
+<xsl:with-param name="rows" select="$headRows"/>\r
+<xsl:with-param name="headIdx" select="$headIdx"/>\r
+<xsl:with-param name="frozen" select="0"/>\r
+<xsl:with-param name="id" select="concat($id,'_tab1h')"/>\r
+</xsl:call-template>\r
+\r
+</xsl:element>\r
+</xsl:element>\r
+\r
+<xsl:element name="div">\r
+<xsl:attribute name="id"><xsl:value-of select="concat($id,'_scrollDiv')"/></xsl:attribute>\r
+<xsl:attribute name="class">ricoLG_scrollDiv</xsl:attribute>\r
+\r
+<xsl:call-template name="convertTBody">\r
+<xsl:with-param name="rows" select="$bodyRows"/>\r
+<xsl:with-param name="cols" select="$headCols"/>\r
+<xsl:with-param name="id" select="concat($id,'_tab1')"/>\r
+<xsl:with-param name="frozen" select="0"/>\r
+</xsl:call-template>\r
+</xsl:element>\r
+\r
+</xsl:element>\r
+\r
+</xsl:template>\r
+\r
+\r
+<!-- Convert thead section -->\r
+\r
+<xsl:template name="convertTHead">\r
+<xsl:param name = "rows" />\r
+<xsl:param name = "headIdx" />\r
+<xsl:param name = "frozen" />\r
+<xsl:param name = "id" />\r
+<xsl:element name="table" use-attribute-sets="ricoTable">\r
+<xsl:attribute name="id"><xsl:value-of select="$id"/></xsl:attribute>\r
+<xsl:attribute name="class">ricoLG_table ricoLG_top\r
+<xsl:if test="$frozen">ricoLG_left</xsl:if>\r
+<xsl:if test="not($frozen)">ricoLG_right</xsl:if>\r
+</xsl:attribute>\r
+<xsl:element name="thead">\r
+  <xsl:for-each select="$rows">\r
+    <xsl:choose>\r
+    <xsl:when test="position() = $headIdx">\r
+      <xsl:apply-templates select="." mode="convertHeadRow">\r
+      <xsl:with-param name="id" select="concat($id,'_main')"/>\r
+      <xsl:with-param name="frozen" select="$frozen"/>\r
+      </xsl:apply-templates>\r
+    </xsl:when>\r
+    <xsl:otherwise>\r
+      <xsl:apply-templates select="." mode="convertHeadRow">\r
+      <xsl:with-param name="id" select="concat($id,'_',position())"/>\r
+      <xsl:with-param name="frozen" select="$frozen"/>\r
+      </xsl:apply-templates>\r
+    </xsl:otherwise>\r
+    </xsl:choose>\r
+  </xsl:for-each>\r
+</xsl:element>\r
+<tbody />\r
+</xsl:element>\r
+</xsl:template>\r
+\r
+\r
+<xsl:template match="*" mode="convertHeadRow">\r
+<xsl:param name = "id" />\r
+<xsl:param name = "frozen" />\r
+  <xsl:variable name="class" select="@class"/>\r
+  <xsl:variable name="cells" select="xhtml:th | xhtml:td"/>\r
+  <xsl:element name="tr">\r
+  <xsl:if test="$id">\r
+    <xsl:attribute name="id"><xsl:value-of select="$id"/></xsl:attribute>\r
+  </xsl:if>\r
+  <xsl:attribute name="class">ricoLG_hdg <xsl:value-of select="$class"/></xsl:attribute>\r
+  <xsl:for-each select="$cells[@class='ricoFrozen' and $frozen or not(@class='ricoFrozen') and not($frozen)]">\r
+      <xsl:copy>\r
+        <xsl:copy-of select="@*"/>\r
+        <div class='ricoLG_col' style='width:100px'>\r
+          <xsl:element name="div">\r
+          <xsl:attribute name="class">ricoLG_cell <xsl:value-of select="@class"/></xsl:attribute>\r
+            <xsl:copy-of select="* | @*[name()!='class'] | text()"/>\r
+          </xsl:element>\r
+        </div>\r
+      </xsl:copy>\r
+    </xsl:for-each>\r
+  </xsl:element>\r
+</xsl:template>\r
+\r
+\r
+<!-- Convert tbody section -->\r
+\r
+<xsl:template name="convertTBody">\r
+<xsl:param name = "rows" />\r
+<xsl:param name = "cols" />\r
+<xsl:param name = "id" />\r
+<xsl:param name = "frozen" />\r
+<xsl:element name="table" use-attribute-sets="ricoTable">\r
+<xsl:attribute name="id"><xsl:value-of select="$id"/></xsl:attribute>\r
+<xsl:attribute name="class">ricoLG_table ricoLG_bottom\r
+<xsl:if test="$frozen">ricoLG_left</xsl:if>\r
+<xsl:if test="not($frozen)">ricoLG_right</xsl:if>\r
+</xsl:attribute> \r
+<xsl:element name="tbody">\r
+  <tr>\r
+  <xsl:for-each select="$cols">\r
+    <xsl:if test="@class='ricoFrozen' and $frozen or not(@class='ricoFrozen') and not($frozen)">\r
+      <xsl:variable name="colpos" select="position()"/>\r
+      <td>\r
+        <div class='ricoLG_col' style='width:100px'>\r
+          <xsl:for-each select="$rows">\r
+            <xsl:element name="div">\r
+            <xsl:attribute name="class">ricoLG_cell <xsl:value-of select="xhtml:td[$colpos]/@class"/></xsl:attribute>\r
+              <xsl:copy-of select="xhtml:td[$colpos]/* | xhtml:td[$colpos]/@*[name()!='class'] | xhtml:td[$colpos]/text()"/>\r
+            </xsl:element>\r
+          </xsl:for-each>\r
+        </div>\r
+      </td>\r
+    </xsl:if>\r
+  </xsl:for-each>\r
+  </tr>\r
+</xsl:element>\r
+</xsl:element>\r
+</xsl:template>\r
+\r
+</xsl:stylesheet> \r
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoSimpleGrid2xl.xsl b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoSimpleGrid2xl.xsl
new file mode 100644 (file)
index 0000000..f04b2e1
--- /dev/null
@@ -0,0 +1,160 @@
+<xsl:stylesheet version="1.0"\r
+  xmlns="urn:schemas-microsoft-com:office:spreadsheet"\r
+  xmlns:xhtml="http://www.w3.org/1999/xhtml"\r
+  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" \r
+       xmlns:msxsl="urn:schemas-microsoft-com:xslt"\r
+       xmlns:o="urn:schemas-microsoft-com:office:office"\r
+       xmlns:x="urn:schemas-microsoft-com:office:excel"\r
+  xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">\r
+\r
+<xsl:output method="xml" indent="yes" omit-xml-declaration="no" media-type="application/xml"/>\r
+\r
+<xsl:template match="/">\r
+  <xsl:processing-instruction name="mso-application">\r
+  <xsl:text>progid="Excel.Sheet"</xsl:text> \r
+  </xsl:processing-instruction>\r
+\r
+  <Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"\r
+   xmlns:o="urn:schemas-microsoft-com:office:office"\r
+   xmlns:x="urn:schemas-microsoft-com:office:excel"\r
+   xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"\r
+   xmlns:html="http://www.w3.org/TR/REC-html40">\r
+\r
+ <Styles>\r
+  <Style ss:ID="Default" ss:Name="Normal">\r
+   <Alignment ss:Vertical="Bottom"/>\r
+   <Borders/>\r
+   <Font/>\r
+   <Interior/>\r
+   <NumberFormat/>\r
+   <Protection/>\r
+  </Style>\r
+  <Style ss:ID="s21">\r
+   <Font ss:Bold="1"/>\r
+   <Alignment ss:Horizontal="Center" ss:Vertical="Bottom"/>\r
+  </Style>\r
+  <Style ss:ID="s22">\r
+   <Alignment ss:Horizontal="Left" ss:Vertical="Bottom"/>\r
+   <Font ss:Bold="1"/>\r
+   <Interior ss:Color="#99CCFF" ss:Pattern="Solid"/>\r
+  </Style>\r
+  <Style ss:ID="s23" ss:Name="Currency">\r
+   <NumberFormat\r
+    ss:Format="_(&quot;$&quot;* #,##0.00_);_(&quot;$&quot;* \(#,##0.00\);_(&quot;$&quot;* &quot;-&quot;??_);_(@_)"/>\r
+  </Style>\r
+  <Style ss:ID="s24">\r
+   <NumberFormat ss:Format="_(* #,##0.00_);_(* \(#,##0.00\);_(* &quot;-&quot;??_);_(@_)"/>\r
+  </Style>\r
+  <Style ss:ID="s25">\r
+   <Alignment ss:Horizontal="Center" ss:Vertical="Bottom"/>\r
+  </Style>\r
+ </Styles>\r
+\r
+  <xsl:apply-templates mode="top"/> \r
+\r
+  </Workbook>\r
+</xsl:template>\r
+\r
+\r
+<xsl:template match="*" mode="top">\r
+  <xsl:choose>\r
+\r
+  <xsl:when test="xhtml:table[@class='ricoSimpleGrid']">\r
+  <xsl:apply-templates mode="grid"/> \r
+  </xsl:when>\r
+\r
+  <xsl:otherwise>\r
+  <xsl:apply-templates select="*" mode="top"/> \r
+  </xsl:otherwise>\r
+  \r
+  </xsl:choose>\r
+</xsl:template>\r
+\r
+\r
+<xsl:template match="*" mode="grid">\r
+\r
+  <xsl:choose>\r
+  \r
+  <xsl:when test="xhtml:thead">\r
+  <xsl:call-template name="processTable">\r
+  <xsl:with-param name="id" select="@id"/>\r
+  <xsl:with-param name="headRows" select="xhtml:thead/xhtml:tr"/>\r
+  <xsl:with-param name="bodyRows" select="xhtml:tbody/xhtml:tr"/>\r
+  </xsl:call-template>\r
+  </xsl:when>\r
+  \r
+  <xsl:when test="xhtml:tbody">\r
+  <xsl:call-template name="processTable">\r
+  <xsl:with-param name="id" select="@id"/>\r
+  <xsl:with-param name="headRows" select="xhtml:tbody/xhtml:tr[1]"/>\r
+  <xsl:with-param name="bodyRows" select="xhtml:tbody/xhtml:tr[position() &gt; 1]"/>\r
+  </xsl:call-template>\r
+  </xsl:when>\r
+  \r
+  <xsl:otherwise>\r
+  <xsl:call-template name="processTable">\r
+  <xsl:with-param name="id" select="@id"/>\r
+  <xsl:with-param name="headRows" select="xhtml:tr[1]"/>\r
+  <xsl:with-param name="bodyRows" select="xhtml:tr[position() &gt; 1]"/>\r
+  </xsl:call-template>\r
+  </xsl:otherwise>\r
+  \r
+  </xsl:choose>\r
+\r
+</xsl:template>\r
+\r
+\r
+<!-- Perform the actual table transformation -->\r
+  \r
+<xsl:template name="processTable">\r
+<xsl:param name="id" />\r
+<xsl:param name="headRows" />\r
+<xsl:param name="bodyRows" />\r
+\r
+ <Worksheet>\r
+ <xsl:attribute name="ss:Name">\r
+   <xsl:value-of select='$id'/>\r
+ </xsl:attribute>\r
+  <Table>\r
+\r
+  <xsl:apply-templates select="$headRows" mode="convertHeadRow"/>\r
+  <xsl:apply-templates select="$bodyRows" mode="convertBodyRow"/>\r
+\r
+  </Table>\r
+ </Worksheet>\r
+\r
+</xsl:template>\r
+\r
+\r
+<xsl:template match="*" mode="convertHeadRow">\r
+   <Row>\r
+    <xsl:apply-templates select="xhtml:td | xhtml:th" mode="convertHeadCell"/>\r
+   </Row>\r
+</xsl:template>\r
+\r
+\r
+<xsl:template match="*" mode="convertHeadCell">\r
+  <xsl:element name="Cell">\r
+  <xsl:attribute name="ss:StyleID">s22</xsl:attribute>\r
+  <xsl:if test="@colspan">\r
+  <xsl:attribute name="ss:MergeAcross"><xsl:value-of select="number(@colspan)-1"/></xsl:attribute>\r
+  </xsl:if>\r
+    <Data ss:Type="String">\r
+    <xsl:value-of select="."/>\r
+    </Data>\r
+  </xsl:element>\r
+</xsl:template>\r
+\r
+\r
+<xsl:template match="*" mode="convertBodyRow">\r
+   <Row>\r
+    <xsl:apply-templates select="xhtml:td | xhtml:th" mode="convertBodyCell"/>\r
+   </Row>\r
+</xsl:template>\r
+\r
+\r
+<xsl:template match="*" mode="convertBodyCell">\r
+    <Cell><Data ss:Type="String"><xsl:value-of select="."/></Data></Cell>\r
+</xsl:template>\r
+\r
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoStyles.js b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoStyles.js
new file mode 100644 (file)
index 0000000..34af1aa
--- /dev/null
@@ -0,0 +1,469 @@
+/**
+  *
+  *  Copyright 2005 Sabre Airline Solutions
+  *
+  *  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.
+  **/
+
+//-------------------- ricoColor.js
+Rico.Color = Class.create();
+
+Rico.Color.prototype = {
+
+   initialize: function(red, green, blue) {
+      this.rgb = { r: red, g : green, b : blue };
+   },
+
+   setRed: function(r) {
+      this.rgb.r = r;
+   },
+
+   setGreen: function(g) {
+      this.rgb.g = g;
+   },
+
+   setBlue: function(b) {
+      this.rgb.b = b;
+   },
+
+   setHue: function(h) {
+
+      // get an HSB model, and set the new hue...
+      var hsb = this.asHSB();
+      hsb.h = h;
+
+      // convert back to RGB...
+      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
+   },
+
+   setSaturation: function(s) {
+      // get an HSB model, and set the new hue...
+      var hsb = this.asHSB();
+      hsb.s = s;
+
+      // convert back to RGB and set values...
+      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
+   },
+
+   setBrightness: function(b) {
+      // get an HSB model, and set the new hue...
+      var hsb = this.asHSB();
+      hsb.b = b;
+
+      // convert back to RGB and set values...
+      this.rgb = Rico.Color.HSBtoRGB( hsb.h, hsb.s, hsb.b );
+   },
+
+   darken: function(percent) {
+      var hsb  = this.asHSB();
+      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.max(hsb.b - percent,0));
+   },
+
+   brighten: function(percent) {
+      var hsb  = this.asHSB();
+      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.min(hsb.b + percent,1));
+   },
+
+   blend: function(other) {
+      this.rgb.r = Math.floor((this.rgb.r + other.rgb.r)/2);
+      this.rgb.g = Math.floor((this.rgb.g + other.rgb.g)/2);
+      this.rgb.b = Math.floor((this.rgb.b + other.rgb.b)/2);
+   },
+
+   isBright: function() {
+      var hsb = this.asHSB();
+      return this.asHSB().b > 0.5;
+   },
+
+   isDark: function() {
+      return ! this.isBright();
+   },
+
+   asRGB: function() {
+      return "rgb(" + this.rgb.r + "," + this.rgb.g + "," + this.rgb.b + ")";
+   },
+
+   asHex: function() {
+      return "#" + this.rgb.r.toColorPart() + this.rgb.g.toColorPart() + this.rgb.b.toColorPart();
+   },
+
+   asHSB: function() {
+      return Rico.Color.RGBtoHSB(this.rgb.r, this.rgb.g, this.rgb.b);
+   },
+
+   toString: function() {
+      return this.asHex();
+   }
+
+};
+
+Rico.Color.createFromHex = function(hexCode) {
+  if(hexCode.length==4) {
+    var shortHexCode = hexCode; 
+    var hexCode = '#';
+    for(var i=1;i<4;i++)
+      hexCode += (shortHexCode.charAt(i) + shortHexCode.charAt(i));
+  }
+  if ( hexCode.indexOf('#') == 0 )
+    hexCode = hexCode.substring(1);
+  var red   = hexCode.substring(0,2);
+  var green = hexCode.substring(2,4);
+  var blue  = hexCode.substring(4,6);
+  return new Rico.Color( parseInt(red,16), parseInt(green,16), parseInt(blue,16) );
+}
+
+/**
+ * Factory method for creating a color from the background of
+ * an HTML element.
+ */
+Rico.Color.createColorFromBackground = function(elem) {
+
+   var actualColor = Element.getStyle(elem, "background-color");
+
+   // if color is tranparent, check parent
+   // Safari returns "rgba(0, 0, 0, 0)", which means transparent
+   if ( actualColor.match(/^(transparent|rgba\(0,\s*0,\s*0,\s*0\))$/i) && elem.parentNode )
+      return Rico.Color.createColorFromBackground(elem.parentNode);
+
+   if ( actualColor == null )
+      return new Rico.Color(255,255,255);
+
+   if ( actualColor.indexOf("rgb(") == 0 ) {
+      var colors = actualColor.substring(4, actualColor.length - 1 );
+      var colorArray = colors.split(",");
+      return new Rico.Color( parseInt( colorArray[0] ),
+                            parseInt( colorArray[1] ),
+                            parseInt( colorArray[2] )  );
+
+   }
+   else if ( actualColor.indexOf("#") == 0 ) {
+      return Rico.Color.createFromHex(actualColor);
+   }
+   else
+      return new Rico.Color(255,255,255);
+}
+
+Rico.Color.HSBtoRGB = function(hue, saturation, brightness) {
+
+   var red   = 0;
+       var green = 0;
+       var blue  = 0;
+
+   if (saturation == 0) {
+      red = parseInt(brightness * 255.0 + 0.5);
+          green = red;
+          blue = red;
+       }
+       else {
+      var h = (hue - Math.floor(hue)) * 6.0;
+      var f = h - Math.floor(h);
+      var p = brightness * (1.0 - saturation);
+      var q = brightness * (1.0 - saturation * f);
+      var t = brightness * (1.0 - (saturation * (1.0 - f)));
+
+      switch (parseInt(h)) {
+         case 0:
+            red   = (brightness * 255.0 + 0.5);
+            green = (t * 255.0 + 0.5);
+            blue  = (p * 255.0 + 0.5);
+            break;
+         case 1:
+            red   = (q * 255.0 + 0.5);
+            green = (brightness * 255.0 + 0.5);
+            blue  = (p * 255.0 + 0.5);
+            break;
+         case 2:
+            red   = (p * 255.0 + 0.5);
+            green = (brightness * 255.0 + 0.5);
+            blue  = (t * 255.0 + 0.5);
+            break;
+         case 3:
+            red   = (p * 255.0 + 0.5);
+            green = (q * 255.0 + 0.5);
+            blue  = (brightness * 255.0 + 0.5);
+            break;
+         case 4:
+            red   = (t * 255.0 + 0.5);
+            green = (p * 255.0 + 0.5);
+            blue  = (brightness * 255.0 + 0.5);
+            break;
+          case 5:
+            red   = (brightness * 255.0 + 0.5);
+            green = (p * 255.0 + 0.5);
+            blue  = (q * 255.0 + 0.5);
+            break;
+           }
+       }
+
+   return { r : parseInt(red), g : parseInt(green) , b : parseInt(blue) };
+}
+
+Rico.Color.RGBtoHSB = function(r, g, b) {
+
+   var hue;
+   var saturation;
+   var brightness;
+
+   var cmax = (r > g) ? r : g;
+   if (b > cmax)
+      cmax = b;
+
+   var cmin = (r < g) ? r : g;
+   if (b < cmin)
+      cmin = b;
+
+   brightness = cmax / 255.0;
+   if (cmax != 0)
+      saturation = (cmax - cmin)/cmax;
+   else
+      saturation = 0;
+
+   if (saturation == 0)
+      hue = 0;
+   else {
+      var redc   = (cmax - r)/(cmax - cmin);
+       var greenc = (cmax - g)/(cmax - cmin);
+       var bluec  = (cmax - b)/(cmax - cmin);
+
+       if (r == cmax)
+          hue = bluec - greenc;
+       else if (g == cmax)
+          hue = 2.0 + redc - bluec;
+      else
+          hue = 4.0 + greenc - redc;
+
+       hue = hue / 6.0;
+       if (hue < 0)
+          hue = hue + 1.0;
+   }
+
+   return { h : hue, s : saturation, b : brightness };
+}
+
+//-------------------- ricoCorner.js
+Rico.Corner = {
+
+   round: function(e, options) {
+      var e = $(e);
+      this._setOptions(options);
+      var color = this.options.color == "fromElement" ? this._background(e) : this.options.color;
+      var bgColor = this.options.bgColor == "fromParent" ? this._background(e.parentNode) : this.options.bgColor;
+      this._roundCornersImpl(e, color, bgColor);
+   },
+
+   _roundCornersImpl: function(e, color, bgColor) {
+      if(this.options.border)
+         this._renderBorder(e,bgColor);
+      if(this._isTopRounded())
+         this._roundTopCorners(e,color,bgColor);
+      if(this._isBottomRounded())
+         this._roundBottomCorners(e,color,bgColor);
+   },
+
+   _renderBorder: function(el,bgColor) {
+      var borderValue = "1px solid " + this._borderColor(bgColor);
+      var borderL = "border-left: "  + borderValue;
+      var borderR = "border-right: " + borderValue;
+      var style   = "style='" + borderL + ";" + borderR +  "'";
+      el.innerHTML = "<div " + style + ">" + el.innerHTML + "</div>"
+   },
+
+   _roundTopCorners: function(el, color, bgColor) {
+      var corner = this._createCorner(bgColor);
+      for(var i=0 ; i < this.options.numSlices ; i++ )
+         corner.appendChild(this._createCornerSlice(color,bgColor,i,"top"));
+      el.style.paddingTop = '0px';
+      el.insertBefore(corner,el.firstChild);
+   },
+
+   _roundBottomCorners: function(el, color, bgColor) {
+      var corner = this._createCorner(bgColor);
+      for(var i=(this.options.numSlices-1) ; i >= 0 ; i-- )
+         corner.appendChild(this._createCornerSlice(color,bgColor,i,"bottom"));
+      el.style.paddingBottom = 0;
+      el.appendChild(corner);
+   },
+
+   _createCorner: function(bgColor) {
+      var corner = document.createElement("div");
+      corner.style.backgroundColor = (this._isTransparent() ? "transparent" : bgColor);
+      return corner;
+   },
+
+   _createCornerSlice: function(color,bgColor, n, position) {
+      var slice = document.createElement("span");
+
+      var inStyle = slice.style;
+      inStyle.backgroundColor = color;
+      inStyle.display  = "block";
+      inStyle.height   = "1px";
+      inStyle.overflow = "hidden";
+      inStyle.fontSize = "1px";
+
+      var borderColor = this._borderColor(color,bgColor);
+      if ( this.options.border && n == 0 ) {
+         inStyle.borderTopStyle    = "solid";
+         inStyle.borderTopWidth    = "1px";
+         inStyle.borderLeftWidth   = "0px";
+         inStyle.borderRightWidth  = "0px";
+         inStyle.borderBottomWidth = "0px";
+         inStyle.height            = "0px"; // assumes css compliant box model
+         inStyle.borderColor       = borderColor;
+      }
+      else if(borderColor) {
+         inStyle.borderColor = borderColor;
+         inStyle.borderStyle = "solid";
+         inStyle.borderWidth = "0px 1px";
+      }
+
+      if ( !this.options.compact && (n == (this.options.numSlices-1)) )
+         inStyle.height = "2px";
+
+      this._setMargin(slice, n, position);
+      this._setBorder(slice, n, position);
+      return slice;
+   },
+
+   _setOptions: function(options) {
+      this.options = {
+         corners : "all",
+         color   : "fromElement",
+         bgColor : "fromParent",
+         blend   : true,
+         border  : false,
+         compact : false
+      }
+      Object.extend(this.options, options || {});
+
+      this.options.numSlices = this.options.compact ? 2 : 4;
+      if ( this._isTransparent() )
+         this.options.blend = false;
+   },
+
+   _whichSideTop: function() {
+      if ( this._hasString(this.options.corners, "all", "top") )
+         return "";
+
+      if ( this.options.corners.indexOf("tl") >= 0 && this.options.corners.indexOf("tr") >= 0 )
+         return "";
+
+      if (this.options.corners.indexOf("tl") >= 0)
+         return "left";
+      else if (this.options.corners.indexOf("tr") >= 0)
+          return "right";
+      return "";
+   },
+
+   _whichSideBottom: function() {
+      if ( this._hasString(this.options.corners, "all", "bottom") )
+         return "";
+
+      if ( this.options.corners.indexOf("bl")>=0 && this.options.corners.indexOf("br")>=0 )
+         return "";
+
+      if(this.options.corners.indexOf("bl") >=0)
+         return "left";
+      else if(this.options.corners.indexOf("br")>=0)
+         return "right";
+      return "";
+   },
+
+   _borderColor : function(color,bgColor) {
+      if ( color == "transparent" )
+         return bgColor;
+      else if ( this.options.border )
+         return this.options.border;
+      else if ( this.options.blend )
+         return this._blend( bgColor, color );
+      else
+         return "";
+   },
+
+
+   _setMargin: function(el, n, corners) {
+      var marginSize = this._marginSize(n);
+      var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();
+
+      if ( whichSide == "left" ) {
+         el.style.marginLeft = marginSize + "px"; el.style.marginRight = "0px";
+      }
+      else if ( whichSide == "right" ) {
+         el.style.marginRight = marginSize + "px"; el.style.marginLeft  = "0px";
+      }
+      else {
+         el.style.marginLeft = marginSize + "px"; el.style.marginRight = marginSize + "px";
+      }
+   },
+
+   _setBorder: function(el,n,corners) {
+      var borderSize = this._borderSize(n);
+      var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();
+      if ( whichSide == "left" ) {
+         el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = "0px";
+      }
+      else if ( whichSide == "right" ) {
+         el.style.borderRightWidth = borderSize + "px"; el.style.borderLeftWidth  = "0px";
+      }
+      else {
+         el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px";
+      }
+      if (this.options.border != false)
+        el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px";
+   },
+
+   _marginSize: function(n) {
+      if ( this._isTransparent() )
+         return 0;
+
+      var marginSizes          = [ 5, 3, 2, 1 ];
+      var blendedMarginSizes   = [ 3, 2, 1, 0 ];
+      var compactMarginSizes   = [ 2, 1 ];
+      var smBlendedMarginSizes = [ 1, 0 ];
+
+      if ( this.options.compact && this.options.blend )
+         return smBlendedMarginSizes[n];
+      else if ( this.options.compact )
+         return compactMarginSizes[n];
+      else if ( this.options.blend )
+         return blendedMarginSizes[n];
+      else
+         return marginSizes[n];
+   },
+
+   _borderSize: function(n) {
+      var transparentBorderSizes = [ 5, 3, 2, 1 ];
+      var blendedBorderSizes     = [ 2, 1, 1, 1 ];
+      var compactBorderSizes     = [ 1, 0 ];
+      var actualBorderSizes      = [ 0, 2, 0, 0 ];
+
+      if ( this.options.compact && (this.options.blend || this._isTransparent()) )
+         return 1;
+      else if ( this.options.compact )
+         return compactBorderSizes[n];
+      else if ( this.options.blend )
+         return blendedBorderSizes[n];
+      else if ( this.options.border )
+         return actualBorderSizes[n];
+      else if ( this._isTransparent() )
+         return transparentBorderSizes[n];
+      return 0;
+   },
+
+   _hasString: function(str) { for(var i=1 ; i<arguments.length ; i++) if (str.indexOf(arguments[i]) >= 0) return true; return false; },
+   _blend: function(c1, c2) { var cc1 = Rico.Color.createFromHex(c1); cc1.blend(Rico.Color.createFromHex(c2)); return cc1; },
+   _background: function(el) { try { return Rico.Color.createColorFromBackground(el).asHex(); } catch(err) { return "#ffffff"; } },
+   _isTransparent: function() { return this.options.color == "transparent"; },
+   _isTopRounded: function() { return this._hasString(this.options.corners, "all", "top", "tl", "tr"); },
+   _isBottomRounded: function() { return this._hasString(this.options.corners, "all", "bottom", "bl", "br"); },
+   _hasSingleTextChild: function(el) { return el.childNodes.length == 1 && el.childNodes[0].nodeType == 3; }
+}
+
+Rico.includeLoaded('ricoStyles.js');
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoTree.js b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/ricoTree.js
new file mode 100644 (file)
index 0000000..45f7a95
--- /dev/null
@@ -0,0 +1,318 @@
+//  Rico Tree Control\r
+//  by Matt Brown\r
+//  Oct 2006\r
+//  email: dowdybrown@yahoo.com\r
+\r
+//  Requires prototype.js and ricoCommon.js\r
+\r
+// each node in nodeIndex is an Array with 6+n positions\r
+//  node[0] is 0/1 when the node is closed/open\r
+//  node[1] is 0/1 when the folder is closed/open\r
+//  node[2] is 1 if the node is a leaf node\r
+//  node[3] is the node id\r
+//  node[4] is the node description\r
+//  node[5] is 1 when the node is selectable, 0 otherwise\r
+//  node[6]...node[6+n] are the child nodes\r
+\r
+Rico.TreeControl = Class.create();\r
+\r
+Rico.TreeControl.prototype = {\r
+\r
+  initialize: function(id,url,options) {\r
+    Object.extend(this, new Rico.Popup({ignoreClicks:true}));\r
+    Object.extend(this.options, {\r
+      nodeIdDisplay:'none',   // first, last, tooltip, or none\r
+      showCheckBox: false,\r
+      showFolders: false,\r
+      showPlusMinus: true,\r
+      defaultAction: this.nodeClick.bindAsEventListener(this),\r
+      height: '300px',\r
+      width: '300px',\r
+      leafIcon: Rico.imgDir+'doc.gif'\r
+    });\r
+    Object.extend(this.options, options || {});\r
+    this.img=[];\r
+    this.FirstChildNode=6;\r
+    this.nodeIndex={};\r
+    this.nodeCount=0;\r
+    this.foldersTree=0;\r
+    this.timeOutId=0;\r
+    this.id=id;\r
+    this.dataSource=url;\r
+    this.close=this.closePopup;\r
+  },\r
+\r
+  atLoad : function() {\r
+    var imgsrc = new Array("node.gif","nodelast.gif","folderopen.gif","folderclosed.gif");\r
+    for (i=0;i<imgsrc.length;i++) {\r
+      this.img[i] = new Image\r
+      this.img[i].src = Rico.imgDir+imgsrc[i]\r
+      //this.img[i].src = Rico.imgDir + imgsrc[i]\r
+    }\r
+    this.treeDiv=document.createElement("div");\r
+    this.treeDiv.id=this.id;\r
+    this.treeDiv.className='ricoTree';\r
+    this.treeDiv.style.height=this.options.height;\r
+    this.treeDiv.style.width=this.options.width;\r
+    this.container=document.createElement("div");\r
+    this.container.style.display="none"\r
+    this.container.className='ricoTreeContainer';\r
+    this.container.appendChild(this.treeDiv);\r
+    document.body.appendChild(this.container);\r
+    this.setDiv(this.container);\r
+    this.close();\r
+  },\r
+\r
+  // Building the data in the tree\r
+  open: function() {\r
+    this.openPopup();\r
+    if (this.nodeCount==0) this.loadXMLDoc();\r
+  },\r
+\r
+  loadXMLDoc: function(branchPin) {\r
+    var parms="id="+this.id;\r
+    if (branchPin) parms+="&Parent="+branchPin;\r
+    Rico.writeDebugMsg('Tree loadXMLDoc:\n'+parms+'\n'+this.dataSource);\r
+    new Ajax.Request(this.dataSource, {parameters:parms,method:'get',onComplete:this.processResponse.bind(this)});\r
+  },\r
+\r
+  processResponse: function(request) {\r
+    var response = request.responseXML.getElementsByTagName("ajax-response");\r
+    if (response == null || response.length != 1) return;\r
+    var rowsElement = response[0].getElementsByTagName('rows')[0];\r
+    var trs = rowsElement.getElementsByTagName("tr");\r
+    //alert('processResponse: '+trs.length);\r
+    for ( var i=0 ; i < trs.length; i++ ) {\r
+      var cells = trs[i].getElementsByTagName("td");\r
+      if (cells.length != 5) continue;\r
+      // cells[0]=parent node id\r
+      // cells[1]=node id\r
+      // cells[2]=description\r
+      // cells[3]=L/zero (leaf), C/non-zero (container)\r
+      // cells[4]= 0->not selectable, 1->selectable (use default action), otherwise the node is selectable and cells[4] contains the action\r
+      var content=[];\r
+      for (var j=0; j<cells.length; j++)\r
+        content[j]=this.getContent(cells[j]);\r
+        //content[j]=RicoUtil.getContentAsString(cells[j]);\r
+      var node=this.addNode(content[3],content[1],content[2],content[4]);\r
+      if (this.foldersTree==0) {\r
+        this.foldersTree = node;\r
+        node[0]=1;\r
+        node[1]=1;\r
+      } else {\r
+        var parentNode=this.nodeIndex[content[0]]\r
+        if (typeof parentNode=='undefined')\r
+          alert('ERROR!\nReceived invalid response from server - could not find parent in existing tree:\n'+content[0]);\r
+        else\r
+          parentNode.push(node);\r
+      }\r
+    }\r
+    if (this.nodeCount==1 && node[2])\r
+      this.loadXMLDoc(node[3]);\r
+    else\r
+      this.redrawTree();\r
+  },\r
+\r
+  getContent: function(cell) {\r
+    if (cell.innerHTML) return cell.innerHTML;\r
+    switch (cell.childNodes.length) {\r
+      case 0:  return "";\r
+      case 1:  return cell.firstChild.nodeValue;\r
+      default: return cell.childNodes[1].nodeValue;\r
+    }\r
+  },\r
+\r
+  // create new node\r
+  // NodeType is "C" or non-zero (container), or "L" or zero (leaf)\r
+  // id is the unique identifier for the node\r
+  // desc is the text displayed to the user\r
+  addNode: function(NodeType,id,desc,selectable) {\r
+    var arrayAux\r
+    //alert("addNode: " + desc + " (" + selectable + ")")\r
+    arrayAux = new Array\r
+    arrayAux[0] = 0\r
+    arrayAux[1] = 0\r
+    arrayAux[2] = (NodeType=='0' || NodeType.toUpperCase()=='L' ? 0 : 1)\r
+    arrayAux[3] = id\r
+    arrayAux[4] = desc\r
+    arrayAux[5] = parseInt(selectable);\r
+    this.nodeIndex[id]=arrayAux\r
+    this.nodeCount++;\r
+  \r
+    return arrayAux\r
+  },\r
+\r
+  RemoveAllChildren: function(obj) {\r
+       while (obj.hasChildNodes()) {\r
+               this.RemoveAllChildren(obj.childNodes[0])\r
+               obj.removeChild(obj.childNodes[0])\r
+       }\r
+  },\r
+\r
+  redrawTree: function() {\r
+    //alert('redrawTree');\r
+    this.RemoveAllChildren(this.treeDiv)\r
+    this.redrawNode(this.foldersTree, 0, 1, [])\r
+  },\r
+\r
+  DisplayImages: function(row,arNames) {\r
+    var i,img,td\r
+    for(i=0;i<arNames.length;i++) {\r
+      img = document.createElement("img")\r
+      img.src=Rico.imgDir+arNames[i] + ".gif"\r
+      td=row.insertCell(-1)\r
+      td.appendChild(img)\r
+    }\r
+  },\r
+\r
+  redrawNode: function(foldersNode, level, lastNode, leftSide) {\r
+    var tab,row\r
+    //alert("redrawNode at level " + level + " (" + foldersNode[3] + ")")\r
+    \r
+    tab = document.createElement("table")\r
+    tab.border=0\r
+    tab.cellSpacing=0\r
+    tab.cellPadding=0\r
+    row=tab.insertRow(0)\r
+    this.DisplayImages(row,leftSide)\r
+    var newLeft=leftSide.slice(0);\r
+    if (level>0) {\r
+      var suffix=lastNode ? 'last' : '';\r
+      if (this.options.showPlusMinus && foldersNode[2])\r
+        this.showPlusMinus(row.insertCell(-1),foldersNode,suffix);\r
+      else\r
+        this.NodeImage(row.insertCell(-1),suffix)\r
+      newLeft.push(lastNode ? "nodeblank" : "nodeline")\r
+    }\r
+    if (this.options.showFolders)\r
+      this.showFolders(row.insertCell(-1),foldersNode);\r
+    if (this.options.showCheckBox && foldersNode[5])\r
+      this.showCheckBox(row.insertCell(-1),foldersNode);\r
+    this.displayLabel(row,foldersNode)\r
+    this.treeDiv.appendChild(tab)\r
+  \r
+    if (foldersNode.length > this.FirstChildNode && foldersNode[0]) {\r
+      //there are sub-nodes and the folder is open\r
+      for (var i=this.FirstChildNode; i<foldersNode.length;i++)\r
+        this.redrawNode(foldersNode[i], level+1, (i==foldersNode.length-1 ? 1 : 0), newLeft)\r
+    }\r
+  },\r
+\r
+  NodeImage: function(td, suffix) {\r
+    var img\r
+    img = document.createElement("img")\r
+    img.src=Rico.imgDir+"node"+suffix+".gif"\r
+    td.appendChild(img)\r
+  },\r
+\r
+\r
+  showPlusMinus: function(td,foldersNode,suffix) {\r
+    var img = document.createElement("img")\r
+    img.name=foldersNode[3];\r
+    img.style.cursor='pointer';\r
+    if (foldersNode.length > this.FirstChildNode)\r
+      img.onclick=this.openBranch.bindAsEventListener(this);\r
+    else\r
+      img.onclick=this.getChildren.bindAsEventListener(this);\r
+    var prefix=foldersNode[1] ? "nodem" : "nodep"\r
+    img.src=Rico.imgDir+prefix+suffix+".gif";\r
+    td.appendChild(img)\r
+  },\r
+\r
+  showFolders: function(td,foldersNode) {\r
+    var img = document.createElement("img")\r
+    if (!foldersNode[2]) {\r
+      img.src=this.options.leafIcon;\r
+    } else {\r
+      img.name=foldersNode[3];\r
+      img.style.cursor='pointer';\r
+      if (foldersNode.length > this.FirstChildNode)\r
+        img.onclick=this.openBranch.bindAsEventListener(this);\r
+      else\r
+        img.onclick=this.getChildren.bindAsEventListener(this);\r
+      img.src=Rico.imgDir+(foldersNode[1] ? "folderopen.gif" : "folderclosed.gif");\r
+    }\r
+    td.appendChild(img)\r
+  },\r
+\r
+  showCheckBox: function(td,foldersNode) {\r
+    var inp=document.createElement("input")\r
+    inp.type="checkbox"\r
+    inp.name=foldersNode[3]\r
+    td.appendChild(inp)\r
+  },\r
+\r
+  displayLabel: function(row,foldersNode) {\r
+    if (foldersNode[5]) {\r
+      var span=document.createElement('a');\r
+      span.href='#';\r
+      span.onclick=this.options.defaultAction;\r
+    } else {\r
+      var span=document.createElement('p');\r
+    }\r
+    span.id=this.id+"__"+foldersNode[3];\r
+    var desc=foldersNode[4];\r
+    switch (this.options.nodeIdDisplay) {\r
+      case 'last': desc+=' ('+foldersNode[3]+')'; break;\r
+      case 'first': desc=foldersNode[3]+' - '+desc; break;\r
+      case 'tooltip': span.title=foldersNode[3]; break;\r
+    }\r
+       span.appendChild(document.createTextNode(desc))\r
+    var td=row.insertCell(-1)\r
+    td.appendChild(span)\r
+  },\r
+\r
+  //when a parent is closed all children also are\r
+  closeFolders: function(foldersNode) {\r
+    var i=0\r
+    if (foldersNode[2]) {\r
+      for (i=this.FirstChildNode; i< foldersNode.length; i++)\r
+        this.closeFolders(foldersNode[i])\r
+    }\r
+    foldersNode[0] = 0\r
+    foldersNode[1] = 0\r
+  },\r
+  \r
+  nodeClick: function(e) {\r
+    var node=Event.element(e);\r
+    if (this.returnValue) {\r
+      var v=node.id;\r
+      var i=v.indexOf('__');\r
+      if (i>=0) v=v.substr(i+2);\r
+      this.returnValue(v,node.innerHTML);\r
+    }\r
+    this.close();\r
+  },\r
+\r
+  //recurse over the tree structure\r
+  //called by openbranch\r
+  clickOnFolderRec: function(foldersNode, folderName) {\r
+    var i=0\r
+    if (foldersNode[3] == folderName) {\r
+      if (foldersNode[0]) {\r
+        this.closeFolders(foldersNode)\r
+      } else {\r
+        foldersNode[0] = 1\r
+        foldersNode[1] = 1\r
+      }\r
+    } else if (foldersNode[2]) {\r
+      for (i=this.FirstChildNode; i< foldersNode.length; i++)\r
+        this.clickOnFolderRec(foldersNode[i], folderName)\r
+    }\r
+  },\r
+\r
+  openBranch: function(e) {\r
+    var node=Event.element(e);\r
+    this.clickOnFolderRec(this.foldersTree, node.name)\r
+    this.timeOutId = setTimeout(this.redrawTree.bind(this),100)\r
+  },\r
+\r
+  getChildren: function(e) {\r
+    var node=Event.element(e);\r
+    this.loadXMLDoc(node.name)\r
+    this.openBranch(e)\r
+  }\r
+\r
+}\r
+\r
+Rico.includeLoaded('ricoTree.js');
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/translations/livegrid_de.js b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/translations/livegrid_de.js
new file mode 100644 (file)
index 0000000..530820a
--- /dev/null
@@ -0,0 +1,86 @@
+/*****************************************************************\r
+ Page : livegrid_DE.js\r
+ Description : LiveGrid text for German menus\r
+ Translator: rainer@langheiter@.com\r
+ Version 0.1 - please send corrections to dowdybrown@yahoo.com\r
+******************************************************************/\r
+// 2007-02-09, made some improvements - debach@gmx.de\r
+\r
+RicoTranslate.addPhrase("Sort by","Sortiert nach")\r
+RicoTranslate.addPhrase("Filter by","Gefiltert nach")\r
+RicoTranslate.addPhrase("Hide","Verbergen")\r
+RicoTranslate.addPhrase("Show","Zeige")\r
+RicoTranslate.addPhrase("Show All","Alle zeigen")\r
+RicoTranslate.addPhrase("Ascending","Aufsteigend")\r
+RicoTranslate.addPhrase("Descending","Absteigend")\r
+\r
+RicoTranslate.addPhrase("Include only this value","Nur diesen Wert einbeziehen")\r
+RicoTranslate.addPhrase("Exclude this value","Diesen Wert ausschließen")\r
+RicoTranslate.addPhrase("Exclude this value also","Diesen Wert ebenfalls ausschließen")\r
+RicoTranslate.addPhrase("Greater than or equal to this value","Größer oder gleich diesem Wert")\r
+RicoTranslate.addPhrase("Less than or equal to this value","Kleiner oder gleich diesem wert")\r
+RicoTranslate.addPhrase("Contains keyword","Enthält Schlüsselwort")\r
+RicoTranslate.addPhrase("Change keyword","Schlüsselwort ändern ")\r
+RicoTranslate.addPhrase("Enter keyword to search for","Schlüsselwort eintragen, nach dem gesucht werden soll")\r
+RicoTranslate.addPhrase("use * as a wildcard","verwende * als Wildcard")\r
+RicoTranslate.addPhrase("Remove filter","Entferne Filter")\r
+\r
+RicoTranslate.addPhrase("Waiting for data","Warte auf Daten")\r
+RicoTranslate.addPhrase("Request for data timed out","Zeitüberschreitung")\r
+\r
+RicoTranslate.addPhrase("No matching records","Keine übereinstimmenden Einträge")\r
+RicoTranslate.addPhrase("Listing records","Zeige Einträge")\r
+RicoTranslate.addPhrase("of","von")\r
+RicoTranslate.addPhrase("of about","von ungefähr")\r
+\r
+RicoTranslate.monthNames=["Januar", "Februar", "März", "April", "Mai","Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"]\r
+RicoTranslate.dayNames=["Sonntag", "Montag","Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"]\r
+\r
+RicoTranslate.thouSep="."\r
+RicoTranslate.decPoint=","\r
+RicoTranslate.dateFmt="dd.mm.yyyy"\r
+\r
+// added for 22 August release\r
+\r
+RicoTranslate.addPhrase("Print","Druck")\r
+RicoTranslate.addPhrase("Export","Exportiert")\r
+RicoTranslate.addPhrase("Visible rows","Sichtbare Zeilen")\r
+RicoTranslate.addPhrase("All rows","Alle Zeilen")\r
+\r
+// added for Feb 2007 release\r
+\r
+RicoTranslate.addPhrase("Loading","Lade")\r
+RicoTranslate.addPhrase("minutes before your session expires","Minuten, bevor Ihre Sitzung abläuft") // formal\r
+//RicoTranslate.addPhrase("minutes before your session expires","Minuten, bevor deine Sitzung abläuft") // informal\r
+//RicoTranslate.addPhrase("minutes before your session expires","Minuten, bevor die Sitzung abläuft") // indirect\r
+RicoTranslate.addPhrase("EXPIRED","ABGELAUFEN")\r
+RicoTranslate.addPhrase("Close","Schließen")\r
+RicoTranslate.addPhrase("Cancel","Abbrechen")\r
+RicoTranslate.addPhrase("Save","Speichere")\r
+RicoTranslate.addPhrase("Add","Erstelle")\r
+RicoTranslate.addPhrase("Edit","Bearbeite")\r
+RicoTranslate.addPhrase("Delete","Lösche")\r
+RicoTranslate.addPhrase("Are you sure you want to delete","Möchten Sie dies wirklich löschen:") // formal\r
+//RicoTranslate.addPhrase("Are you sure you want to delete","Möchtest du das wirklich löschen:") // informal\r
+//RicoTranslate.addPhrase("Are you sure you want to delete","Wirklich löschen:") // indirect\r
+RicoTranslate.addPhrase("record","Eintrag")\r
+RicoTranslate.addPhrase("this record","diesen Eintrag") // "dieser"?\r
+RicoTranslate.addPhrase("new record","neuen Eintrag") // "neuer"?\r
+RicoTranslate.addPhrase("Please enter","Bitte eingeben")\r
+RicoTranslate.addPhrase("a value for","einen Wert für")\r
+RicoTranslate.addPhrase("an integer value for","einen ganzzahligen Wert für")\r
+RicoTranslate.addPhrase("a positive integer value for","einen positiven ganzzahligen Wert für")\r
+RicoTranslate.addPhrase("The request returned an error","Die Anfrage ergab einen Fehler")\r
+\r
+// for demo only:\r
+\r
+RicoTranslate.addPhrase("Show orders for","Zeige Bestellungen für")\r
+RicoTranslate.addPhrase("Show order detail","Zeige Bestelldetails")\r
+RicoTranslate.addPhrase("order","Bestellung")\r
+RicoTranslate.addPhrase("this order","diese Bestellung")\r
+RicoTranslate.addPhrase("new order","neue Bestellung")\r
+\r
+// add your own here:\r
+\r
+RicoTranslate.addPhrase("Visible rows to web page","Sichtbare Zeilen auf eine Webseite")\r
+RicoTranslate.addPhrase("All rows to web page","Alle Zeilen auf eine Webseite")
\ No newline at end of file
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/translations/livegrid_es.js b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/translations/livegrid_es.js
new file mode 100644 (file)
index 0000000..dc5b8f6
--- /dev/null
@@ -0,0 +1,79 @@
+/*****************************************************************\r
+ Page : livegrid_es.js\r
+ Description : LiveGrid text for Spanish menus\r
+ Version 0.2 (by Marco Scarnatto)\r
+ If you have better translations, or would like to include\r
+ translations for another language, please send them to dowdybrown@yahoo.com\r
+******************************************************************/\r
+\r
+RicoTranslate.addPhrase("Sort by","Ordenar por")\r
+RicoTranslate.addPhrase("Filter by","Filtrar por")\r
+RicoTranslate.addPhrase("Hide","Ocultar")\r
+RicoTranslate.addPhrase("Show","Mostrar")\r
+RicoTranslate.addPhrase("Show All","Mostrar todo")\r
+RicoTranslate.addPhrase("Ascending","Ascendente")\r
+RicoTranslate.addPhrase("Descending","Descendente")\r
+\r
+RicoTranslate.addPhrase("Include only this value","Incluir solo este valor")\r
+RicoTranslate.addPhrase("Exclude this value","Excluir este valor")\r
+RicoTranslate.addPhrase("Exclude this value also","Excluir este valor también")\r
+RicoTranslate.addPhrase("Greater than or equal to this value","Mayor o igual a este valor")\r
+RicoTranslate.addPhrase("Less than or equal to this value","Menor o igual a este valor")\r
+RicoTranslate.addPhrase("Contains keyword","Contiene el texto")\r
+RicoTranslate.addPhrase("Change keyword","Cambiar texto")\r
+RicoTranslate.addPhrase("Enter keyword to search for","Ingrese texto a buscar")\r
+RicoTranslate.addPhrase("use * as a wildcard","use * como un comodín")\r
+RicoTranslate.addPhrase("Remove filter","Quitar filtro")\r
+\r
+RicoTranslate.addPhrase("Waiting for data","Esperando datos")\r
+RicoTranslate.addPhrase("Request for data timed out","Tiempo excedido en recibir datos ")\r
+\r
+RicoTranslate.addPhrase("No matching records","No hay datos coincidentes")\r
+RicoTranslate.addPhrase("Listing records","Mostrando datos")\r
+RicoTranslate.addPhrase("of","de")\r
+RicoTranslate.addPhrase("of about","de alrededor de")\r
+\r
+RicoTranslate.thouSep=","\r
+RicoTranslate.decPoint="."\r
+RicoTranslate.dateFmt="dd/mm/yyyy"\r
+\r
+RicoTranslate.monthNames=['Enero','Febrero', 'Marzo', 'Abril', 'Mayo','Junio', 'Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre']\r
+RicoTranslate.dayNames=['Domingo','Lunes','Martes','Miércoles','Jueves','Viernes','Sábado']\r
+\r
+// added for Aug 2006 release\r
+\r
+RicoTranslate.addPhrase("Print","Imprimir")\r
+RicoTranslate.addPhrase("Export","Exportar")\r
+RicoTranslate.addPhrase("Visible rows","Filas visibles")\r
+RicoTranslate.addPhrase("All rows","Todas las filas")\r
+\r
+// added for Feb 2007 release
+
+RicoTranslate.addPhrase("Loading","Cargar")
+RicoTranslate.addPhrase("minutes before your session expires","minutos antes de su sesión expiran")
+RicoTranslate.addPhrase("EXPIRED","EXPIRADO")\r
+RicoTranslate.addPhrase("Close","Cerrar")
+RicoTranslate.addPhrase("Cancel","Cancelar")
+RicoTranslate.addPhrase("Save","Guardar")
+RicoTranslate.addPhrase("Add","Añadir")
+RicoTranslate.addPhrase("Edit","Editar")
+RicoTranslate.addPhrase("Delete","Borrar")
+RicoTranslate.addPhrase("Are you sure you want to delete","¿Está seguro de que quiere borrar")\r
+RicoTranslate.addPhrase("record","expediente")
+RicoTranslate.addPhrase("this record","este expediente")
+RicoTranslate.addPhrase("new record","expediente nuevo")
+RicoTranslate.addPhrase("Please enter","Introduzca")
+RicoTranslate.addPhrase("a value for","un valor para")
+RicoTranslate.addPhrase("an integer value for","un valor del número entero para")
+RicoTranslate.addPhrase("a positive integer value for","un valor positivo del número entero para")
+RicoTranslate.addPhrase("The request returned an error","Un error ocurrió mientras que recibía datos")
+
+// for demo only:\r
+\r
+RicoTranslate.addPhrase("Show orders for","Mostrar las ordenes de")\r
+RicoTranslate.addPhrase("Show order detail","Mostrar detalle de la orden")\r
+RicoTranslate.addPhrase("order","la orden")\r
+RicoTranslate.addPhrase("this order","esta orden")\r
+RicoTranslate.addPhrase("new order","nueva orden")\r
+\r
+// add your own here:\r
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/translations/livegrid_fr.js b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/translations/livegrid_fr.js
new file mode 100644 (file)
index 0000000..7f92442
--- /dev/null
@@ -0,0 +1,79 @@
+/*****************************************************************
+ Page : livegrid_FR.js
+ Description : LiveGrid text for French menus
+ Version 0.3 (revisions by Pierre Grellet)
+ If you have better translations, or would like to include
+ translations for another language, please send them to dowdybrown@yahoo.com
+******************************************************************/
+
+RicoTranslate.addPhrase("Sort by","Trier par")
+RicoTranslate.addPhrase("Filter by","Filtrer par")
+RicoTranslate.addPhrase("Hide","Masquer")
+RicoTranslate.addPhrase("Show","Afficher")
+RicoTranslate.addPhrase("Show All","Afficher tous")
+RicoTranslate.addPhrase("Ascending","Croissant")
+RicoTranslate.addPhrase("Descending","Décroissant")
+
+RicoTranslate.addPhrase("Include only this value","Inclure seulement cette valeur")
+RicoTranslate.addPhrase("Exclude this value","Exclure cette valeur aussi")
+RicoTranslate.addPhrase("Exclude this value also","Exclure cette valeur")
+RicoTranslate.addPhrase("Greater than or equal to this value","Supérieur ou égal à cette valeur")
+RicoTranslate.addPhrase("Less than or equal to this value","Inférieur ou égal à cette valeur")
+RicoTranslate.addPhrase("Contains keyword","Contenant le mot-clé")
+RicoTranslate.addPhrase("Change keyword","Changer le mot-clé")
+RicoTranslate.addPhrase("Enter keyword to search for","Écrivez le mot-clé à rechercher")
+RicoTranslate.addPhrase("use * as a wildcard","utiliser * comme caractère générique")
+RicoTranslate.addPhrase("Remove filter","Enlever le filtre")
+
+RicoTranslate.addPhrase("Waiting for data","En attente des données")
+RicoTranslate.addPhrase("Request for data timed out","La requête a atteint sa limite de temps ")
+
+RicoTranslate.addPhrase("No matching records","Aucun articles ne correspond")
+RicoTranslate.addPhrase("Listing records","Résultats")
+RicoTranslate.addPhrase("of","de")
+RicoTranslate.addPhrase("of about","sur un total d'environ")
+
+RicoTranslate.thouSep="'"
+RicoTranslate.decPoint="."
+RicoTranslate.dateFmt="dd.mm.yyyy"
+
+RicoTranslate.monthNames=['Janvier','Février', 'Mars', 'Avril', 'Mai','Juin', 'Juillet','Août','Septembre','Octobre','Novembre','Décembre']
+RicoTranslate.dayNames=['Dimanche','Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi']
+
+// added for 22 August release
+
+RicoTranslate.addPhrase("Print","Imprimer")
+RicoTranslate.addPhrase("Export","Exporter")
+RicoTranslate.addPhrase("Visible rows","Rangs visible")
+RicoTranslate.addPhrase("All rows","Tous les rangs")
+
+// added for Feb 2007 release
+
+RicoTranslate.addPhrase("Loading","Chargement")
+RicoTranslate.addPhrase("minutes before your session expires","minutes avant votre session expire")
+RicoTranslate.addPhrase("EXPIRED","EXPIRÉ")
+RicoTranslate.addPhrase("Close","Fermer")
+RicoTranslate.addPhrase("Cancel","Annuler")
+RicoTranslate.addPhrase("Save","Sauvegarder")
+RicoTranslate.addPhrase("Add","Ajouter")
+RicoTranslate.addPhrase("Edit","Éditer")
+RicoTranslate.addPhrase("Delete","Supprimer")
+RicoTranslate.addPhrase("Are you sure you want to delete","Êtes-vous sûr de vouloir supprimer")
+RicoTranslate.addPhrase("record","enregistrement")
+RicoTranslate.addPhrase("this record","cet enregistrement")
+RicoTranslate.addPhrase("new record","nouvel enregistrement")
+RicoTranslate.addPhrase("Please enter","Veuillez saisir")
+RicoTranslate.addPhrase("a value for","une valeur pour")
+RicoTranslate.addPhrase("an integer value for","un nombre entier pour")
+RicoTranslate.addPhrase("a positive integer value for","un nombre positif pour")
+RicoTranslate.addPhrase("The request returned an error","La requête a renvoyé une erreur")
+
+// for demo only:
+
+RicoTranslate.addPhrase("Show orders for","Montrer les commandes pour")
+RicoTranslate.addPhrase("Show order detail","Montrer les détails de la commande")
+RicoTranslate.addPhrase("order","la commande")\r
+RicoTranslate.addPhrase("this order","cette commande")\r
+RicoTranslate.addPhrase("new order","nouvelle commande")\r
+
+// add your own here:
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/translations/livegrid_it.js b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/translations/livegrid_it.js
new file mode 100644 (file)
index 0000000..efc7124
--- /dev/null
@@ -0,0 +1,80 @@
+/*****************************************************************
+ Page : livegrid_IT.js
+ Description : LiveGrid text for Italian menus
+ Version 0.1 (these are guesses based mostly on babelfish results)
+ If you have better translations, or would like to include
+ translations for another language, please send them to dowdybrown@yahoo.com
+******************************************************************/
+
+RicoTranslate.addPhrase("Sort by","Ordina per")
+RicoTranslate.addPhrase("Filter by","Filtra per")
+RicoTranslate.addPhrase("Hide","Nascondi")
+RicoTranslate.addPhrase("Show","Mostra")
+RicoTranslate.addPhrase("Show All","Mostra Tutte")
+RicoTranslate.addPhrase("Ascending","Crescente")
+RicoTranslate.addPhrase("Descending","Decrescente")
+
+RicoTranslate.addPhrase("Include only this value","Ritieni soltanto questo valore")
+RicoTranslate.addPhrase("Exclude this value","Escludi questo valore")
+RicoTranslate.addPhrase("Exclude this value also","Escludi questo valore anche")
+RicoTranslate.addPhrase("Greater than or equal to this value","Superiore o uguale a questo valore")
+RicoTranslate.addPhrase("Less than or equal to this value","Inferiore o uguale a questo valore")
+RicoTranslate.addPhrase("Contains keyword","Contiene la parola chiave")
+RicoTranslate.addPhrase("Change keyword","Cambia la parola chiave")
+RicoTranslate.addPhrase("Enter keyword to search for","Entra la parola chiave da cercare")
+RicoTranslate.addPhrase("use * as a wildcard","usa * come \"jolly\"")
+RicoTranslate.addPhrase("Remove filter","Rimuovi filtro")
+
+RicoTranslate.addPhrase("Waiting for data","Attendere per i dati")
+RicoTranslate.addPhrase("Request for data timed out","La richiesta dei dati ha raggiunto il limite di tempo")
+
+RicoTranslate.addPhrase("No matching records","Nessun articoli corrispondenti")
+RicoTranslate.addPhrase("Listing records","Risultati")
+RicoTranslate.addPhrase("of","di")
+RicoTranslate.addPhrase("of about","di circa")
+
+RicoTranslate.monthNames=["Gennaio", "Febbraio", "Marzo", "Aprile", "Maggio", "Giugno", "Luglio", "Agosto", "Settembre", "Ottobre", "Novembre", "Dicembre"]
+RicoTranslate.dayNames=["Domenica", "Lunedì", "Martedì", "Mercoledì", "Giovedì", "Venerdì", "Sabato"]
+
+RicoTranslate.thouSep="."
+RicoTranslate.decPoint=","
+RicoTranslate.dateFmt="dd/mm/yyyy"
+
+// added for Feb 2007 release
+
+RicoTranslate.addPhrase("Loading","Caricamento")
+RicoTranslate.addPhrase("minutes before your session expires","minuti prima che la vostra sessione termini")
+RicoTranslate.addPhrase("EXPIRED","TERMINATA")
+RicoTranslate.addPhrase("Close","Chiudi")
+RicoTranslate.addPhrase("Cancel","Annulla")
+RicoTranslate.addPhrase("Save","Salva")
+RicoTranslate.addPhrase("Add","Aggiungi")
+RicoTranslate.addPhrase("Edit","Modifica")
+RicoTranslate.addPhrase("Delete","Cancella")
+RicoTranslate.addPhrase("Are you sure you want to delete","Siete sicuri che volete cancellare")
+RicoTranslate.addPhrase("record","registrazione")
+RicoTranslate.addPhrase("this record","qesta registrazione")
+RicoTranslate.addPhrase("new record","nuova registrazione")
+RicoTranslate.addPhrase("Please enter","Per favore inserire")
+RicoTranslate.addPhrase("a value for","un valore per")
+RicoTranslate.addPhrase("an integer value for","un numero intero per")
+RicoTranslate.addPhrase("a positive integer value for","un numero intero positivo per")
+RicoTranslate.addPhrase("The request returned an error","La richiesta ha generato un errore")
+
+// for demo only:
+
+RicoTranslate.addPhrase("Show orders for","Mostra gli ordini per")
+RicoTranslate.addPhrase("Show order detail","Mostra i dettagli dell'ordine")
+RicoTranslate.addPhrase("order","ordine")\r
+RicoTranslate.addPhrase("this order","questo ordine")\r
+RicoTranslate.addPhrase("new order","nuovo ordine")\r
+
+// added for 22 August release
+
+RicoTranslate.addPhrase("Print","Stampa")
+RicoTranslate.addPhrase("Export","Esporta")
+RicoTranslate.addPhrase("Visible rows","Righe visibili")
+RicoTranslate.addPhrase("All rows","Tutte le righe")
+
+// add your own here:
+
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/translations/livegrid_ja.js b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/translations/livegrid_ja.js
new file mode 100644 (file)
index 0000000..479f9e2
--- /dev/null
@@ -0,0 +1,49 @@
+/*****************************************************************\r
+ Page : livegrid_ja.js\r
+ Description : LiveGrid text for Japanese menus\r
+ Translator: hsur\r
+******************************************************************/\r
+\r
+RicoTranslate.addPhrase("Sort by","ソート")\r
+RicoTranslate.addPhrase("Filter by","絞込み")\r
+RicoTranslate.addPhrase("Hide","隠す")\r
+RicoTranslate.addPhrase("Show","表示")\r
+RicoTranslate.addPhrase("Ascending","昇順")\r
+RicoTranslate.addPhrase("Descending","降順")\r
+\r
+RicoTranslate.addPhrase("Include only this value","この値を含む")\r
+RicoTranslate.addPhrase("Exclude this value","この値を除く")\r
+RicoTranslate.addPhrase("Greater than or equal to this value","これ以上の値")\r
+RicoTranslate.addPhrase("Less than or equal to this value","これ以下の値")\r
+RicoTranslate.addPhrase("Contains keyword","このキーワードを含む")\r
+RicoTranslate.addPhrase("Change keyword","キーワードを変更")\r
+RicoTranslate.addPhrase("Enter keyword to search for","キーワードを入力してください")\r
+RicoTranslate.addPhrase("use * as a wildcard","ワイルドカードとして*を使います")\r
+RicoTranslate.addPhrase("also","も")\r
+RicoTranslate.addPhrase("Remove filter","絞込みを削除")\r
+\r
+RicoTranslate.addPhrase("Waiting for data","データ取得中")\r
+RicoTranslate.addPhrase("Request for data timed out","タイムアウトしました")\r
+\r
+RicoTranslate.addPhrase("No matching records","一致するレコードはありません")\r
+RicoTranslate.addPhrase("Listing records","")\r
+RicoTranslate.addPhrase("of","of")\r
+RicoTranslate.addPhrase("of about","of about")\r
+\r
+RicoTranslate.monthNames=["1月", "2月", "3月", "4月", "5月","6月", "7月", "8月", "9月", "10月", "11月", "12月"]\r
+RicoTranslate.dayNames=["月","火", "水","木", "金", "土", "日"]\r
+\r
+RicoTranslate.thouSep="."\r
+RicoTranslate.decPoint=","\r
+RicoTranslate.dateFmt="yyyy/mm/dd"\r
+RicoTranslate.timeFmt="HH:nn:ss"\r
+\r
+// added for 22 August release\r
+\r
+RicoTranslate.addPhrase("Print","印刷")\r
+RicoTranslate.addPhrase("Export","抽出")\r
+RicoTranslate.addPhrase("Visible rows","Visible rows")\r
+RicoTranslate.addPhrase("All rows","全ての行")\r
+\r
+// add your own here:\r
+\r
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/translations/livegrid_pt.js b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/translations/livegrid_pt.js
new file mode 100644 (file)
index 0000000..bdaa9d2
--- /dev/null
@@ -0,0 +1,55 @@
+/*****************************************************************\r
+ Page : livegrid_pt.js (brazilian)\r
+ Description : LiveGrid text for Brazilian Portuguese menus\r
+ Version 0.1 (by Adriano Accorsi - adriano@token.com.br)\r
+ If you have better translations, or would like to include\r
+ translations for another language, please send them to dowdybrown@yahoo.com\r
+******************************************************************/\r
\r
+RicoTranslate.addPhrase("Sort by","Ordenar por")\r
+RicoTranslate.addPhrase("Filter by","Filtrar por")\r
+RicoTranslate.addPhrase("Hide","Ocultar")\r
+RicoTranslate.addPhrase("Show","Exibir")\r
+RicoTranslate.addPhrase("Show All","Exibir tudo")\r
+RicoTranslate.addPhrase("Ascending","Ascendente")\r
+RicoTranslate.addPhrase("Descending","Descendente")\r
\r
+RicoTranslate.addPhrase("Include only this value","Incluir apenas este valor")\r
+RicoTranslate.addPhrase("Exclude this value","Excluir este valor")\r
+RicoTranslate.addPhrase("Greater than or equal to this value","Maior ou igual a este valor")\r
+RicoTranslate.addPhrase("Less than or equal to this value","Menor ou igual a este valor")\r
+RicoTranslate.addPhrase("Contains keyword","Contém texto")\r
+RicoTranslate.addPhrase("Change keyword","Alterar texto")\r
+RicoTranslate.addPhrase("Enter keyword to search for","Informe texto a ser pesquisado")\r
+RicoTranslate.addPhrase("use * as a wildcard","use * como 'coringa'")\r
+RicoTranslate.addPhrase("also","também")\r
+RicoTranslate.addPhrase("Remove filter","Remover filtro")\r
\r
+RicoTranslate.addPhrase("Waiting for data","Aguardando dados")\r
+RicoTranslate.addPhrase("Request for data timed out","Tempo esgotado para espera de dados")\r
\r
+RicoTranslate.addPhrase("No matching records","Nenhum registro encontrado.")\r
+RicoTranslate.addPhrase("Listing records","Listar registros")\r
+RicoTranslate.addPhrase("of","de")\r
+RicoTranslate.addPhrase("of about","de aproximadamente")\r
\r
+RicoTranslate.thouSep="."\r
+RicoTranslate.decPoint=","\r
+RicoTranslate.dateFmt="dd/mm/yyyy"\r
\r
+RicoTranslate.monthNames=["Janeiro", "Fevereiro", "Março", "Abril", "Maio","Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"]\r
+RicoTranslate.dayNames=["Domingo", "Segunda","Terça", "Quarta", "Quinta", "Sexta", "Sábado"]\r
\r
+// added for 22 August release\r
+\r
+RicoTranslate.addPhrase("Print","Imprimir")\r
+RicoTranslate.addPhrase("Export","Exportar")\r
+RicoTranslate.addPhrase("Visible rows","Fileiras visíveis")\r
+RicoTranslate.addPhrase("All rows","Todas as fileiras")\r
+\r
+// for demo only:\r
\r
+RicoTranslate.addPhrase("Show orders for","Exibir pedidos de")\r
+RicoTranslate.addPhrase("Show order detail","Exibir detalhes do pedido")\r
\r
+// add your own here:\r
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/translations/livegrid_ru.js b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/translations/livegrid_ru.js
new file mode 100644 (file)
index 0000000..c4ceea7
--- /dev/null
@@ -0,0 +1,82 @@
+/*****************************************************************\r
+ Page : livegrid_ru.js\r
+ Description : LiveGrid text for Russian menus\r
+ Version 0.2 (by Illiya Gannitskiy,Alexey Uvarov)\r
+ If you have better translations, or would like to include\r
+ translations for another language, please send them to dowdybrown@yahoo.com\r
+******************************************************************/\r
+\r
+RicoTranslate.addPhrase("Sort by","Ñîðòèðîâêà ïî")\r
+RicoTranslate.addPhrase("Filter by","Ôèëüòðàöèÿ ïî")\r
+RicoTranslate.addPhrase("Hide","Ñïðÿòàòü")\r
+RicoTranslate.addPhrase("Show","Ïîêàçàòü")\r
+RicoTranslate.addPhrase("Show All","Ïîêàçàòü âñå")\r
+RicoTranslate.addPhrase("Ascending","Âîçðàñòàþùàÿ")\r
+RicoTranslate.addPhrase("Descending","Óáûâàþùàÿ")\r
+\r
+RicoTranslate.addPhrase("Include only this value","Âêëþ÷èòü òîëüêî ýòî çíà÷åíèå")\r
+RicoTranslate.addPhrase("Exclude this value","Èñêëþ÷èòü ýòî çíà÷åíèå")\r
+RicoTranslate.addPhrase("Greater than or equal to this value","Áîëüøå èëè ðàâíî äàííîìó çíà÷åíèþ")\r
+RicoTranslate.addPhrase("Less than or equal to this value","Ìåíüøå èëè ðàâíî äàííîìó çíà÷åíèþ")\r
+RicoTranslate.addPhrase("Contains keyword","Ñîäåðæèò çíà÷åíèå")\r
+RicoTranslate.addPhrase("Change keyword","Èçìåíèòü çíà÷åíèå")\r
+RicoTranslate.addPhrase("Enter keyword to search for","Èñêàòü ïî êëþ÷ó")\r
+RicoTranslate.addPhrase("use * as a wildcard","Èñïîëüçîâàòü * äëÿ âñåõ çàïèñåé")\r
+RicoTranslate.addPhrase("also","òàêæå")\r
+RicoTranslate.addPhrase("Remove filter","Óáðàòü ôèëüòð")\r
+\r
+RicoTranslate.addPhrase("Waiting for data","Îæèäàíèå äàííûõ")\r
+RicoTranslate.addPhrase("Request for data timed out","Ïðåâûøåí èíòåðâàë îæèäàíèÿ äàííûõ")\r
+\r
+RicoTranslate.addPhrase("No matching records","Íåò ñîâïàäåíèé")\r
+RicoTranslate.addPhrase("Listing records","Ïðîñìîòð çàïèñåé")\r
+RicoTranslate.addPhrase("of","èç")\r
+RicoTranslate.addPhrase("of about","èç î")\r
+\r
+RicoTranslate.thouSep=","\r
+RicoTranslate.decPoint="."\r
+RicoTranslate.dateFmt="dd/mm/yyyy"\r
+\r
+RicoTranslate.monthNames=['ßíâàðü','Ôåâðàëü', 'Ìàðò', 'Àïðåëü', 'Ìàé','Èþíü', 'Èþëü','Àâãóñò','Ñåíòÿáðü','Îòêòÿáðü','Íîÿáðü','Äåêàáðü']\r
+RicoTranslate.dayNames=['Ïîíåäåëüíèê','Âòîðíèê','Ñðåäà','×åòâåðã','Ïÿòíèöà','Ñóááîòà','Âîñêðåñåíüå']\r
+\r
+// added for 22 August release\r
+\r
+RicoTranslate.addPhrase("Print","Ïå÷àòü")\r
+RicoTranslate.addPhrase("Export","Ýêñïîðò")\r
+RicoTranslate.addPhrase("Visible rows","Âèäèìûå çàïèñè")\r
+RicoTranslate.addPhrase("All rows","Âñå çàïèñè")\r
+\r
+// added for Feb 2007 release\r
+\r
+RicoTranslate.addPhrase("Loading","Çàãðóçêà")\r
+RicoTranslate.addPhrase("minutes before your session expires","ìèíóò äî èñòå÷åíèÿ ñåññèè")\r
+RicoTranslate.addPhrase("EXPIRED","ÈÑÒÅÊËÎ")\r
+RicoTranslate.addPhrase("Close","Çàêðûòî")\r
+RicoTranslate.addPhrase("Cancel","Îòìåíà")\r
+RicoTranslate.addPhrase("Save","Ñîõðàíèòü")\r
+RicoTranslate.addPhrase("Add","Äîáàâèòü")\r
+RicoTranslate.addPhrase("Edit","Ðåäàêòèðîâàòü")\r
+RicoTranslate.addPhrase("Delete","Óäàëèòü")\r
+RicoTranslate.addPhrase("Are you sure you want to delete","Âû óâåðåííû,÷òî õîòèòå óäàëèòü")\r
+RicoTranslate.addPhrase("record","çàïèñü")\r
+RicoTranslate.addPhrase("this record","ýòà çàïèñü")\r
+RicoTranslate.addPhrase("new record","íîâàÿ çàïèñü")\r
+RicoTranslate.addPhrase("Please enter","Ïîæàëóéñòà ââåäèòå")\r
+RicoTranslate.addPhrase("a value for","çíà÷åíèå äëÿ")\r
+RicoTranslate.addPhrase("an integer value for","öåëîå çíà÷åíèå äëÿ")\r
+RicoTranslate.addPhrase("a positive integer value for","ïîëîæèòåëüíîå öåëîå çíà÷åíèå äëÿ")\r
+RicoTranslate.addPhrase("The request returned an error","Çàïðîñ âîçâðàòèë îøèáêó")\r
+\r
+\r
+\r
+// for demo only:\r
+\r
+RicoTranslate.addPhrase("Show orders for","Ïîêàçàòü çíà÷åíèÿ äëÿ")\r
+RicoTranslate.addPhrase("Show order detail","Ïîêàçàòü çíà÷åíèÿ ïîäðîáíî")\r
+RicoTranslate.addPhrase("order","çíà÷åíèå")\r
+RicoTranslate.addPhrase("this order","ýòî çíà÷åíèå")\r
+RicoTranslate.addPhrase("new order","íîâîå çíà÷åíèå")\r
+\r
+\r
+// add your own here:\r
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/translations/livegrid_ua.js b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/translations/livegrid_ua.js
new file mode 100644 (file)
index 0000000..18206c8
--- /dev/null
@@ -0,0 +1,82 @@
+/*****************************************************************\r
+ Page : livegrid_ua.js\r
+ Description : LiveGrid text for Ukrainian menus\r
+ Version 0.1 (by Illiya Gannitskiy,Alexey Uvarov)\r
+ If you have better translations, or would like to include\r
+ translations for another language, please send them to dowdybrown@yahoo.com\r
+******************************************************************/\r
+\r
+RicoTranslate.addPhrase("Sort by","Cîðòóâàííÿ ïî")\r
+RicoTranslate.addPhrase("Filter by","Ô³ëüòðàö³ÿ ïî")\r
+RicoTranslate.addPhrase("Hide","Ñõîâàòè")\r
+RicoTranslate.addPhrase("Show","Ïîêàçàòè")\r
+RicoTranslate.addPhrase("Show All","Ïîêàçàòè âñå")\r
+RicoTranslate.addPhrase("Ascending","Çðîñòàþ÷à")\r
+RicoTranslate.addPhrase("Descending","Óáóâàþ÷à")\r
+\r
+RicoTranslate.addPhrase("Include only this value","Âêëþ÷èòè ò³ëüêè öå çíà÷åííÿ")\r
+RicoTranslate.addPhrase("Exclude this value","Âèêëþ÷èòè öå çíà÷åííÿ")\r
+RicoTranslate.addPhrase("Greater than or equal to this value","Á³ëüøå àáî ð³âíî äàíîìó çíà÷åííþ")\r
+RicoTranslate.addPhrase("Less than or equal to this value","Ìåíøå àáî ð³âíî äàíîìó çíà÷åííþ")\r
+RicoTranslate.addPhrase("Contains keyword","̳ñòèòü çíà÷åííÿ")\r
+RicoTranslate.addPhrase("Change keyword","Çì³íèòè çíà÷åííÿ")\r
+RicoTranslate.addPhrase("Enter keyword to search for","Øóêàòè ïî êëþ÷ó")\r
+RicoTranslate.addPhrase("use * as a wildcard","Âèêîðèñòîâóâàòè * äëÿ âñ³õ çàïèñ³â")\r
+RicoTranslate.addPhrase("also","òàêîæ")\r
+RicoTranslate.addPhrase("Remove filter","Ïðèáðàòè ô³ëüòð")\r
+\r
+RicoTranslate.addPhrase("Waiting for data","Î÷³êóâàííÿ äàíèõ")\r
+RicoTranslate.addPhrase("Request for data timed out","Ïåðåâèùåíèé ³íòåðâàë î÷³êóâàííÿ äàíèõ")\r
+\r
+RicoTranslate.addPhrase("No matching records","Íåìຠçá³ã³â")\r
+RicoTranslate.addPhrase("Listing records","Ïåðåãëÿä çàïèñ³â")\r
+RicoTranslate.addPhrase("of","ç")\r
+RicoTranslate.addPhrase("of about","ç î")\r
+\r
+RicoTranslate.thouSep=","\r
+RicoTranslate.decPoint="."\r
+RicoTranslate.dateFmt="dd/mm/yyyy"\r
+\r
+RicoTranslate.monthNames=['ѳ÷åíü','Ëþòèé', 'Áåðåçåíü', 'Êâ³òåíü', 'Òðàâåíü','×åðâåíü', 'Ëèïåíü','Ñåðïåíü','Âåðåñåíü','Æîâòåíü','Ëèñòîïàä','Ãðóäåíü']\r
+RicoTranslate.dayNames=['Ïîíåä³ëîê','³âòîðîê','Ñåðåäà','×åòâåð','Ï'ÿòíèöÿ','Ñóáîòà','Íåä³ëÿ']\r
+\r
+// added for 22 August release\r
+\r
+RicoTranslate.addPhrase("Print","Äðóê")\r
+RicoTranslate.addPhrase("Export","Åêñïîðò")\r
+RicoTranslate.addPhrase("Visible rows","Âèäèì³ çàïèñè")\r
+RicoTranslate.addPhrase("All rows","Âñ³ çàïèñè")\r
+\r
+// added for Feb 2007 release\r
+\r
+RicoTranslate.addPhrase("Loading","Çàâàíòàæåííÿ")\r
+RicoTranslate.addPhrase("minutes before your session expires","õâèëèí äî çàê³í÷åííÿ ñåñ³¿")\r
+RicoTranslate.addPhrase("EXPIRED","ÇÀʲÍ×ÈËÎÑß")\r
+RicoTranslate.addPhrase("Close","Çàêðèòî")\r
+RicoTranslate.addPhrase("Cancel","³äì³íà")\r
+RicoTranslate.addPhrase("Save","Çáåðåãòè")\r
+RicoTranslate.addPhrase("Add","Äîäàòè")\r
+RicoTranslate.addPhrase("Edit","Ðåäàãóâàòè")\r
+RicoTranslate.addPhrase("Delete","Âèäàëèòè")\r
+RicoTranslate.addPhrase("Are you sure you want to delete","Âè óïåâíåí³,ùî áàæàºòå âèäàëèòè")\r
+RicoTranslate.addPhrase("record","çàïèñ")\r
+RicoTranslate.addPhrase("this record","öåé çàïèñ")\r
+RicoTranslate.addPhrase("new record","íîâèé çàïèñ")\r
+RicoTranslate.addPhrase("Please enter","Áóäü ëàñêà, ââåä³òü")\r
+RicoTranslate.addPhrase("a value for","çíà÷åííÿ äëÿ")\r
+RicoTranslate.addPhrase("an integer value for","ö³ëå çíà÷åííÿ äëÿ")\r
+RicoTranslate.addPhrase("a positive integer value for","ïîçèòèâíå ö³ëå çíà÷åííÿ äëÿ")\r
+RicoTranslate.addPhrase("The request returned an error","Çàïèò ïîâåðíóâ ïîìèëêó")\r
+\r
+\r
+\r
+// for demo only:\r
+\r
+RicoTranslate.addPhrase("Show orders for","Ïîêàçàòè çíà÷åííÿ")\r
+RicoTranslate.addPhrase("Show order detail","Ïîêàçàòè çíà÷åííÿ äåòàëüíî")\r
+RicoTranslate.addPhrase("order","Çíà÷åííÿ")\r
+RicoTranslate.addPhrase("this order","öå çíà÷åííÿ")\r
+RicoTranslate.addPhrase("new order","íîâå çíà÷åííÿ")\r
+\r
+\r
+// add your own here:\r
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/js/rico/translations/livegrid_zh.js b/NP_TrackBack/branches/DOM-branch/trackback/js/rico/translations/livegrid_zh.js
new file mode 100644 (file)
index 0000000..96b9a78
--- /dev/null
@@ -0,0 +1,55 @@
+/*****************************************************************
+ Page : livegrid_zh.js
+ Description : LiveGrid text for CHINA menus
+ Translator: Sam Shan at gz_shanming@yahoo.com
+ Version 0.1 - please send corrections to dowdybrown@yahoo.com
+ Version 0.2 - Updated based on Xinjun liu's comments.
+******************************************************************/
+
+RicoTranslate.addPhrase("Sort by","排序")
+RicoTranslate.addPhrase("Filter by","过滤")
+RicoTranslate.addPhrase("Hide","隐藏")
+RicoTranslate.addPhrase("Show","显示")
+RicoTranslate.addPhrase("Ascending","升序")
+RicoTranslate.addPhrase("Descending","降序")
+
+RicoTranslate.addPhrase("Include only this value","只包含此值")
+RicoTranslate.addPhrase("Exclude this value","不包含此值")
+RicoTranslate.addPhrase("Greater than or equal to this value","大于等于")
+RicoTranslate.addPhrase("Less than or equal to this value","小于等于")
+RicoTranslate.addPhrase("Contains keyword","包含关键字")
+RicoTranslate.addPhrase("Change keyword","更改关键字")
+RicoTranslate.addPhrase("Enter keyword to search for","输入关键字")
+RicoTranslate.addPhrase("use * as a wildcard","使用*通配符")
+RicoTranslate.addPhrase("also","也")
+RicoTranslate.addPhrase("Remove filter","移除过滤器")
+
+RicoTranslate.addPhrase("Waiting for data","等待接收数据")
+RicoTranslate.addPhrase("Request for data timed out","等待数据超时")
+
+RicoTranslate.addPhrase("No matching records","没有匹配的数据")
+RicoTranslate.addPhrase("Listing records","列出纪录")
+RicoTranslate.addPhrase("of","共")
+RicoTranslate.addPhrase("of about","大约")
+
+RicoTranslate.monthNames=["一月", "二月", "三月", "四月", "五月","六月", "七月", "八月", "九月", "十月", "十一月", "十二月"]
+RicoTranslate.dayNames=["星期日","星期一", "星期二","星期三", "星期四", "星期五", "星期六"]
+
+RicoTranslate.thouSep="."
+RicoTranslate.decPoint=","
+RicoTranslate.dateFmt="yyyy/mm/dd"
+
+// added for 22 August release
+
+RicoTranslate.addPhrase("Print","打印")
+RicoTranslate.addPhrase("Export","输出")
+RicoTranslate.addPhrase("Visible rows","当前显示记录")
+RicoTranslate.addPhrase("All rows","所有记录")
+
+// for demo only:
+
+RicoTranslate.addPhrase("Show orders for","显示订单")
+RicoTranslate.addPhrase("Show order detail","显示订单详情")
+
+// add your own here:
+
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/language/english.php b/NP_TrackBack/branches/DOM-branch/trackback/language/english.php
new file mode 100644 (file)
index 0000000..b81e490
--- /dev/null
@@ -0,0 +1,87 @@
+<?php
+// plugin description
+define('_TB_DESCRIPTION',               'Send trackbacks to other weblogs and receive tracbacks from others.');
+
+// BLOG settings messages
+define('_TB_isAcceptWOLinkDef',         'Accept pings w/o link ? (blog default)');
+define('_TB_isAcceptWOLinkDef_VAL',     'yes|yes|no (block)|block|no (ignore)|ignore');
+define('_TB_AllowTrackBack',            'Accept pings to this blog');
+define('_TB_NotifyEmailBlog',           'Which e-mail address to send these notification to?');
+
+// ITEM add/eit form messages
+define('_TB_LIST_IT',                   'List it');
+define('_TB_ItemAcceptPing',            'Accept pings');
+define('_TB_isAcceptWOLink',            'Accept pings w/o link ?');
+define('_TB_isAcceptWOLink_VAL',        'default|default|yes|yes|no|no');
+
+// Global plugin options messages
+define('_TB_AcceptPing',                'Accept pings');
+define('_TB_SendPings',                 'Allow sending pings');
+define('_TB_AutoXMLHttp',               'Auto-detect Trackback URLs as you type');
+define('_TB_CheckIDs',                  'Only allow valid itemids as trackback-ids');
+define('_TB_NotifyEmail',               'Which e-mail address to send these notification to?');
+define('_TB_DropTable',                 'Clear the database when uninstalling');
+define('_TB_HideUrl',                   'Hide external URL');
+define('_TB_ajaxEnabled',               'Enable Ajax ?');
+
+// notify e-mail template
+define('_TB_NORTIFICATION_MAIL_BODY',   "Your weblog received a new trackback from <%blogname%> for ID <%tb_id%>. "
+                                                                         . "Below are the full details:\n\n"
+                                                                         . "URL:\t<%url%>\nTitle:\t<%title%>\nExcerpt:\t<%excerpt%>\nBlogname:\t<%blogname%>");
+define('_TB_NORTIFICATION_MAIL_TITLE',  "New Trackback received for ID <%tb_id%>");
+
+// template title
+define('_TB_dateFormat',                'Date format');
+define('_TB_tplHeader',                 'Header');
+define('_TB_tplEmpty',                  'Empty');
+define('_TB_tplItem',                   'Item');
+define('_TB_tplFooter',                 'Footer');
+define('_TB_tplLocalHeader',            'Header (Local)');
+define('_TB_tplLocalEmpty',             'Empty (Local)');
+define('_TB_tplLocalItem',              'Item (Local)');
+define('_TB_tplLocalFooter',            'Footer (Local)');
+define('_TB_tplNO_ACCEPT',              'No accept message');
+define('_TB_tplTbNone',                 'Trackback count (none)');
+define('_TB_tplTbOne',                  'Trackback count (one)');
+define('_TB_tplTbMore',                 'Trackback count (more)');
+
+// template values
+define('_TB_dateFormat_VAL',            "%e/%m/%g");
+define('_TB_tplHeader_VAL',             "<div class=\"tb\">\n\t<div class=\"head\">Trackback</div><%admin%>\n\n");
+define('_TB_tplEmpty_VAL',              "\t<div class=\"empty\">\n"
+                                                                         . "\t\tThere are currently no trackbacks for this item.\n\t</div>\n\n");
+define('_TB_tplItem_VAL',               "\t<div class=\"item\">\n\t\t<div class=\"name\"><%name%></div>\n"
+                                                                         . "\t\t<div class=\"body\">\n\t\t\t<a href=\"<%url%>\"><%title%>:</a> <%excerpt%>\n"
+                                                                         . "\t\t</div>\n\t\t<div class=\"date\">\n\t\t\t<%date%>\n\t\t</div>\n\t</div>\n\n");
+define('_TB_tplFooter_VAL',             "\t<div class=\"info\">\n\t\tUse this <a href=\"<%action%>\">TrackBack url</a> "
+                                                                         . "to ping this item (right-click, copy link target).\n"
+                                                                         . "\t\tIf your blog does not support Trackbacks you can manually add your trackback by using "
+                                                                         . "<a href=\"<%form%>\" onclick=\"window.open(this.href, 'trackback', 'scrollbars=yes,width=600,height=340,left=10,top=10,status=yes,resizable=yes'); return false;\">"
+                                                                         . "this form</a>.\n\t</div>\n</div>");
+define('_TB_tplLocalHeader_VAL',        "<div class=\"tblocal\">\n\t<div class=\"head\">Local Trackback</div>\n\n");
+define('_TB_tplLocalEmpty_VAL',         "");
+define('_TB_tplLocalItem_VAL',          "\t<div class=\"item\">\n\t\t<div class=\"body\">\n\t\t\t<%delete%> <a href=\"<%url%>\">"
+                                                                         . "<%title%></a>: <%excerpt%>\n\t\t</div>\n\t\t<div class=\"date\">\n\t\t\t<%timestamp%>\n"
+                                                                         . "\t\t</div>\n\t</div>\n\n");
+define('_TB_tplLocalFooter_VAL',        "\t</div>");
+
+// error messages
+define('_TB_msgNOTALLOWED_SEND',        "You're not allowed to send pings");
+define('_TB_msgDISABLED_SEND',          "Sending trackback pings is disabled");
+define('_TB_msgNO_SENDER_URL',          "No ping URL");
+define('_TB_msgBAD_SENDER_URL',         'Bad ping URL');
+define('_TB_msgCOULDNOT_SEND_PING',     'Could not send ping: %s');
+define('_TB_msgRESP_HTTP_ERROR',        'An error occurred: HTTP Error: [%s] %s');
+define('_TB_msgAN_ERROR_OCCURRED',      "An error occurred: %s");
+define('_TB_msgTBID_IS_MISSING',        "TrackBack ID is missing (tb_id)");
+define('_TB_msgTB_COULDNOT_TB_UPDATE',  'Could not update trackback data: %s');
+define('_TB_msgDUPLICATED_TB_BLOCKED',  "Trackback: Duplicated Blocked Trackback [ignore] (itemid:%d from: %s)");
+define('_TB_msgLINK_CHECK_OK',          "Trackback: LinkCheck OK. (link: %s pat: %s )");
+define('_TB_msgLINK_CHECK_IGNORE',      "Trackback: LinkCheck NG. [ignore] (itemid:%d from: %s cnt: %s pat: %s)");
+define('_TB_msgLINK_CHECK_BLOCK',       "Trackback: LinkCheck NG. [block] (itemid:%d from: %s cnt: %s pat: %s)");
+define('_TB_msgCOULDNOT_SAVE_DOUBLE',   'Could not save trackback data, possibly because of a double entry: ');
+
+
+
+
+
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/language/japanese-euc.php b/NP_TrackBack/branches/DOM-branch/trackback/language/japanese-euc.php
new file mode 100644 (file)
index 0000000..c7dd64a
--- /dev/null
@@ -0,0 +1,69 @@
+<?php 
+// plugin description
+define('_TB_DESCRIPTION',               '¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤Î¼õÁ÷¿®¤ò¹Ô¤¤¤Þ¤¹');
+
+// BLOG settings messages
+define('_TB_isAcceptWOLinkDef',         '¸ÀµÚ¥ê¥ó¥¯¤¬¤Ê¤¯¤Æ¤âTB¤ò¼õÉÕ¤¹¤ë¤«? (blog¥Ç¥Õ¥©¥ë¥È)');
+define('_TB_isAcceptWOLinkDef_VAL',     '¤Ï¤¤|yes|¤¤¤¤¤¨(ÊÝα)|block|¤¤¤¤¤¨(̵»ë)|ignore');
+define('_TB_AllowTrackBack',            '¤³¤Î¥Ö¥í¥°¤ÇTB¤ò¼õÉÕ¤¹¤ë¤«?');
+define('_TB_NotifyEmailBlog',           'ping¼õÉÕ»þ¤Î¥á¡¼¥ëÁ÷¿®Àè(;¤Ç¶èÀڤäÆÊ£¿ôÆþÎϲÄǽ)');
+
+// ITEM add/eit form messages
+define('_TB_LIST_IT',                   'Á÷¿®¥ê¥¹¥È¤ËÄɲÃ');
+define('_TB_ItemAcceptPing',            'TB¤ò¼õÉÕ¤¹¤ë¤«?');
+define('_TB_isAcceptWOLink',            '¸ÀµÚ¥ê¥ó¥¯¤¬¤Ê¤¯¤Æ¤âTB¤ò¼õÉÕ¤¹¤ë¤«?');
+define('_TB_isAcceptWOLink_VAL',        '¥Ö¥í¥°¥Ç¥Õ¥©¥ë¥È¤Ë½¾¤¦|default|¤Ï¤¤|yes|¤¤¤¤¤¨|no');
+
+// Global plugin options messages
+define('_TB_AcceptPing',                '¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤Î¼õÉÕ¤ò¤¹¤ë¤«?');
+define('_TB_SendPings',                 '¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤ÎÁ÷¿®¤ò²Äǽ¤Ë¤¹¤ë¤«?');
+define('_TB_AutoXMLHttp',               'autodiscoveryµ¡Ç½(µ­»öÆâ¤Î¥ê¥ó¥¯Àè¤ÎTrackbackURL¤Î¼«Æ°¸¡ÃÎ)¤ò»È¤¦¤«?');
+define('_TB_CheckIDs',                  'ping¼õÉÕ»þ¤ËÍ­¸ú¤Êitemid¤«¤É¤¦¤«¤ò¥Á¥§¥Ã¥¯¤¹¤ë¤«?');
+define('_TB_NotifyEmail',               'ping¼õÉÕ»þ¤Î¥á¡¼¥ëÁ÷¿®Àè(;¤Ç¶èÀڤäÆÊ£¿ôÆþÎϲÄǽ)');
+define('_TB_DropTable',                 '¥×¥é¥°¥¤¥ó¤Îºï½ü»þ¤Ë¥Ç¡¼¥¿¤òºï½ü¤¹¤ë¤«?');
+define('_TB_HideUrl',                   '°ìÍ÷ɽ¼¨¤ÎºÝ¤Ë³°Éô¤ÎURL¤ò¥ê¥À¥¤¥ì¥¯¥È¤ËÊÑ´¹¤¹¤ë¤«?');
+define('_TB_ajaxEnabled',               '´ÉÍý²èÌ̤ÇAjax¤òÍ­¸ú¤Ë¤¹¤ë¤«');
+
+// notify e-mail template
+define('_TB_NORTIFICATION_MAIL_BODY',   "<%blogname%> ¤«¤é ID:<%tb_id%> ¤Îµ­»ö¤ËÂФ·¤Æ¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤ò¼õ¿®¤·¤Þ¤·¤¿¡£ "
+                                                                         . "¾ÜºÙ¤Ï²¼µ­¤Î¤È¤ª¤ê¤Ç¤¹:\n\n"
+                                                                         . "URL:\t<%url%>\n¥¿¥¤¥È¥ë:\t<%title%>\n³µÍ×:\t<%excerpt%>\n¥Ö¥í¥°Ì¾:\t<%blogname%>");
+define('_TB_NORTIFICATION_MAIL_TITLE',  "¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤ò¼õ¿®¤·¤Þ¤·¤¿ ID:<%tb_id%>");
+
+// template title
+define('_TB_dateFormat',                'ÆüÉդηÁ¼°');
+define('_TB_tplHeader',                 'TB°ìÍ÷¥Æ¥ó¥×¥ì¡¼¥È(¥Ø¥Ã¥ÀÉô)');
+define('_TB_tplEmpty',                  'TB°ìÍ÷¥Æ¥ó¥×¥ì¡¼¥È(0·ï¤Î¤È¤­)');
+define('_TB_tplItem',                   'TB°ìÍ÷¥Æ¥ó¥×¥ì¡¼¥È(¥¢¥¤¥Æ¥àÉô)');
+define('_TB_tplFooter',                 'TB°ìÍ÷¥Æ¥ó¥×¥ì¡¼¥È(¥Õ¥Ã¥¿Éô)');
+define('_TB_tplLocalHeader',            '¥í¡¼¥«¥ëTB°ìÍ÷¥Æ¥ó¥×¥ì¡¼¥È(¥Ø¥Ã¥ÀÉô)');
+define('_TB_tplLocalEmpty',             '¥í¡¼¥«¥ëTB°ìÍ÷¥Æ¥ó¥×¥ì¡¼¥È(0·ï¤Î¤È¤­)');
+define('_TB_tplLocalItem',              '¥í¡¼¥«¥ëTB°ìÍ÷¥Æ¥ó¥×¥ì¡¼¥È(¥¢¥¤¥Æ¥àÉô)');
+define('_TB_tplLocalFooter',            '¥í¡¼¥«¥ëTB°ìÍ÷¥Æ¥ó¥×¥ì¡¼¥È(¥Õ¥Ã¥¿Éô)');
+define('_TB_tplNO_ACCEPT',              '¥È¥é¥Ã¥¯¥Ð¥Ã¥¯µñÈݤλþ¤Î¥á¥Ã¥»¡¼¥¸');
+define('_TB_tplTbNone',                 'TB¿ôɽ¼¨·Á¼°(0·ï)');
+define('_TB_tplTbOne',                  'TB¿ôɽ¼¨·Á¼°(1·ï)');
+define('_TB_tplTbMore',                 'TB¿ôɽ¼¨·Á¼°(2·ï°Ê¾å)');
+
+// template values
+define('_TB_dateFormat_VAL',            "%Y/%m/%d %H:%I");
+define('_TB_tplHeader_VAL',             "<div class=\"tb\">\n\t<div class=\"head\">¥È¥é¥Ã¥¯¥Ð¥Ã¥¯</div><%admin%>\n\n");
+define('_TB_tplEmpty_VAL',              "\t<div class=\"empty\">\n\t\t¤³¤Î¥¨¥ó¥È¥ê¤Ë¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤Ï¤¢¤ê¤Þ¤»¤ó\n\t</div>\n\n");
+
+define('_TB_tplItem_VAL',               "\t<div class=\"item\">\n\t\t<div class=\"name\"><%name%></div>\n"
+                                                                         . "\t\t<div class=\"body\">\n\t\t\t<a href=\"<%url%>\"><%title%>:</a> <%excerpt%>\n"
+                                                                         . "\t\t</div>\n\t\t<div class=\"date\">\n\t\t\t<%date%>\n\t\t</div>\n\t</div>\n\n");
+define('_TB_tplFooter_VAL',             "\t<div class=\"info\">\n\t\t¤³¤Î<a href=\"<%action%>\">¥È¥é¥Ã¥¯¥Ð¥Ã¥¯URL</a>¤ò»È¤Ã¤Æ¤³¤Îµ­»ö¤Ë¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤òÁ÷¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£\n"
+                                                                         . "\t\t¤â¤·¤¢¤Ê¤¿¤Î¥Ö¥í¥°¤¬¥È¥é¥Ã¥¯¥Ð¥Ã¥¯Á÷¿®¤ËÂбþ¤·¤Æ¤¤¤Ê¤¤¾ì¹ç¤Ë¤Ï"
+                                                                         . "<a href=\"<%form%>\" onclick=\"window.open(this.href, 'trackback', 'scrollbars=yes,width=600,height=340,left=10,top=10,status=yes,resizable=yes'); return false;\">¤³¤Á¤é¤Î¥Õ¥©¡¼¥à</a>"
+                                                                         . "¤«¤é¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤òÁ÷¿®¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£.\n\t</div>\n</div>");
+define('_TB_tplLocalHeader_VAL',        "<div class=\"tblocal\">\n\t<div class=\"head\">¥í¡¼¥«¥ë¥È¥é¥Ã¥¯¥Ð¥Ã¥¯</div>\n\n");
+define('_TB_tplLocalEmpty_VAL',         "");
+define('_TB_tplLocalItem_VAL',          "\t<div class=\"item\">\n\t\t<div class=\"body\">\n\t\t\t<%delete%> <a href=\"<%url%>\"><%title%></a>: <%excerpt%>\n"
+                                                                         . "\t\t</div>\n\t\t<div class=\"date\">\n\t\t\t<%timestamp%>\n\t\t</div>\n\t</div>\n\n");
+define('_TB_tplLocalFooter_VAL',        "\t</div>");
+
+
+
+
+
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/language/japanese-utf8.php b/NP_TrackBack/branches/DOM-branch/trackback/language/japanese-utf8.php
new file mode 100644 (file)
index 0000000..9ecea5c
--- /dev/null
@@ -0,0 +1,84 @@
+<?php 
+// plugin description
+define('_TB_DESCRIPTION',               'トラックバックの受送信を行います');
+
+// BLOG settings messages
+define('_TB_isAcceptWOLinkDef',         '言及リンクがなくてもTBを受付するか? (blogデフォルト)');
+define('_TB_isAcceptWOLinkDef_VAL',     'はい|yes|いいえ(保留)|block|いいえ(無視)|ignore');
+define('_TB_AllowTrackBack',            'このブログでTBを受付するか?');
+define('_TB_NotifyEmailBlog',           'ping受付時のメール送信先(;で区切って複数入力可能)');
+
+// ITEM add/eit form messages
+define('_TB_LIST_IT',                   '送信リストに追加');
+define('_TB_ItemAcceptPing',            'TBを受付するか?');
+define('_TB_isAcceptWOLink',            '言及リンクがなくてもTBを受付するか?');
+define('_TB_isAcceptWOLink_VAL',        'ブログデフォルトに従う|default|はい|yes|いいえ|no');
+
+// Global plugin options messages
+define('_TB_AcceptPing',                'トラックバックの受付をするか?');
+define('_TB_SendPings',                 'トラックバックの送信を可能にするか?');
+define('_TB_AutoXMLHttp',               'autodiscovery機能(記事内のリンク先のTrackbackURLの自動検知)を使うか?');
+define('_TB_CheckIDs',                  'ping受付時に有効なitemidかどうかをチェックするか?');
+define('_TB_NotifyEmail',               'ping受付時のメール送信先(;で区切って複数入力可能)');
+define('_TB_DropTable',                 'プラグインの削除時にデータを削除するか?');
+define('_TB_HideUrl',                   '一覧表示の際に外部のURLをリダイレクトに変換するか?');
+define('_TB_ajaxEnabled',               '管理画面でAjaxを有効にするか');
+
+// notify e-mail template
+define('_TB_NORTIFICATION_MAIL_BODY',   "<%blogname%> から ID:<%tb_id%> の記事に対してトラックバックを受信しました。 "
+                                                                         . "詳細は下記のとおりです:\n\n"
+                                                                         . "URL:\t<%url%>\nタイトル:\t<%title%>\n概要:\t<%excerpt%>\nブログ名:\t<%blogname%>");
+define('_TB_NORTIFICATION_MAIL_TITLE',  "トラックバックを受信しました ID:<%tb_id%>");
+
+// template title
+define('_TB_dateFormat',                '日付の形式');
+define('_TB_tplHeader',                 'TB一覧テンプレート(ヘッダ部)');
+define('_TB_tplEmpty',                  'TB一覧テンプレート(0件のとき)');
+define('_TB_tplItem',                   'TB一覧テンプレート(アイテム部)');
+define('_TB_tplFooter',                 'TB一覧テンプレート(フッタ部)');
+define('_TB_tplLocalHeader',            'ローカルTB一覧テンプレート(ヘッダ部)');
+define('_TB_tplLocalEmpty',             'ローカルTB一覧テンプレート(0件のとき)');
+define('_TB_tplLocalItem',              'ローカルTB一覧テンプレート(アイテム部)');
+define('_TB_tplLocalFooter',            'ローカルTB一覧テンプレート(フッタ部)');
+define('_TB_tplNO_ACCEPT',              'トラックバック拒否の時のメッセージ');
+define('_TB_tplTbNone',                 'TB数表示形式(0件)');
+define('_TB_tplTbOne',                  'TB数表示形式(1件)');
+define('_TB_tplTbMore',                 'TB数表示形式(2件以上)');
+
+// template values
+define('_TB_dateFormat_VAL',            "%Y/%m/%d %H:%I");
+define('_TB_tplHeader_VAL',             "<div class=\"tb\">\n\t<div class=\"head\">トラックバック</div><%admin%>\n\n");
+define('_TB_tplEmpty_VAL',              "\t<div class=\"empty\">\n\t\tこのエントリにトラックバックはありません\n\t</div>\n\n");
+
+define('_TB_tplItem_VAL',               "\t<div class=\"item\">\n\t\t<div class=\"name\"><%name%></div>\n"
+                                                                         . "\t\t<div class=\"body\">\n\t\t\t<a href=\"<%url%>\"><%title%>:</a> <%excerpt%>\n"
+                                                                         . "\t\t</div>\n\t\t<div class=\"date\">\n\t\t\t<%date%>\n\t\t</div>\n\t</div>\n\n");
+define('_TB_tplFooter_VAL',             "\t<div class=\"info\">\n\t\tこの<a href=\"<%action%>\">トラックバックURL</a>を使ってこの記事にトラックバックを送ることができます。\n"
+                                                                         . "\t\tもしあなたのブログがトラックバック送信に対応していない場合には"
+                                                                         . "<a href=\"<%form%>\" onclick=\"window.open(this.href, 'trackback', 'scrollbars=yes,width=600,height=340,left=10,top=10,status=yes,resizable=yes'); return false;\">こちらのフォーム</a>"
+                                                                         . "からトラックバックを送信することができます。.\n\t</div>\n</div>");
+define('_TB_tplLocalHeader_VAL',        "<div class=\"tblocal\">\n\t<div class=\"head\">ローカルトラックバック</div>\n\n");
+define('_TB_tplLocalEmpty_VAL',         "");
+define('_TB_tplLocalItem_VAL',          "\t<div class=\"item\">\n\t\t<div class=\"body\">\n\t\t\t<%delete%> <a href=\"<%url%>\"><%title%></a>: <%excerpt%>\n"
+                                                                         . "\t\t</div>\n\t\t<div class=\"date\">\n\t\t\t<%timestamp%>\n\t\t</div>\n\t</div>\n\n");
+define('_TB_tplLocalFooter_VAL',        "\t</div>");
+
+// error messages
+define('_TB_msgNOTALLOWED_SEND',        "トラックバックの送信が許可されていません");
+define('_TB_msgDISABLED_SEND',          "トラックバックの送信が無効に設定されています");
+define('_TB_msgNO_SENDER_URL',          "トラックバック送信先のURLが空です");
+define('_TB_msgBAD_SENDER_URL',         "トラックバック送信先のURLが正しくありません");
+define('_TB_msgCOULDNOT_SEND_PING',     "エラーのためにトラックバックが送信出来ませんでした(エラー:%s)");
+define('_TB_msgRESP_HTTP_ERROR',        "エラーが発生しました:HTTPエラー : [%s] %s");
+define('_TB_msgAN_ERROR_OCCURRED',      "エラーが発生しました:%s");
+define('_TB_msgTBID_IS_MISSING',        "トラックバックIDが指定されていません");
+define('_TB_msgTB_COULDNOT_TB_UPDATE',  "トラックバックデータを更新できませんでした: %s");
+define('_TB_msgDUPLICATED_TB_BLOCKED',  "Trackback: itemid:%dへの%sからのトラックバックをブロックしました[拒否]");
+define('_TB_msgLINK_CHECK_OK',          "Trackback: リンクチェック OK. (link: %s pat: %s )");
+define('_TB_msgLINK_CHECK_IGNORE',      "Trackback: リンクチェック NG. [拒否] (itemid:%d from: %s cnt: %s pat: %s)");
+define('_TB_msgLINK_CHECK_BLOCK',       "Trackback: リンクチェック NG. [ブロック] (itemid:%d from: %s cnt: %s pat: %s);
+define('_TB_msgCOULDNOT_SAVE_DOUBLE',   'データ重複のためトラックバックを保存できませんでした: ');
+
+
+
+
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/mkeuc.sh b/NP_TrackBack/branches/DOM-branch/trackback/mkeuc.sh
new file mode 100644 (file)
index 0000000..d9f44be
--- /dev/null
@@ -0,0 +1,11 @@
+#!/bin/bash -x\r
+\r
+FILES=`find japanese-utf8.templates -name '*ml'`\r
+\r
+for utf8file in $FILES\r
+do\r
+       eucfile=`echo $utf8file | sed 's/japanese-utf8/japanese-euc/'`\r
+       nkf -e -W -d < $utf8file > $eucfile\r
+done\r
+\r
+nkf -e -W -d < japanese-utf8.help.html > japanese-euc.help.html\r
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/silk/accept.png b/NP_TrackBack/branches/DOM-branch/trackback/silk/accept.png
new file mode 100644 (file)
index 0000000..f103759
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/silk/accept.png differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/silk/application_view_list.png b/NP_TrackBack/branches/DOM-branch/trackback/silk/application_view_list.png
new file mode 100644 (file)
index 0000000..1745b48
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/silk/application_view_list.png differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/silk/cross.png b/NP_TrackBack/branches/DOM-branch/trackback/silk/cross.png
new file mode 100644 (file)
index 0000000..2802c2c
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/silk/cross.png differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/silk/delete.png b/NP_TrackBack/branches/DOM-branch/trackback/silk/delete.png
new file mode 100644 (file)
index 0000000..49d3864
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/silk/delete.png differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/silk/exclamation.png b/NP_TrackBack/branches/DOM-branch/trackback/silk/exclamation.png
new file mode 100644 (file)
index 0000000..39d4592
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/silk/exclamation.png differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/silk/help.png b/NP_TrackBack/branches/DOM-branch/trackback/silk/help.png
new file mode 100644 (file)
index 0000000..477a015
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/silk/help.png differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/silk/house_go.png b/NP_TrackBack/branches/DOM-branch/trackback/silk/house_go.png
new file mode 100644 (file)
index 0000000..396ecb7
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/silk/house_go.png differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/silk/link.png b/NP_TrackBack/branches/DOM-branch/trackback/silk/link.png
new file mode 100644 (file)
index 0000000..93d4f0e
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/silk/link.png differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/silk/link_break.png b/NP_TrackBack/branches/DOM-branch/trackback/silk/link_break.png
new file mode 100644 (file)
index 0000000..244b018
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/silk/link_break.png differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/silk/plugin_edit.png b/NP_TrackBack/branches/DOM-branch/trackback/silk/plugin_edit.png
new file mode 100644 (file)
index 0000000..80c9a62
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/silk/plugin_edit.png differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/silk/readme.txt b/NP_TrackBack/branches/DOM-branch/trackback/silk/readme.txt
new file mode 100644 (file)
index 0000000..400a64d
--- /dev/null
@@ -0,0 +1,22 @@
+Silk icon set 1.3\r
+\r
+_________________________________________\r
+Mark James\r
+http://www.famfamfam.com/lab/icons/silk/\r
+_________________________________________\r
+\r
+This work is licensed under a\r
+Creative Commons Attribution 2.5 License.\r
+[ http://creativecommons.org/licenses/by/2.5/ ]\r
+\r
+This means you may use it for any purpose,\r
+and make any changes you like.\r
+All I ask is that you include a link back\r
+to this page in your credits.\r
+\r
+Are you using this icon set? Send me an email\r
+(including a link or picture if available) to\r
+mjames@gmail.com\r
+\r
+Any other questions about this icon set please\r
+contact mjames@gmail.com
\ No newline at end of file
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/silk/tick.png b/NP_TrackBack/branches/DOM-branch/trackback/silk/tick.png
new file mode 100644 (file)
index 0000000..b2d0522
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/silk/tick.png differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/silk/transmit_go.png b/NP_TrackBack/branches/DOM-branch/trackback/silk/transmit_go.png
new file mode 100644 (file)
index 0000000..57993b7
Binary files /dev/null and b/NP_TrackBack/branches/DOM-branch/trackback/silk/transmit_go.png differ
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/template.php b/NP_TrackBack/branches/DOM-branch/trackback/template.php
new file mode 100644 (file)
index 0000000..64ffe8a
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+class Trackback_Template {
+    var $vars; 
+
+    function Trackback_Template($file = null, $prefix = '') {
+        $this->file = ($prefix ? $prefix . '/' : '') . $file;
+               $this->prefix = $prefix;
+    }
+
+    function set($name, $value) {
+        $this->vars[$name] = is_object($value) ? $value->fetch() : $value;
+    }
+       
+       function template($file = null) {
+               $language = ereg_replace( '[\\|/]', '', getLanguageName());
+               $this->file = (file_exists(($this->prefix ? $this->prefix . '/' : '') . $language.'.'.$file))? ($this->prefix ? $this->prefix . '/' : '') . $language.'.'.$file: ($this->prefix ? $this->prefix . '/' : '') . $file;
+       }
+
+    function fetch($file = null) {
+        if(!$file) $file = $this->file;
+               else ($prefix ? $prefix . '/' : '') . $file;
+               
+               if ($file != null)
+               {
+               if (is_array($this->vars)) extract($this->vars);          
+        
+                       ob_start();
+               include($file);
+               $contents = ob_get_contents();
+               ob_end_clean();
+        
+                       return $contents;
+               }
+    }
+}
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/templates/all.html b/NP_TrackBack/branches/DOM-branch/trackback/templates/all.html
new file mode 100644 (file)
index 0000000..56c75b1
--- /dev/null
@@ -0,0 +1,106 @@
+<?php global $manager; ?>
+<h2>
+       All trackbacks
+       <?php if ($count > $amount): ?>
+               (Page <?php echo ceil($start / $amount) + 1;?> of <?php echo ceil($count / $amount);?>)
+       <?php endif; ?>
+</h2>
+
+<?php if(count($items)): ?>
+<?php if ($count > $amount): ?>
+<table class="navigation">
+       <tr>
+               <td style='padding: 0;'>
+                       <?php if ($start > 0): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="&lt;&lt; Previous" />       
+                                       <input type="hidden" name="action" value="all" />
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+               <td style='padding: 0; text-align: right;'>     
+                       <?php if ($start + $amount < $count): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="Next &gt; &gt;" />
+                                       <input type="hidden" name="action" value="all" />
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+       </tr>
+</table>
+<?php endif; ?>
+
+<table>
+       <thead>
+               <tr>
+                       <th>Date</th>
+                       <th>Story</th>
+                       <th>Title, Blog and Excerpt</th>
+                       <th colspan="2">Actions</th>
+               </tr>
+       </thead>
+       <tbody>
+               <?php while (list(,$item) = each ($items)): ?>
+               <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>
+                       <td>
+                               <?php echo str_replace(' ', '&nbsp;', date("Y-m-d @ H:i",$item['timestamp']));?>
+                       </td>
+                       <td>
+                               <a href="<?php echo $item['story_url']; ?>"><?php echo $item['story'];?></a>
+                       </td>
+                       <td>
+                               <a href="<?php echo $item['url'];?>"><img alt="Visit" border="0" src="<?php echo $plugindirurl?>silk/house_go.png" /></a>
+                               <strong><?php echo $item['title'];?></strong> 
+                               <em>(<?php echo $item['blog_name'];?>)</em><br />
+                               <?php echo $item['excerpt'];?>
+                       </td>
+                       <td>
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=block&tb='.$item['id'].'&next=all&start='.$start),ENT_QUOTES);?>"><img alt="Block" border="0" src="<?php echo $plugindirurl?>silk/delete.png" /></a>
+                       </td>
+                       <td>
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=delete&tb='.$item['id'].'&next=all&start='.$start),ENT_QUOTES);?>"><img alt="Delete" border="0" src="<?php echo $plugindirurl?>silk/cross.png" /></a>
+                       </td>
+               </tr>
+               <?php endwhile; ?>
+       </tbody>
+</table>
+
+<?php if ($count > $amount): ?>
+<table class="navigation">
+       <tr>
+               <td style='padding: 0;'>
+                       <?php if ($start > 0): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="&lt;&lt; Previous" />       
+                                       <input type="hidden" name="action" value="all" />
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+               <td style='padding: 0; text-align: right;'>     
+                       <?php if ($start + $amount < $count): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="Next &gt; &gt;" />
+                                       <input type="hidden" name="action" value="all" />
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+       </tr>
+</table>
+<?php endif; ?>
+<?php endif; ?>
\ No newline at end of file
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/templates/all_ajax.html b/NP_TrackBack/branches/DOM-branch/trackback/templates/all_ajax.html
new file mode 100644 (file)
index 0000000..cb19843
--- /dev/null
@@ -0,0 +1,128 @@
+<?php global $manager; ?>
+<h2>
+       All trackbacks
+</h2>
+
+<div id="message" style="color: red;"></div>
+
+<div style="width: 95%">
+<span id="tb_grid_bookmark"></span>
+
+<table id="tb_grid" style="border:0; margin:0;">
+       <colgroup>
+               <col style="width:25px;" />
+               <col style="width:40px;" />
+               <col style="width:70px;" />
+               <col style="width:150px;" />
+               <col style="width:200px;"/>
+               <col style="width:25px;" />
+       </colgroup>
+       <thead>
+               <tr>
+                       <th>&#160;</th>
+                       <th>id</th>
+                       <th>Date</th>
+                       <th>Story</th>
+                       <th>Title, Blog and Excerpt</th>
+                       <th>&#160;</th>
+               </tr>
+       </thead>
+</table>
+With selected: 
+<a href="#" onclick="javascript: doDelete()"><img alt="Delete" border="0" src="<?php echo $plugindirurl?>silk/cross.png" /></a>
+<a href="#" onclick="javascript: doBlock()"><img alt="Block" border="0" src="<?php echo $plugindirurl?>silk/delete.png" /></a>
+</div>
+
+<!--
+<textarea id='tb_grid_debugmsgs' rows='5' cols='80' style='font-size:smaller;'></textarea>
+-->
+
+<script type="text/javascript">
+//<![CDATA[
+       Rico.loadModule('LiveGridAjax');
+       Rico.loadModule('LiveGridMenu');
+       Rico.include('translations/livegrid_ja.js');
+       Rico.include('ricoAjaxEngine.js');
+       
+       Rico.onLoad( function() {
+               var params = [
+                       'action=ajax',
+                       'type=all',
+                       'ticket=<?php echo $ticket ;?>'
+               ]; 
+               
+               var cb = new Rico.TableColumn.checkbox('1','0');
+               var colspec = [
+                       {canHide:false, type:'control', control:cb, ClassName:'aligncenter'},
+                       {type:'raw'},
+                       {type:'raw'},
+                       ,
+                       ,
+                       ,
+               ];
+               
+               var opts = {
+                       saveColumnInfo   : {width:true, filter:false, sort:false}, 
+                       menuEvent       : 'none',
+                       frozenColumns   : 2,
+                       canSortDefault  : true,
+                       canHideDefault  : true,
+                       allowColResize  : true,
+                       canFilterDefault: false,
+                       highlightElem   : 'none',
+                       columnSpecs     : colspec
+               };
+               
+               buffer = new Rico.Buffer.AjaxSQL('<?php echo $CONF['PluginURL'].'trackback/';?>grid.php',
+                               {TimeOut:10, requestParameters:params, sortParmFmt: 'displayName'}
+               );
+               orderGrid=new Rico.LiveGrid ('tb_grid', buffer, opts);
+               orderGrid.menu=new Rico.GridMenu({});
+               
+               // ajaxEngine
+               ajaxEngine = new Rico.AjaxEngine;
+               ajaxEngine.registerRequest('updateData', '<?php echo $CONF['PluginURL'].'trackback/';?>grid.php' );
+               ajaxEngine.registerAjaxElement('message');
+       });
+
+       function checkUpdateIds(){
+               var updateIds = [];
+               Rico.writeDebugMsg('check updated rows');
+               for(var i = 0; i < buffer.size; i++){
+                       row = buffer.rows[i];
+                       if( row[0].content && row[0].content == '1' ){
+                               updateIds.push(row[1].content);
+                               Rico.writeDebugMsg('id: '+row[1].content+' updated');
+                       }
+               }
+               return updateIds;
+       }
+       
+       function doBlock(){
+               var ids = checkUpdateIds();
+               if( !(ids.length && ids.length > 0) ) return ;
+               var params = [
+                       'action=doblock',
+                       'ticket=<?php echo $ticket ;?>',
+                       'ids='+ids.join(',')
+               ]; 
+               ajaxEngine.sendRequest('updateData', {parameters: ajaxEngine._createQueryString(params, 0)});
+               orderGrid.resetContents('tb_grid');
+               buffer.fetch(-1);
+       }
+       
+       function doDelete(){
+               var ids = checkUpdateIds();
+               if( !(ids.length && ids.length > 0) ) return ;
+               
+               var params = [
+                       'action=dodelete',
+                       'ticket=<?php echo $ticket ;?>',
+                       'ids='+ids.join(',')
+               ];
+               ajaxEngine.sendRequest('updateData', {parameters: ajaxEngine._createQueryString(params, 0)});
+               orderGrid.resetContents('tb_grid');
+               buffer.fetch(-1);
+       }
+//]]>
+</script>
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/templates/blocked.html b/NP_TrackBack/branches/DOM-branch/trackback/templates/blocked.html
new file mode 100644 (file)
index 0000000..15b98de
--- /dev/null
@@ -0,0 +1,119 @@
+<?php global $manager; ?>
+<h2>
+       Blocked trackbacks
+       <?php if ($count > $amount): ?>
+               (Page <?php echo ceil($start / $amount) + 1;?> of <?php echo ceil($count / $amount);?>)
+       <?php endif; ?>
+</h2>
+
+<ul>
+       <li><a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=blocked_clear&next=blocked'),ENT_QUOTES); ?>">ブロックされたトラックバックのクリア</a></li>
+       <li><a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=blocked_spamclear&next=blocked'),ENT_QUOTES); ?>">spam判定されたトラックバックのクリア</a></li> 
+</ul>
+
+<?php if(count($items)): ?>
+<?php if ($count > $amount): ?>
+<table class="navigation">
+       <tr>
+               <td style='padding: 0;'>
+                       <?php if ($start > 0): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="&lt;&lt; Previous" />       
+                                       <input type="hidden" name="action" value="blocked" />
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+               <td style='padding: 0; text-align: right;'>     
+                       <?php if ($start + $amount < $count): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="Next &gt; &gt;" />
+                                       <input type="hidden" name="action" value="blocked" />
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+       </tr>
+</table>
+<?php endif; ?>
+
+<table>
+       <thead>
+               <tr>
+                       <th>Date</th>
+                       <th>Story</th>
+                       <th>Title, Blog and Excerpt</th>
+                       <th colspan="2">Actions</th>
+               </tr>
+       </thead>
+       <tbody>
+               <?php while (list(,$item) = each ($items)): ?>
+               <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>
+                       <td>
+                               <?php echo str_replace(' ', '&nbsp;', date("Y-m-d @ H:i",$item['timestamp']));?>
+                       </td>
+                       <td>
+                               <a href="<?php echo $item['story_url']; ?>"><?php echo $item['story'];?></a>
+                       </td>
+                       <td>
+                               <a href="<?php echo $item['url'];?>"><?php echo $item['url'];?>"><img alt="Visit" border="0" src="<?php echo $plugindirurl;?>silk/house_go.png" /></a>
+                               <strong><?php echo $item['title'];?></strong> 
+                               <em>(<?php echo $item['blog_name'];?>)</em>
+                               <?php echo $item['spam'] ? 
+                                       '<img alt="spam" border="0" src="' . $plugindirurl . 'silk/delete.png" />' : 
+                                       '';?>
+                               <?php echo $item['link'] ? 
+                                       '' : 
+                                       '<img alt="NOT Linked" border="0" src="' . $plugindirurl . 'silk/link_break.png" />';?>
+                               <br />
+                               <?php echo $item['excerpt'];?>
+                       </td>
+                       <td>
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=unblock&tb='.$item['id'].'&next=blocked&start='.$start),ENT_QUOTES);?>"><img alt="Unblock" border="0" src="<?php echo $plugindirurl;?>silk/accept.png" /></a>
+                       </td>
+                       <td>
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=delete&tb='.$item['id'].'&next=blocked&start='.$start),ENT_QUOTES);?>"><img alt="Delete" border="0" src="<?php echo $plugindirurl;?>silk/cross.png" /></a>
+                       </td>
+               </tr>
+               <?php endwhile; ?>
+       </tbody>
+</table>
+
+<?php if ($count > $amount): ?>
+<table class="navigation">
+       <tr>
+               <td style='padding: 0;'>
+                       <?php if ($start > 0): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="&lt;&lt; Previous" />       
+                                       <input type="hidden" name="action" value="blocked" />
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+               <td style='padding: 0; text-align: right;'>     
+                       <?php if ($start + $amount < $count): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="Next &gt; &gt;" />
+                                       <input type="hidden" name="action" value="blocked" />
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+       </tr>
+</table>
+<?php endif; ?>
+<?php endif; ?>
+
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/templates/blocked_ajax.html b/NP_TrackBack/branches/DOM-branch/trackback/templates/blocked_ajax.html
new file mode 100644 (file)
index 0000000..394a603
--- /dev/null
@@ -0,0 +1,133 @@
+<?php global $manager; ?>
+<h2>
+       Blocked Trackbacks
+</h2>
+
+<ul>
+       <li><a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=blocked_clear&next=blocked'),ENT_QUOTES); ?>">ブロックされたトラックバックのクリア</a></li>
+       <li><a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=blocked_spamclear&next=blocked'),ENT_QUOTES); ?>">spam判定されたトラックバックのクリア</a></li> 
+</ul>
+
+<div id="message" style="color: red;"></div>
+
+<div style="width: 95%">
+<span id="tb_grid_bookmark"></span>
+
+<table id="tb_grid" style="border:0; margin:0;">
+       <colgroup>
+               <col style="width:25px;" />
+               <col style="width:40px;" />
+               <col style="width:70px;" />
+               <col style="width:150px;" />
+               <col style="width:200px;"/>
+               <col style="width:25px;" />
+       </colgroup>
+       <thead>
+               <tr>
+                       <th>&#160;</th>
+                       <th>id</th>
+                       <th>Date</th>
+                       <th>Story</th>
+                       <th>Title, Blog and Excerpt</th>
+                       <th>&#160;</th>
+               </tr>
+       </thead>
+</table>
+With selected: 
+<a href="#" onclick="javascript: doUnblock()"><img alt="Unblock" border="0" src="<?php echo $plugindirurl;?>silk/accept.png" /></a>
+<a href="#" onclick="javascript: doDelete()"><img alt="Delete" border="0" src="<?php echo $plugindirurl?>silk/cross.png" /></a>
+</div>
+
+<!--
+<textarea id='tb_grid_debugmsgs' rows='5' cols='80' style='font-size:smaller;'></textarea>
+-->
+
+<script type="text/javascript">
+//<![CDATA[
+       Rico.loadModule('LiveGridAjax');
+       Rico.loadModule('LiveGridMenu');
+       Rico.include('translations/livegrid_ja.js');
+       Rico.include('ricoAjaxEngine.js');
+       
+       Rico.onLoad( function() {
+               var params = [
+                       'action=ajax',
+                       'type=blocked',
+                       'ticket=<?php echo $ticket ;?>'
+               ]; 
+               
+               var cb = new Rico.TableColumn.checkbox('1','0');
+               var colspec = [
+                       {canHide:false, type:'control', control:cb, ClassName:'aligncenter'},
+                       {type:'raw'},
+                       {type:'raw'},
+                       ,
+                       ,
+                       ,
+               ];
+               
+               var opts = {
+                       saveColumnInfo   : {width:true, filter:false, sort:false}, 
+                       menuEvent       : 'none',
+                       frozenColumns   : 2,
+                       canSortDefault  : true,
+                       canHideDefault  : true,
+                       allowColResize  : true,
+                       canFilterDefault: false,
+                       highlightElem   : 'none',
+                       columnSpecs     : colspec
+               };
+               
+               buffer = new Rico.Buffer.AjaxSQL('<?php echo $CONF['PluginURL'].'trackback/';?>grid.php',
+                               {TimeOut:10, requestParameters:params, sortParmFmt: 'displayName'}
+               );
+               orderGrid=new Rico.LiveGrid ('tb_grid', buffer, opts);
+               orderGrid.menu=new Rico.GridMenu({});
+               
+               // ajaxEngine
+               ajaxEngine = new Rico.AjaxEngine;
+               ajaxEngine.registerRequest('updateData', '<?php echo $CONF['PluginURL'].'trackback/';?>grid.php' );
+               ajaxEngine.registerAjaxElement('message');
+       });
+
+       function checkUpdateIds(){
+               var updateIds = [];
+               Rico.writeDebugMsg('check updated rows');
+               for(var i = 0; i < buffer.size; i++){
+                       row = buffer.rows[i];
+                       if( row[0].content && row[0].content == '1' ){
+                               updateIds.push(row[1].content);
+                               Rico.writeDebugMsg('id: '+row[1].content+' updated');
+                       }
+               }
+               return updateIds;
+       }
+       
+       function doUnBlock(){
+               var ids = checkUpdateIds();
+               if( !(ids.length && ids.length > 0) ) return ;
+               var params = [
+                       'action=dounblock',
+                       'ticket=<?php echo $ticket ;?>',
+                       'ids='+ids.join(',')
+               ]; 
+               ajaxEngine.sendRequest('updateData', {parameters: ajaxEngine._createQueryString(params, 0)});
+               orderGrid.resetContents('tb_grid');
+               buffer.fetch(-1);
+       }
+       
+       function doDelete(){
+               var ids = checkUpdateIds();
+               if( !(ids.length && ids.length > 0) ) return ;
+               
+               var params = [
+                       'action=dodelete',
+                       'ticket=<?php echo $ticket ;?>',
+                       'ids='+ids.join(',')
+               ];
+               ajaxEngine.sendRequest('updateData', {parameters: ajaxEngine._createQueryString(params, 0)});
+               orderGrid.resetContents('tb_grid');
+               buffer.fetch(-1);
+       }
+//]]>
+</script>
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/templates/form.html b/NP_TrackBack/branches/DOM-branch/trackback/templates/form.html
new file mode 100644 (file)
index 0000000..ba61b48
--- /dev/null
@@ -0,0 +1,56 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+       <head>
+               <title>Add TrackBack</title>
+               <link rel="stylesheet" type="text/css" href="<?php echo $CONF['AdminURL']?>styles/bookmarklet.css" />
+       </head>
+       
+       <body>
+               <h1>TrackBack Ping</h1>
+<?php if ($success): ?>
+               <p>
+                       <strong>Your trackback has been received properly.</strong>
+               </p>
+<?php endif; ?>
+<?php if ($error): ?>
+               <p>
+                       <strong><?php echo $status; ?></strong>
+               </p>
+<?php endif; ?>
+<?php if ($form): ?>           
+               <form method="post" action="<?php echo $CONF['ActionURL'] ?>">
+               
+               <div>
+                       <input type="hidden" name="tb_id" value="<?php echo $itemid;?>" />
+                       <input type="hidden" name="action" value="plugin" />
+                       <input type="hidden" name="name" value="TrackBack" />
+                       <input type="hidden" name="type" value="ping" />
+                       
+                       <table>
+                               <tr>
+                                       <td>Article URL</td>
+                                       <td><input type="text" value="" name="url" size="60" /></td>
+                               </tr>
+                               <tr>
+                                       <td>Article Title</td>
+                                       <td><input type="text" value="" name="title" size="60" /></td>
+                               </tr>
+                               <tr>
+                                       <td>Excerpt from article</td>
+                                       <td><textarea name="excerpt" cols="40" rows="5"></textarea></td>
+                               </tr>
+                               <tr>
+                                       <td>Your Blog Name</td>
+                                       <td><input type="text" value="" name="blog_name" size="60" /></td>
+                               </tr>
+                               <tr>
+                                       <td>Add TrackBack</td>
+                                       <td><input type="submit" value="Add TrackBack" /></td>
+                               </tr>
+                       </table>
+               </div>
+               
+               </form>
+<?php endif; ?>
+       </body>
+</html>
\ No newline at end of file
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/templates/index.html b/NP_TrackBack/branches/DOM-branch/trackback/templates/index.html
new file mode 100644 (file)
index 0000000..15a2ce4
--- /dev/null
@@ -0,0 +1,34 @@
+<?php global $manager; ?>
+<h2>Overview of all items</h2>
+
+<?php if(count($blogs)): ?>
+
+<table>
+<?php while (list(,$blog) = each ($blogs)): ?>
+<?php if(count($blog['items'])): ?>
+       <thead>
+               <tr>
+                       <th>Blog: <?php echo htmlspecialchars($blog['bname']);?></th>
+                       <th>Total</th>
+                       <th>Action</th>
+               </tr>
+       </thead>
+       <tbody>
+               <?php while (list(,$item) = each ($blog['items'])): ?>
+               <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>
+                       <td>
+                               <?php echo $item['ititle'];?>
+                       </td>
+                       <td>
+                               <?php echo htmlspecialchars($item['total']);?>
+                       </td>
+                       <td>
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=list&id='.$item['inumber']),ENT_QUOTES);?>">Trackbacks</a>
+                       </td>
+               </tr>
+               <?php endwhile; ?>
+       </tbody>
+<?php endif; ?>
+<?php endwhile; ?>
+</table>
+<?php endif; ?>
\ No newline at end of file
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/templates/list.html b/NP_TrackBack/branches/DOM-branch/trackback/templates/list.html
new file mode 100644 (file)
index 0000000..0fcf3c3
--- /dev/null
@@ -0,0 +1,107 @@
+<?php global $manager; ?>
+<h2>
+       All trackbacks for &quot;<?php echo $story['title'];?>&quot;
+       <?php if ($count > $amount): ?>
+               (Page <?php echo ceil($start / $amount) + 1;?> of <?php echo ceil($count / $amount);?>)
+       <?php endif; ?>
+</h2>
+
+<?php if(count($items)): ?>
+<?php if ($count > $amount): ?>
+<table class="navigation">
+       <tr>
+               <td style='padding: 0;'>
+                       <?php if ($start > 0): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="&lt;&lt; Previous" />       
+                                       <input type="hidden" name="action" value="list" />
+                                       <input type="hidden" name="id" value="<?php echo $story['id'];?>" />
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+               <td style='padding: 0; text-align: right;'>     
+                       <?php if ($start + $amount < $count): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="Next &gt; &gt;" />
+                                       <input type="hidden" name="action" value="list" />
+                                       <input type="hidden" name="id" value="<?php echo $story['id'];?>" />
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+       </tr>
+</table>
+<?php endif; ?>
+
+<table>
+       <thead>
+               <tr>
+                       <th>Date</th>
+                       <th>Title, Blog and Excerpt</th>
+                       <th colspan="2">Actions</th>
+               </tr>
+       </thead>
+       <tbody>
+               <?php while (list(,$item) = each ($items)): ?>
+               <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>
+                       <td>
+                               <?php echo str_replace(' ', '&nbsp;', date("Y-m-d @ H:i",$item['timestamp']));?>
+                       </td>
+                       <td>
+                               <a href="<?php echo $item['url'];?>"><img alt="Visit" border="0" src="<?php echo $plugindirurl?>silk/house_go.png" /></a>
+                               <strong><?php echo $item['title'];?></strong> 
+                               <em>(<?php echo $item['blog_name'];?>)</em><br />
+                               <?php echo $item['excerpt'];?>
+                       </td>
+                       <td>
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=block&tb='.$item['id'].'&next=list&id='.$story['id'].'&start='.$start),ENT_QUOTES);?>"><img alt="Block" border="0" src="<?php echo $plugindirurl?>silk/delete.png" /></a>
+                       </td>
+                       <td>
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=delete&tb='.$item['id'].'&next=list&id='.$story['id'].'&start='.$start),ENT_QUOTES);?>"><img alt="Delete" border="0" src="<?php echo $plugindirurl?>silk/cross.png" /></a>
+                       </td>
+               </tr>
+               <?php endwhile; ?>
+       </tbody>
+</table>
+
+<?php if ($count > $amount): ?>
+<table class="navigation">
+       <tr>
+               <td style='padding: 0;'>
+                       <?php if ($start > 0): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="&lt;&lt; Previous" />       
+                                       <input type="hidden" name="action" value="list" />
+                                       <input type="hidden" name="id" value="<?php echo $story['id'];?>" />
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+               <td style='padding: 0; text-align: right;'>     
+                       <?php if ($start + $amount < $count): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="Next &gt; &gt;" />
+                                       <input type="hidden" name="action" value="list" />
+                                       <input type="hidden" name="id" value="<?php echo $story['id'];?>" />
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+       </tr>
+</table>
+<?php endif; ?>
+<?php endif; ?>
+
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/templates/menu.html b/NP_TrackBack/branches/DOM-branch/trackback/templates/menu.html
new file mode 100644 (file)
index 0000000..92fa7e1
--- /dev/null
@@ -0,0 +1,33 @@
+<?php global $manager?>
+
+<script type="text/javascript" src="<?php echo $CONF['PluginURL'].'trackback/js/'?>prototype.js"></script>
+<script type="text/javascript" src="<?php echo $CONF['PluginURL'].'trackback/js/'?>rico.js"></script>
+
+<h2>Trackback</h2>
+
+<ul>
+       <li>
+               <img border="0" src="<?php echo $plugindirurl?>silk/application_view_list.png" />
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=index'),ENT_QUOTES);?>">Overview of all items</a>
+       </li>
+       <li>
+               <img border="0" src="<?php echo $plugindirurl?>silk/tick.png" />
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=all'),ENT_QUOTES);?>">All trackbacks</a>
+       </li>
+       <li>
+               <img border="0" src="<?php echo $plugindirurl?>silk/exclamation.png" />
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=blocked'),ENT_QUOTES);?>">Blocked trackbacks</a>
+       </li> 
+       <li>
+               <img border="0" src="<?php echo $plugindirurl?>silk/transmit_go.png" />
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=ping'),ENT_QUOTES);?>">Manually ping another weblog</a>
+       </li>
+       <li>
+               <img border="0" src="<?php echo $plugindirurl?>silk/help.png" />
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=help'),ENT_QUOTES);?>">Help</a>
+       </li>
+    <li>
+               <img border="0" src="<?php echo $plugindirurl?>silk/plugin_edit.png" />
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['AdminURL'].'index.php?action=pluginoptions&plugid='.$plugid),ENT_QUOTES);?>">Plugin Options</a>
+       </li>
+</ul>
\ No newline at end of file
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/templates/ping.html b/NP_TrackBack/branches/DOM-branch/trackback/templates/ping.html
new file mode 100644 (file)
index 0000000..91216ce
--- /dev/null
@@ -0,0 +1,50 @@
+<?php global $manager; ?>
+<h2>Manually ping another weblog</h2>
+
+<form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+
+    <input type="hidden" name="action" value="sendping" />
+    <input type="hidden" name="next" value="ping" />
+    <?php $manager->addTicketHidden(); ?>
+       
+    <table>
+        <tr>
+            <th colspan='2'>Manually Ping</th>
+        </tr>
+        <tr>
+            <td>Your URL</td>
+            <td>
+                <input type="text" name="url" size="60" value="<?php echo htmlspecialchars($item['url']);?>" />
+            </td>
+        </tr>
+        <tr>
+            <td>Your Title</td>
+            <td>
+                <input type="text" name="title" size="60" value="<?php echo htmlspecialchars($item['title']);?>" />
+            </td>
+        </tr>
+        <tr>
+            <td>Your Excerpt</td>
+            <td>
+                    <textarea name="excerpt" cols="40" rows="5"><?php echo $item['excerpt'];?></textarea>
+            </td>
+        </tr>
+        <tr>
+            <td>Your Blog Name</td>
+            <td>
+                <input type="text" name="blog_name" size="60" value="<?php echo htmlspecialchars($item['blogname']);?>" />
+            </td>
+        </tr>
+        <tr>
+            <td>External Ping URL</td>
+            <td>
+                <input type="text" value="" name="ping_url" size="60" />
+            </td>
+        </tr>
+        <tr>
+            <td>Send Ping</td>
+            <td><input type="submit" value="Send Ping" /></td>
+        </tr>
+    </table>
+
+</form>
\ No newline at end of file
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/templates/response_all.xml b/NP_TrackBack/branches/DOM-branch/trackback/templates/response_all.xml
new file mode 100644 (file)
index 0000000..7a160ad
--- /dev/null
@@ -0,0 +1,35 @@
+<?php echo '<'.'?xml version="1.0" encoding="UTF-8"?'.'>';?>
+<?php global $manager; ?>
+
+<ajax-response>
+       <response type="object" id="tb_grid_updater">
+               <rowcount><?php echo $count; ?></rowcount>
+               <rows update_ui="true" offset="<?php echo $start; ?>" >
+                       <?php while (list(,$item) = each ($items)): ?>
+                       <tr>
+                               <td>0</td>
+                               <td><?php echo $item['id'];?></td>
+                               <td>
+                                       <?php echo date("Y-m-d H:i:s",$item['timestamp']);?>
+                               </td>
+                               <td>
+                                       <!--
+                                       <a href="<?php echo $item['story_url']; ?>"><?php echo $item['story'];?></a>
+                                       -->
+                               </td>
+                               <td>
+                                       <!--
+                                       <a href="<?php echo $item['url'];?>">
+                                               <img alt="Visit" border="0" src="<?php echo $plugindirurl?>silk/house_go.png" />
+                                       </a>
+                                       <strong><?php echo $item['title'];?></strong>
+                                       <?php echo $item['excerpt'];?>
+                                       <em>(<?php echo $item['blog_name'];?>)</em>
+                                       -->
+                               </td>
+                               <td></td>
+                       </tr>
+                       <?php endwhile; ?>
+               </rows> 
+       </response>
+</ajax-response>
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/templates/response_blocked.xml b/NP_TrackBack/branches/DOM-branch/trackback/templates/response_blocked.xml
new file mode 100644 (file)
index 0000000..d085390
--- /dev/null
@@ -0,0 +1,41 @@
+<?php echo '<'.'?xml version="1.0" encoding="UTF-8"?'.'>';?>
+<?php global $manager; ?>
+
+<ajax-response>
+       <response type="object" id='tb_grid_updater'>
+               <rowcount><?php echo $count; ?></rowcount>
+               <rows update_ui='true' >
+                       <?php while (list(,$item) = each ($items)): ?>
+                       <tr>
+                               <td>0</td>
+                               <td><?php echo $item['id'];?></td>
+                               <td>
+                                       <?php echo date("Y-m-d H:i:s",$item['timestamp']);?>
+                               </td>
+                               <td>
+                                       <!--
+                                       <a href="<?php echo $item['story_url']; ?>"><?php echo $item['story'];?></a>
+                                       -->
+                               </td>
+                               <td>
+                                       <!--
+                                       <a href="<?php echo $item['url'];?>">
+                                               <img alt="Visit" border="0" src="<?php echo $plugindirurl?>silk/house_go.png" />
+                                       </a>
+                                       <strong><?php echo $item['title'];?></strong>
+                                       <?php echo $item['spam'] ? 
+                                               '<img alt="spam" border="0" src="' . $plugindirurl . 'silk/delete.png" />' : 
+                                               '';?>
+                                       <?php echo $item['link'] ? 
+                                               '' : 
+                                               '<img alt="NOT Linked" border="0" src="' . $plugindirurl . 'silk/link_break.png" />';?>
+                                       <?php echo $item['excerpt'];?>
+                                       <em>(<?php echo $item['blog_name'];?>)</em>
+                                       -->
+                               </td>
+                               <td></td>
+                       </tr>
+                       <?php endwhile; ?>
+               </rows> 
+       </response>
+</ajax-response>
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/templates/response_doblock.xml b/NP_TrackBack/branches/DOM-branch/trackback/templates/response_doblock.xml
new file mode 100644 (file)
index 0000000..a53f37c
--- /dev/null
@@ -0,0 +1,8 @@
+<?echo '<'.'?xml version="1.0" encoding="UTF-8"?'.'>';?>
+<?php global $manager; ?>
+
+<ajax-response>
+   <response type="element" id="message">
+               <?php echo $message; ?>
+   </response>
+</ajax-response>
\ No newline at end of file
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/templates/response_dodelete.xml b/NP_TrackBack/branches/DOM-branch/trackback/templates/response_dodelete.xml
new file mode 100644 (file)
index 0000000..a53f37c
--- /dev/null
@@ -0,0 +1,8 @@
+<?echo '<'.'?xml version="1.0" encoding="UTF-8"?'.'>';?>
+<?php global $manager; ?>
+
+<ajax-response>
+   <response type="element" id="message">
+               <?php echo $message; ?>
+   </response>
+</ajax-response>
\ No newline at end of file
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/templates/response_dounblock.xml b/NP_TrackBack/branches/DOM-branch/trackback/templates/response_dounblock.xml
new file mode 100644 (file)
index 0000000..a53f37c
--- /dev/null
@@ -0,0 +1,8 @@
+<?echo '<'.'?xml version="1.0" encoding="UTF-8"?'.'>';?>
+<?php global $manager; ?>
+
+<ajax-response>
+   <response type="element" id="message">
+               <?php echo $message; ?>
+   </response>
+</ajax-response>
\ No newline at end of file
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/templates/updatetable.html b/NP_TrackBack/branches/DOM-branch/trackback/templates/updatetable.html
new file mode 100644 (file)
index 0000000..113e9e6
--- /dev/null
@@ -0,0 +1,9 @@
+<?php global $manager; ?>
+<blockquote style="color: red;border:1px solid red;padding:1em;"><b>Table update:</b><br />
+                       <form method="post"><div>
+                               <input type="hidden" name="action" value="tableUpgrade" />
+                               <input type="submit" tabindex="10" value="upgrade table" />
+                               <?php $manager->addTicketHidden(); ?>
+                       </div></form>
+</blockquote>
+       
diff --git a/NP_TrackBack/branches/DOM-branch/trackback/templates/updatetablefinished.html b/NP_TrackBack/branches/DOM-branch/trackback/templates/updatetablefinished.html
new file mode 100644 (file)
index 0000000..d94969e
--- /dev/null
@@ -0,0 +1,5 @@
+<?php global $manager; ?>
+<blockquote style="color: red;border:1px solid red;padding:1em;">
+Table update done !
+</blockquote>
+       
diff --git a/NP_TrackBack/trunk/NP_TrackBack.php b/NP_TrackBack/trunk/NP_TrackBack.php
new file mode 100644 (file)
index 0000000..83ccb06
--- /dev/null
@@ -0,0 +1,2586 @@
+<?php\r
+// vim: tabstop=2:shiftwidth=2\r
+\r
+   /* ==========================================================================================\r
+       * Trackback 2.0 for Nucleus CMS \r
+       * ==========================================================================================\r
+       * This program is free software and open source software; you can redistribute\r
+       * it and/or modify it under the terms of the GNU General Public License as\r
+       * published by the Free Software Foundation; either version 2 of the License,\r
+       * or (at your option) any later version.\r
+       *\r
+       * This program is distributed in the hope that it will be useful, but WITHOUT\r
+       * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\r
+       * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for\r
+       * more details.\r
+       *\r
+       * You should have received a copy of the GNU General Public License along\r
+       * with this program; if not, write to the Free Software Foundation, Inc.,\r
+       * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  or visit\r
+       * http://www.gnu.org/licenses/gpl.html\r
+       * ==========================================================================================\r
+       */\r
+\r
+define('NP_TRACKBACK_LINKCHECK_STRICT', 1);\r
+define('NP_TRACKBACK_USE_XML_PARSER', 1);\r
+define('NP_TRACKBACK_ENCODING_DETECT_ORDER', 'ASCII,ISO-2022-JP,UTF-8,EUC-JP,SJIS');\r
+\r
+       class NP_TrackBack_XMLParser {\r
+               function NP_TrackBack_XMLParser(){\r
+                       $this->isError = false;\r
+                       $this->inTarget = false;\r
+               }\r
+       \r
+               function parse($data){\r
+                       $rx = '/(<'.'?xml.*encoding=[\'"])(.*?)([\'"].*?'.'>)/m';\r
+                       if (preg_match($rx, $data, $m)) {\r
+                               $encoding = strtoupper($m[2]);\r
+                       } else {\r
+                               $encoding = mb_detect_encoding($data, NP_TRACKBACK_ENCODING_DETECT_ORDER);\r
+                       }\r
+                       \r
+                       if($encoding == "UTF-8" || $encoding == "ISO-8859-1") {\r
+                               // noting\r
+                       } else {\r
+                               $data = @mb_convert_encoding($data, "UTF-8", $encoding);\r
+                               $data = str_replace ( $m[0], $m[1].'UTF-8'.$m[3], $data);\r
+                               $encoding = 'UTF-8';\r
+                       }\r
+                       \r
+                       $this->parser = xml_parser_create($encoding);\r
+                       xml_set_object($this->parser, $this);\r
+                       xml_set_element_handler($this->parser, "_open", "_close");\r
+                       xml_set_character_data_handler($this->parser, "_cdata");\r
+                       xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, 'UTF-8').\r
+                       \r
+                       $this->words = array();\r
+                       xml_parse($this->parser, $data);\r
+                       $errcode = xml_get_error_code($this->parser);\r
+                   if ( $errcode != XML_ERROR_NONE ) {\r
+                       $this->isError = true;\r
+                               $this->message = 'XML Parse Error: ' . xml_error_string($errcode) . ' in '. xml_get_current_line_number($this->parser);\r
+                   }\r
+                       return $this->message;\r
+               }\r
+       \r
+               function free(){\r
+                       xml_parser_free($this->parser);\r
+               }\r
+       \r
+               function _open($parser, $name, $attribute){\r
+                       switch( $name ){\r
+                               case 'MESSAGE':\r
+                                       $this->inTarget = 'MESSAGE';\r
+                                       break;\r
+                               case 'ERROR':\r
+                                       $this->inTarget = 'ERROR';\r
+                                       break;\r
+                       }\r
+               }\r
+       \r
+               function _close($parser, $name){\r
+                       if( $name == $this->inTarget ) $this->inTarget = null;\r
+               }\r
+       \r
+               function _cdata($parser, $data){\r
+                       switch( $this->inTarget ){\r
+                               case 'MESSAGE':\r
+                                       $this->message = trim($data);\r
+                                       break;\r
+                               case 'ERROR':\r
+                                       $this->isError = ($data ? true : false);\r
+                                       break;\r
+                       }\r
+               }\r
+       }   \r
+   \r
+       class NP_TrackBack extends NucleusPlugin {\r
+               var $useCurl = 1; // use curl? 2:precheck+read by curl, 1: read by curl 0: fread\r
+\r
+//modify start+++++++++\r
+               function _createItemLink($itemid, $b){\r
+                       global $CONF, $manager;\r
+                       \r
+                       $itemLink = createItemLink($itemid,'');\r
+                       if( strpos($itemLink, 'http') === 0 ){\r
+                               return $itemLink;\r
+                       }\r
+                                               \r
+                       $blogurl = $b->getURL();\r
+                       if (!$blogurl) {\r
+                               $b =& $manager->getBlog($CONF['DefaultBlog']);\r
+                               $blogurl = $b->getURL();\r
+                               if (!$blogurl) {\r
+                                       $blogurl = $CONF['IndexURL'];\r
+                               }\r
+                       }\r
+                       \r
+                       if(substr($blogurl, -1) == '/')  $blogurl = substr($blogurl, 0, -1);\r
+                       $usePathInfo = ($CONF['URLMode'] == 'pathinfo');\r
+                       $itemUrlOrg = $CONF['ItemURL'];\r
+                       if( ! ($usePathInfo || substr($blogurl, -4) == '.php') ) $blogurl .= '/index.php';\r
+                       $CONF['ItemURL'] = $blogurl;\r
+                       \r
+                       $itemLink = createItemLink($itemid,'');\r
+                       $CONF['ItemURL'] = $itemUrlOrg;\r
+                       \r
+                       return $itemLink;\r
+               }\r
+//modify end+++++++++\r
+\r
+       /**************************************************************************************\r
+        * SKIN VARS, TEMPLATE VARS AND ACTIONS\r
+                */\r
+\r
+               /*\r
+                * TrackBack data can be inserted using skinvars (or templatevars)\r
+                */\r
+               function doSkinVar($skinType, $what = '', $tb_id = '', $amount = 'limit-1') {\r
+\r
+                       global $itemid, $manager, $CONF;\r
+\r
+//modify start+++++++++\r
+                       if(preg_match('/limit/i', $tb_id)){\r
+                               $amount = $tb_id;\r
+                               $tb_id = '';\r
+                       }\r
+                       $amount = intval(str_replace('limit', '', $amount));\r
+//modify end+++++++++\r
+\r
+                       if ($tb_id == '') $tb_id = intval($itemid);\r
+       \r
+//mod by cles\r
+                       $isAcceptPing = $this->isAcceptTrackBack($tb_id);\r
+\r
+                       //if( $skinType == 'template' && (! $isAcceptPing ) ){\r
+                       //      return;\r
+                       //}\r
+//mod by cles end\r
+                       switch ($what) {\r
+                       \r
+                               // Insert Auto-discovery RDF code\r
+                               case 'tbcode':\r
+                               case 'code':\r
+//mod by cles\r
+//                                     if($skinType == 'item')\r
+\r
+                                       $spamcheck = array (\r
+                                               'type'          => 'tbcode',\r
+                                               'id'            => -1,\r
+                                               'title'         => '',\r
+                                               'excerpt'       => '',\r
+                                               'blogname'      => '',\r
+                                               'url'           => '',\r
+                                               'return'        => true,\r
+                                               'live'          => true,\r
+                                               \r
+                                               /* Backwards compatibility with SpamCheck API 1*/\r
+                                               'data'          => '',\r
+                                               'ipblock'   => true,\r
+                                       );\r
+                                       global $manager;\r
+                                       //$manager->notify('SpamCheck', array ('spamcheck' => & $spamcheck));\r
+                                       $spam = false;\r
+                                       if (isset($spamcheck['result']) && $spamcheck['result'] == true){\r
+                                               $spam = true;\r
+                                       }\r
+\r
+                                       if( ($skinType == 'item') && (!$spam) && $isAcceptPing  )\r
+//mod by cles end\r
+                                               $this->insertCode($tb_id);\r
+                                       break;\r
+                                       \r
+                               // Insert TrackBack URL\r
+                               case 'tburl':\r
+                               case 'url':\r
+//mod by cles\r
+//                                     echo $this->getTrackBackUrl($tb_id);\r
+                                       if($isAcceptPing)\r
+                                               echo $this->getTrackBackUrl($tb_id);\r
+                                       else\r
+                                               echo 'Sorry, no trackback pings are accepted.';\r
+//mod by cles end\r
+                                       break;\r
+                               \r
+                               // Insert manual ping URL\r
+                               case 'form':\r
+                               case 'manualpingformlink':\r
+                                       echo $this->getManualPingUrl($tb_id);\r
+                                       break;\r
+                               \r
+                               case 'sendpinglink':\r
+                                       echo $manager->addTicketToUrl($CONF['PluginURL'] . 'trackback/index.php?action=ping&amp;id=' . intval($tb_id));\r
+                                       break;\r
+       \r
+                               // Insert TrackBack count\r
+                               case 'count':\r
+                                       $count = $this->getTrackBackCount($tb_id);\r
+                                       switch ($count) {\r
+                                               case 0:         echo TEMPLATE::fill($this->getOption('tplTbNone'), array('number' => $count)); break;\r
+                                               case 1:         echo TEMPLATE::fill($this->getOption('tplTbOne'),  array('number' => $count)); break;\r
+                                               default:        echo TEMPLATE::fill($this->getOption('tplTbMore'), array('number' => $count)); break;\r
+                                       }\r
+                                       break;\r
+\r
+                               // Shows the TrackBack list\r
+                               case 'list':\r
+                               case '':\r
+//modify start+++++++++\r
+//                                     $this->showList($tb_id);\r
+                                       $this->showList($tb_id, $amount);\r
+//modify end+++++++++\r
+                                       break;\r
+//mod by cles\r
+                               // show requred URL\r
+                               case 'required':\r
+                                       echo  $this->getRequiredURL($tb_id);\r
+                                       break;\r
+                                       \r
+                               // shows the Local list\r
+                               case 'locallist':\r
+                                       $this->showLocalList($tb_id);\r
+                                       break;                                  \r
+//mod by cles end\r
+                                       \r
+                               default:\r
+                                       return;\r
+                       }\r
+               }\r
+       \r
+               /*\r
+                * When used in templates, the tb_id will be determined by the itemid there\r
+                */\r
+               function doTemplateVar(&$item, $what = '') {\r
+                       $this->doSkinVar('template', $what, $item->itemid);\r
+               }\r
+               \r
+               function doTemplateCommentsVar(&$item, &$comment, $what = ''){\r
+                       $this->doSkinVar('templatecomments', $what, $item->itemid);\r
+               }\r
+               \r
+               /*\r
+               * A trackback ping is to be received on the URL\r
+               * http://yourdomain.com/item/1234.trackback\r
+               * Extra variables to be passed along are url, title, excerpt, blog_name\r
+               */\r
+               function event_InitSkinParse(&$data) {\r
+                       global $CONF, $itemid;\r
+                       $format = requestVar('format');\r
+                       \r
+                       if ($CONF['URLMode'] == 'pathinfo') {\r
+                               if (preg_match('/(\/|\.)(trackback)(\/|$)/', serverVar('PATH_INFO'), $matches)) {\r
+                                       $format = $matches[2];\r
+                               }\r
+                       }\r
+                       \r
+                       if ($format == 'trackback' && $data['type'] == 'item')\r
+                       {\r
+                               $errorMsg = $this->handlePing(intval($itemid));\r
+                               \r
+                               if ($errorMsg != '')\r
+                               $this->xmlResponse($errorMsg);\r
+                               else\r
+                               $this->xmlResponse();\r
+                               \r
+                               exit;\r
+                       }\r
+               }\r
+\r
+               /*\r
+                * A trackback ping is to be received on the URL\r
+                * http://yourdomain.com/action.php?action=plugin&name=TrackBack&tb_id=1234\r
+                * Extra variables to be passed along are url, title, excerpt, blog_name\r
+                */\r
+               function doAction($type)\r
+               {\r
+                       global $CONF,$manager;\r
+                       $aActionsNotToCheck = array(\r
+                               '',\r
+                               'ping',\r
+                               'form',\r
+                               'redirect',\r
+                               'left',\r
+                       );\r
+                       if (!in_array($type, $aActionsNotToCheck)) {\r
+                               if (!$manager->checkTicket()) return _ERROR_BADTICKET;\r
+                       }\r
+                       \r
+                       switch ($type) {\r
+       \r
+                               // When no action type is given, assume it's a ping\r
+                               case '':\r
+                                       $errorMsg = $this->handlePing();\r
+                                       $this->xmlResponse($errorMsg);\r
+                                       break; \r
+                                       \r
+                               // Manual ping\r
+                               case 'ping':\r
+                                       $errorMsg = $this->handlePing();\r
+                                       if ($errorMsg != '')\r
+                                               $this->showManualPingError(intRequestVar('tb_id'), $errorMsg);\r
+                                       else\r
+                                               $this->showManualPingSuccess(intRequestVar('tb_id'));\r
+                                       break; \r
+       \r
+                               // Show manual ping form\r
+                               case 'form':\r
+//mod by cles\r
+//                                     $this->showManualPingForm(intRequestVar('tb_id'));\r
+                                       $tb_id = intRequestVar('tb_id');\r
+                                       $isAcceptPing = $this->isAcceptTrackBack($tb_id);\r
+                                       \r
+                                       if( $isAcceptPing )     \r
+                                               $this->showManualPingForm($tb_id);\r
+                                       else\r
+                                               echo 'Sorry, no trackback pings are accepted.';\r
+//mod by cles end\r
+                                       break;\r
+       \r
+                               // Detect trackback\r
+                               case 'detect':\r
+                                       list($url, $title) = \r
+                                               $this->getURIfromLink(html_entity_decode(requestVar('tb_link')));\r
+\r
+                                       $url = addslashes($url);\r
+                                       $url = $this->_utf8_to_javascript($url);\r
+\r
+                                       $title = addslashes($title);\r
+                                       $title = $this->_utf8_to_javascript($title);\r
+                               \r
+                                       echo "tbDone('" . requestVar('tb_link') . "', '" . $url . "', '" . $title . "');";\r
+\r
+                                       break;\r
+//mod by cles\r
+                               // redirect \r
+                               case 'redirect':\r
+                                       return $this->redirect(intRequestVar('tb_id'), requestVar('urlHash'));\r
+                                       break;\r
+//mod by cles end\r
+                               case 'left':\r
+                                       echo $this->showLeftList(intRequestVar('tb_id'), intRequestVar('amount'));\r
+                                       break;\r
+                               \r
+                               // delete a trackback(local)\r
+                               case 'deletelc':\r
+                                       $err = $this->deleteLocal(intRequestVar('tb_id'), intRequestVar('from_id'));\r
+                                       if( $err )\r
+                                               return $err;\r
+                                       header('Location: ' . serverVar('HTTP_REFERER'));\r
+                                       break;\r
+                       } \r
+\r
+                       exit;\r
+               }\r
+               \r
+               function doIf($key = '', $value = '')\r
+               {\r
+                       global $itemid;\r
+                       //echo "key: $key, value: $value";\r
+                       \r
+                       switch( strtolower($key) ){\r
+                               case '':\r
+                               case 'accept':\r
+                                       if( $value == '' ) $value = 'yes';\r
+                                       $value = ( $value == 'no' || (! $value) ) ? false : true;\r
+                               \r
+                                       $ret = false;\r
+                                       if( $itemid )\r
+                                               $ret = $this->isAcceptTrackBack($itemid);\r
+                                       else\r
+                                               $ret = $this->isAcceptTrackBack();\r
+                                       return ( $value == false ) ? (! $ret) : $ret;\r
+                                       \r
+                               case 'required':\r
+                                       if( $value == '' ) $value = 'yes';\r
+                                       $value = ( $value == 'no' || (! $value) ) ? false : true;\r
+                                       \r
+                                       $ret = false;\r
+                                       if( $itemid )\r
+                                               $ret = $this->isEnableLinkCheck($itemid);\r
+                                       \r
+                                       return ( $value == false ) ? (! $ret) : $ret;\r
+                                       \r
+                               default:\r
+                                       return false;\r
+                       }\r
+               }\r
+\r
+       /**************************************************************************************\r
+        * OUTPUT\r
+                */\r
+\r
+               /*\r
+                * Show a list of left trackbacks for this ID\r
+                */\r
+               function showLeftList($tb_id, $offset = 0, $amount = 99999999) {\r
+                       global $manager, $blog, $CONF;\r
+\r
+                       $out = array();\r
+                       $query = '\r
+                               SELECT \r
+                                       url, \r
+                                       md5(url) as urlHash,\r
+                                       blog_name, \r
+                                       excerpt, \r
+                                       title, \r
+                                       UNIX_TIMESTAMP(timestamp) AS timestamp \r
+                               FROM \r
+                                       '.sql_table('plugin_tb').' \r
+                               WHERE \r
+                                       tb_id = '.intval($tb_id).' AND\r
+                                       block = 0\r
+                               ORDER BY \r
+                                       timestamp DESC\r
+                       ';\r
+                       if($offset)\r
+                               $query .= ' LIMIT '.intval($offset).', ' .intval($amount);\r
+                       $res = sql_query($query);\r
+                       while ($row = mysql_fetch_array($res))\r
+                       {\r
+\r
+                               $row['blog_name']       = htmlspecialchars($row['blog_name'], ENT_QUOTES);\r
+                               $row['title']           = htmlspecialchars($row['title'], ENT_QUOTES);\r
+                               $row['excerpt']         = htmlspecialchars($row['excerpt'], ENT_QUOTES);\r
+                               if (_CHARSET != 'UTF-8') {\r
+//modify start+++++++++\r
+                                       $row['blog_name']       = $this->_restore_to_utf8($row['blog_name']);\r
+                                       $row['title']           = $this->_restore_to_utf8($row['title']);\r
+                                       $row['excerpt']         = $this->_restore_to_utf8($row['excerpt']);\r
+//modify end+++++++++\r
+                                       $row['blog_name']       = $this->_utf8_to_entities($row['blog_name']);\r
+                                       $row['title']           = $this->_utf8_to_entities($row['title']);\r
+                                       $row['excerpt']         = $this->_utf8_to_entities($row['excerpt']);\r
+                               }                               \r
+                               $iVars = array(\r
+                                       'action'        => $this->getTrackBackUrl($tb_id),\r
+                                       'form'          => $this->getManualPingUrl($tb_id),\r
+                                       'name'          => $row['blog_name'],\r
+                                       'title'         => $row['title'],\r
+                                       'excerpt'       => $this->_cut_string($row['excerpt'], 400),\r
+                                       'url'           => htmlspecialchars($row['url'], ENT_QUOTES),\r
+                                       'date'          => htmlspecialchars(strftime($this->getOption('dateFormat'), $row['timestamp']), ENT_QUOTES)\r
+                               );\r
+\r
+//mod by cles\r
+                               if( $this->getOption('HideUrl') == 'yes' )\r
+                                       $iVars['url'] = $CONF['ActionURL'] . '?action=plugin&amp;name=TrackBack&amp;type=redirect&amp;tb_id=' . $tb_id . '&amp;urlHash=' . $row['urlHash'];\r
+                               else\r
+                                       $iVars['url'] = $row['url'];\r
+//mod by cles end\r
+\r
+                               $out[] = TEMPLATE::fill($this->getOption('tplItem'), $iVars);\r
+                       }\r
+                       mysql_free_result($res);\r
+                       \r
+                       return @join("\n",$out);\r
+               }\r
+\r
+               /*\r
+                * Show a list of all trackbacks for this ID\r
+                */\r
+               function showList($tb_id, $amount = 0) {\r
+                       $tb_id = intval($tb_id);\r
+                       global $manager, $blog, $CONF, $member;\r
+//mod by cles\r
+                       $enableHideurl = true;\r
+                       // for TB LinkLookup\r
+                       if( \r
+                                  strpos(serverVar('HTTP_USER_AGENT'),'Hatena Diary Track') === false\r
+                               || strpos(serverVar('HTTP_USER_AGENT'),'NP_TrackBack') === false\r
+                               || strpos(serverVar('HTTP_USER_AGENT'),'TBPingLinkLookup') === false\r
+                               || strpos(serverVar('HTTP_USER_AGENT'),'MT::Plugin::BanNoReferTb') === false\r
+                               || strpos(serverVar('HTTP_USER_AGENT'),'livedoorBlog') === false\r
+                       ){\r
+                               $enableHideurl = false;\r
+                               $amount = '-1';\r
+                       }\r
+//mod by cles end\r
+\r
+/*\r
+                       $res = sql_query('\r
+                               SELECT \r
+                                       url, \r
+                                       md5(url) as urlHash,\r
+                                       blog_name, \r
+                                       excerpt, \r
+                                       title, \r
+                                       UNIX_TIMESTAMP(timestamp) AS timestamp \r
+                               FROM \r
+                                       '.sql_table('plugin_tb').' \r
+                               WHERE \r
+                                       tb_id = '.$tb_id .' AND\r
+                                       block = 0\r
+                               ORDER BY \r
+                                       timestamp ASC\r
+                       ');\r
+*/\r
+                       $query = '\r
+                               SELECT \r
+                                       url, \r
+                                       md5(url) as urlHash,\r
+                                       blog_name, \r
+                                       excerpt, \r
+                                       title, \r
+                                       UNIX_TIMESTAMP(timestamp) AS timestamp \r
+                               FROM \r
+                                       '.sql_table('plugin_tb').' \r
+                               WHERE \r
+                                       tb_id = '.intval($tb_id) .' AND\r
+                                       block = 0\r
+                               ORDER BY \r
+                                       timestamp DESC\r
+                       ';\r
+                       if( $amount == '-1' )\r
+                               $query .= ' LIMIT 9999999';\r
+                       elseif( $amount )\r
+                               $query .= ' LIMIT '.intval($amount);\r
+                       \r
+                       if( $amount != 0)\r
+                               $res = sql_query($query);\r
+\r
+                       $gVars = array(\r
+                               'action' => $this->getTrackBackUrl(intval($tb_id)),\r
+                               'form'   => $this->getManualPingUrl(intval($tb_id)),\r
+                               'required' => $this->getRequiredURL(intval($tb_id)),\r
+                       );\r
+                       \r
+                       if ( $member->isLoggedIn() ){\r
+                               $adminurl = htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'] . 'trackback/index.php?action=list&id=' . intval($tb_id)), ENT_QUOTES);\r
+                               $pingformurl = htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'] . 'trackback/index.php?action=ping&id=' . intval($tb_id)), ENT_QUOTES);\r
+                               $gVars['admin'] = '<a href="' . $adminurl . '" target="_blank">[admin]</a>';\r
+                               $gVars['pingform'] = '<a href="' . $pingformurl . '" target="_blank">[pingform]</a>';\r
+                       }\r
+\r
+                       echo TEMPLATE::fill($this->getOption('tplHeader'), $gVars);\r
+\r
+\r
+                       while ($amount != 0 && $row = mysql_fetch_array($res))\r
+                       {\r
+\r
+                               $row['blog_name']       = htmlspecialchars($row['blog_name'], ENT_QUOTES);\r
+                               $row['title']           = htmlspecialchars($row['title'], ENT_QUOTES);\r
+                               $row['excerpt']         = htmlspecialchars($row['excerpt'], ENT_QUOTES);\r
+\r
+/*\r
+*/\r
+                               if (_CHARSET != 'UTF-8') {\r
+//modify start+++++++++\r
+/*\r
+                                       $row['blog_name']       = $this->_utf8_to_entities($row['blog_name']);\r
+                                       $row['title']           = $this->_utf8_to_entities($row['title']);\r
+                                       $row['excerpt']         = $this->_utf8_to_entities($row['excerpt']);\r
+*/\r
+                                       $row['blog_name']       = $this->_restore_to_utf8($row['blog_name']);\r
+                                       $row['title']           = $this->_restore_to_utf8($row['title']);\r
+                                       $row['excerpt']         = $this->_restore_to_utf8($row['excerpt']);\r
+\r
+                                       $row['blog_name']       = mb_convert_encoding($row['blog_name'], _CHARSET, 'UTF-8');\r
+                                       $row['title']           = mb_convert_encoding($row['title'], _CHARSET, 'UTF-8');\r
+                                       $row['excerpt']         = mb_convert_encoding($row['excerpt'], _CHARSET, 'UTF-8');\r
+//modify end+++++++++\r
+                               }                               \r
+\r
+//modify start+++++++++\r
+/*\r
+                               $iVars = array(\r
+                                       'action'        => $this->getTrackBackUrl($tb_id),\r
+                                       'form'          => $this->getManualPingUrl($tb_id),\r
+                                       'name'          => $row['blog_name'],\r
+                                       'title'         => $row['title'],\r
+                                       'excerpt'       => $row['excerpt'],\r
+                                       'url'           => htmlspecialchars($row['url'], ENT_QUOTES),\r
+                                       'date'          => htmlspecialchars(strftime($this->getOption('dateFormat'), $row['timestamp'] + ($blog->getTimeOffset() * 3600)), ENT_QUOTES)\r
+                               );\r
+*/\r
+                               $iVars = array(\r
+                                       'action'        => $this->getTrackBackUrl($tb_id),\r
+                                       'form'          => $this->getManualPingUrl($tb_id),\r
+                                       'name'          => htmlspecialchars($row['blog_name'], ENT_QUOTES),\r
+                                       'title'         => htmlspecialchars($row['title'], ENT_QUOTES),\r
+                                       'excerpt'       => htmlspecialchars($this->_cut_string($row['excerpt'], 400), ENT_QUOTES),\r
+                                       'url'           => htmlspecialchars($row['url'], ENT_QUOTES),\r
+                                       'date'          => htmlspecialchars(strftime($this->getOption('dateFormat'), $row['timestamp']), ENT_QUOTES)\r
+                               );\r
+\r
+//mod by cles\r
+                               if( $enableHideurl && $this->getOption('HideUrl') == 'yes' )\r
+                                       $iVars['url'] = $CONF['ActionURL'] . '?action=plugin&amp;name=TrackBack&amp;type=redirect&amp;tb_id=' . intval($tb_id) . '&amp;urlHash=' . $row['urlHash'];\r
+                               else\r
+                                       $iVars['url'] = $row['url'];\r
+//mod by cles end\r
+\r
+//modify end+++++++++\r
+                               echo TEMPLATE::fill($this->getOption('tplItem'), $iVars);\r
+                               \r
+                       }\r
+\r
+//modify start+++++++++\r
+                       $q = '\r
+                               SELECT \r
+                                       count(*) \r
+                               FROM \r
+                                       '.sql_table('plugin_tb').' \r
+                               WHERE \r
+                                       tb_id = '.intval($tb_id) .' AND\r
+                                       block = 0\r
+                               ORDER BY \r
+                                       timestamp DESC\r
+                       ';\r
+                       $result = sql_query($q);\r
+                       $total = mysql_result($result,0,0);\r
+\r
+                       if($amount != -1 && $total > $amount){\r
+                               $leftcount = $total - $amount;\r
+\r
+                               echo '<script type="text/javascript" src="' . $this->getAdminURL() . 'detectlist.php?tb_id='.intval($tb_id).'&amp;amount='.intval($amount).'"></script>';\r
+\r
+?>\r
+\r
+<a name="restoftrackback" id="restoftrackback"></a>\r
+<div id="tbshownavi"><a href="#restoftrackback" onclick="resttbStart(); return false;" id="tbshow">Show left <?php echo $leftcount;?> Trackbacks</a></div>\r
+<div id="tbhidenavi" style="display: none;"><a href="#restoftrackback" onclick="hideresttb(); return false;">Hide <?php echo $leftcount;?> Trackbacks</a></div>\r
+<div id="resttb"></div>\r
+\r
+<?php\r
+                       }\r
+//modify end+++++++++\r
+\r
+                       if (mysql_num_rows($res) == 0) \r
+                       {\r
+                               echo TEMPLATE::fill($this->getOption('tplEmpty'), $gVars);\r
+                       }\r
+                       mysql_free_result($res);\r
+                       \r
+                       echo TEMPLATE::fill($this->getOption('tplFooter'), $gVars);\r
+\r
+               }\r
+                       \r
+               /*\r
+                * Returns the TrackBack count for a TrackBack item\r
+                */\r
+               function getTrackBackCount($tb_id) {\r
+                       return quickQuery('SELECT COUNT(*) as result FROM ' . sql_table('plugin_tb') . ' WHERE tb_id='.intval($tb_id).' AND block = 0');\r
+               }\r
+               \r
+               /**\r
+                 * Returns the manual ping URL\r
+                 */\r
+               function getManualPingUrl($itemid) {\r
+                       global $CONF;\r
+                       return $CONF['ActionURL'] . '?action=plugin&amp;name=TrackBack&amp;type=form&amp;tb_id='.$itemid;\r
+               }\r
+\r
+               /**\r
+                 * Show the manual ping form\r
+                 */\r
+               function showManualPingError($itemid, $status = '') {\r
+                       global $CONF;\r
+\r
+                       $form = true; $error = true; $success = false;\r
+                       sendContentType('text/html', 'admin-trackback', _CHARSET);      \r
+//modify start+++++++++\r
+//                     include ($this->getDirectory() . '/templates/form.html');\r
+                       require_once($this->getDirectory() . '/template.php');\r
+                       $mTemplate = new Trackback_Template(null, $this->getDirectory());\r
+                       $mTemplate->set ('CONF', $CONF);\r
+                       $mTemplate->set ('itemid', $itemid);\r
+                       $mTemplate->set ('form', $form);\r
+                       $mTemplate->set ('error', $error);\r
+                       $mTemplate->set ('success', $success);\r
+                       $mTemplate->set ('status', $status);\r
+                       $mTemplate->template('templates/form.html');\r
+                       echo $mTemplate->fetch();\r
+//modify end+++++++++\r
+               }\r
+               \r
+               function showManualPingSuccess($itemid, $status = '') {\r
+                       global $CONF;\r
+\r
+                       $form = false; $error = false; $success = true;\r
+                       sendContentType('text/html', 'admin-trackback', _CHARSET);      \r
+//modify start+++++++++\r
+                       //include ($this->getDirectory() . '/templates/form.html');\r
+                       require_once($this->getDirectory() . '/template.php');\r
+                       $mTemplate = new Trackback_Template(null, $this->getDirectory());\r
+                       $mTemplate->set ('CONF', $CONF);\r
+                       $mTemplate->set ('itemid', $itemid);\r
+                       $mTemplate->set ('form', $form);\r
+                       $mTemplate->set ('error', $error);\r
+                       $mTemplate->set ('success', $success);\r
+                       $mTemplate->set ('status', $status);\r
+                       $mTemplate->template('templates/form.html');\r
+                       echo $mTemplate->fetch();\r
+//modify end+++++++++\r
+               }\r
+               \r
+               function showManualPingForm($itemid, $text = '') {\r
+                       global $CONF;\r
+\r
+                       $form = true; $error = false; $success = false;\r
+\r
+                       // Check if we are allowed to accept pings\r
+                       if ( !$this->isAcceptTrackBack($itemid) ) {\r
+                               $text = 'Sorry, no trackback pings are accepted';\r
+                               $form = false; $error = true;\r
+                       }\r
+                       \r
+                       sendContentType('text/html', 'admin-trackback', _CHARSET);      \r
+//modify start+++++++++\r
+                       //include ($this->getDirectory() . '/templates/form.html');\r
+                       require_once($this->getDirectory() . '/template.php');\r
+                       $mTemplate = new Trackback_Template(null, $this->getDirectory());\r
+                       $mTemplate->set ('CONF', $CONF);\r
+                       $mTemplate->set ('itemid', $itemid);\r
+                       $mTemplate->set ('form', $form);\r
+                       $mTemplate->set ('error', $error);\r
+                       $mTemplate->set ('success', $success);\r
+                       $mTemplate->set ('status', $status);\r
+                       $mTemplate->template('templates/form.html');\r
+                       echo $mTemplate->fetch();\r
+//modify end+++++++++\r
+               }\r
+       \r
+               /**\r
+                 * Returns the trackback URL\r
+                 */\r
+               function getTrackBackUrl($itemid) {\r
+                       global $CONF, $manager;\r
+                       return $CONF['ActionURL'] . '?action=plugin&amp;name=TrackBack&amp;tb_id='.$itemid;\r
+               }               \r
+\r
+               /*\r
+                * Insert RDF code for item\r
+                */\r
+               function insertCode($itemid) {\r
+                       $itemid = intval($itemid);\r
+                       global $manager, $CONF;\r
+\r
+                       $item = & $manager->getItem($itemid, 0, 0);\r
+                       $blog = & $manager->getBlog(getBlogIDFromItemID($item['itemid']));\r
+                               \r
+/*\r
+                       $CONF['ItemURL'] = preg_replace('/\/$/', '', $blog->getURL());   \r
+                       $uri    = createItemLink($item['itemid'],'');   \r
+*/\r
+                       $uri    = $this->_createItemLink($item['itemid'],$blog);        \r
+                                       \r
+                       $title  = strip_tags($item['title']);\r
+                       $desc   = strip_tags($item['body']);\r
+                       $desc   = $this->_cut_string($desc, 200);\r
+                       $desc   = htmlspecialchars($desc, ENT_QUOTES);\r
+                       \r
+                       ?>\r
+                       <!--
+                       <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+                                        xmlns:dc="http://purl.org/dc/elements/1.1/"
+                                        xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
+                       <rdf:Description
+                                        rdf:about="<?php echo $uri; ?>"
+                                        dc:identifier="<?php echo $uri; ?>"
+                                        dc:title="<?php echo $title; ?>"
+                                        dc:description="<?php echo $desc; ?>"
+                                        trackback:ping="<?php echo $this->getTrackBackUrl($itemid)?>"
+                                        dc:date="<?php echo strftime('%Y-%m-%dT%H:%M:%S')?>" />
+                       </rdf:RDF>
+                       -->
+                       <?php\r
+               }\r
+\r
+               /**\r
+                * Retrieving TrackBack Pings (when __mode=rss)\r
+                */\r
+               function rssResponse($tb_id) {\r
+                       $itemid = intval($itemid);\r
+                       global $manager, $CONF;\r
+                       $item =& $manager->getItem($tb_id, 0, 0);\r
+       \r
+                       if($item)\r
+                       {\r
+                               $blog =& $manager->getBlog(getBlogIDFromItemID($item['itemid']));\r
+                               \r
+                               $blog_name  = $blog->getName();\r
+                               $title      = $item['title'];\r
+                               $excerpt    = $item['body'];\r
+\r
+//modify start+++++++++\r
+/*\r
+                               if (_CHARSET != 'UTF-8')\r
+                               {\r
+                                       $title          = $this->_convert_to_utf8($title, $encoding);\r
+                                       $excerpt    = $this->_convert_to_utf8($excerpt, $encoding);\r
+                                       $blog_name  = $this->_convert_to_utf8($blog_name, $encoding);\r
+                               }\r
+\r
+                               $title      = $this->_decode_entities(strip_tags($title));\r
+                               $excerpt    = $this->_decode_entities(strip_tags($excerpt));\r
+                               $blog_name  = $this->_decode_entities(strip_tags($blog_name));\r
+*/\r
+\r
+                               $title      = $this->_restore_to_utf8($title);\r
+                               $excerpt    = $this->_restore_to_utf8($excerpt);\r
+                               $blog_name  = $this->_restore_to_utf8($blog_name);\r
+//modify end+++++++++\r
+\r
+                               $excerpt    = $this->_cut_string($excerpt, 200);\r
+\r
+                               \r
+//modify start+++++++++\r
+/*\r
+                               $CONF['ItemURL'] = preg_replace('/\/$/', '', $blog->getURL());   \r
+                               $url    = createItemLink($item['itemid'],'');   \r
+*/\r
+                               $url    = $this->_createItemLink($item['itemid'],$blog);        \r
+//modify end+++++++++\r
+       \r
+                               // Use UTF-8 charset for output\r
+                               header('Content-Type: text/xml');\r
+                               echo "<","?xml version='1.0' encoding='UTF-8'?",">\n";\r
+                               \r
+                               echo "<response>\n";\r
+                               echo "\t<error>0</error>\n";\r
+                               echo "\t<rss version='0.91'>\n";\r
+                               echo "\t\t<channel>\n";\r
+                               echo "\t\t\t<title>".htmlspecialchars($title, ENT_QUOTES)."</title>\n";\r
+                               echo "\t\t\t<link>".htmlspecialchars($url, ENT_QUOTES)."</link>\n";\r
+                               echo "\t\t\t<description>".htmlspecialchars($excerpt, ENT_QUOTES)."</description>\n";\r
+       \r
+                               $query = 'SELECT url, blog_name, excerpt, title, UNIX_TIMESTAMP(timestamp) as timestamp FROM '.sql_table('plugin_tb').' WHERE tb_id='.intval($tb_id).' AND block = 0 ORDER BY timestamp DESC';\r
+                               $res = sql_query($query);\r
+                               while ($o = mysql_fetch_object($res)) \r
+                               {\r
+                                       // No need to do conversion, because it is already UTF-8\r
+                                       $data = array (\r
+                                               'url'           => htmlspecialchars($o->url, ENT_QUOTES),\r
+                                               'blogname'      => htmlspecialchars($this->_restore_to_utf8($o->blog_name), ENT_QUOTES),\r
+                                               'timestamp' => strftime('%Y-%m-%d',$o->timestamp),\r
+                                               'title'         => htmlspecialchars($this->_restore_to_utf8($o->title), ENT_QUOTES),\r
+                                               'excerpt'       => htmlspecialchars($this->_restore_to_utf8($o->excerpt), ENT_QUOTES),\r
+                                               'tburl'         => $this->getTrackBackUrl($tb_id)\r
+                                       );\r
+                                       \r
+                                       echo "\n";\r
+                                       echo "\t\t\t<item>\n";\r
+                                       echo "\t\t\t\t<title>".$data['title']."</title>\n";\r
+                                       echo "\t\t\t\t<link>".$data['url']."</link>\n";\r
+                                       echo "\t\t\t\t<description>".$data['excerpt']."</description>\n";\r
+                                       echo "\t\t\t</item>\n";\r
+                               }\r
+                               echo "\t\t</channel>\n";\r
+                               echo "\t</rss>\n";\r
+                               echo "</response>";\r
+                               exit;\r
+                       }\r
+                       else\r
+                       {\r
+                               $this->xmlResponse(_ERROR_NOSUCHITEM);\r
+                       }\r
+       \r
+               }\r
+\r
+\r
+\r
+       /**************************************************************************************\r
+        * SENDING AND RECEIVING TRACKBACK PINGS\r
+                */\r
+\r
+               /* \r
+                *  Send a Trackback ping to another website\r
+                */\r
+               function sendPing($itemid, $title, $url, $excerpt, $blog_name, $ping_url) \r
+               {\r
+                       $sendEncoding = 'UTF-8';\r
+                       \r
+                       // 1. Check some basic things\r
+                       if (!$this->canSendPing()) {\r
+                               return 'You\'re not allowed to send pings';\r
+                       }\r
+                       \r
+                       if ($this->getOption('SendPings') == 'no') {\r
+                               return 'Sending trackback pings is disabled';\r
+                       }\r
+                       \r
+                       if ($ping_url == '') {\r
+                               return 'No ping URL';\r
+                       }\r
+       \r
+                       // 2. Check if protocol is correct http URL\r
+                       $parsed_url = parse_url($ping_url);\r
+\r
+                       if ($parsed_url['scheme'] != 'http' || $parsed_url['host'] == '')\r
+                               return 'Bad ping URL';\r
+       \r
+                       $port = ($parsed_url['port']) ? $parsed_url['port'] : 80;\r
+       \r
+                       // 3. Create contents\r
+                       if($sendEncoding != _CHARSET){\r
+                               $title = mb_convert_encoding($title, $sendEncoding, _CHARSET);\r
+                               $excerpt = mb_convert_encoding($excerpt, $sendEncoding, _CHARSET);\r
+                               $blog_name = mb_convert_encoding($blog_name, $sendEncoding, _CHARSET);\r
+                       }\r
+                       \r
+                       \r
+                       $content  = 'title=' .  urlencode( $title );\r
+                       $content .= '&url=' .           urlencode( $url );\r
+                       $content .= '&excerpt=' .       urlencode( $excerpt );\r
+                       $content .= '&blog_name=' . urlencode( $blog_name );\r
+       \r
+                       // 4. Prepare HTTP request\r
+                       $request  = 'POST ' . $parsed_url['path'];\r
+\r
+                       if ($parsed_url['query'] != '')\r
+                               $request .= '?' . $parsed_url['query'];\r
+                               \r
+                       $request .= " HTTP/1.1\r\n";\r
+                       $request .= "Accept: */*\r\n";\r
+                       $request .= "User-Agent: " . $this->userAgent . "\r\n";\r
+                       $request .= ( $port == 80 )?\r
+                                                                       "Host: " . $parsed_url['host'] . "\r\n":\r
+                                                                       "Host: " . $parsed_url['host'] . ":" . $port . "\r\n";\r
+                       $request .= "Cache-Control: no-cache\r\n";\r
+                       $request .= "Connection: Close\r\n";\r
+                       $request .= "Content-Length: " . strlen( $content ) . "\r\n";\r
+                       $request .= "Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n";\r
+                       $request .= "\r\n";\r
+                       $request .= $content;\r
+       \r
+                       $socket = fsockopen( $parsed_url['host'], $port, $errno, $errstr );\r
+                       if ( ! $socket )\r
+                               return 'Could not send ping: '.$errstr.' ('.$errno.')';\r
+       \r
+                       // 5. Execute HTTP request\r
+                       fputs($socket, $request);\r
+       \r
+                       // 6. Receive response\r
+                       $result = '';\r
+                       while (!feof($socket)) {\r
+                               $result .= fgets($socket, 4096);\r
+                       }\r
+                       \r
+                       fclose($socket);\r
+       \r
+//modify start+++++++++\r
+                       list($header, $body) = split("\r\n\r\n", $result, 2);\r
+                       preg_match("/HTTP\/1\.[0-1] ([0-9]+) ([^\r\n]*)\r?\n/", $header, $httpresp);\r
+                       $respCd = $httpresp[1];\r
+                       $respMsg = $httpresp[2];\r
+\r
+                       if( $respCd != 200 ){\r
+                               return 'An error occurred: HTTP Error: [' . $respCd . '] ' . $respMsg;\r
+                       }\r
+                       \r
+                       if( defined('NP_TRACKBACK_USE_XML_PARSER') && function_exists('xml_parser_create') ){\r
+                               $p = new NP_TrackBack_XMLParser();\r
+\r
+                               # remove invalid string\r
+                               $body = strstr($body, '<');\r
+                               $body = substr($body, 0, strrpos($body,'>') === false ? 0 : strrpos($body,'>') + 1 );\r
+\r
+                               $p->parse($body);\r
+                               $p->free();\r
+                               if( $p->isError ){\r
+                                       $errorMessage = mb_convert_encoding($p->message, _CHARSET, 'UTF-8');\r
+                                       return 'An error occurred: ' . htmlspecialchars($errorMessage, ENT_QUOTES);\r
+                               }\r
+                       } else {\r
+                               if ( strpos($DATA[1],'<error>0</error>') === false ){\r
+                                       preg_match("/<message>(.*?)<\/message>/",$DATA[1],$error_message);\r
+                                       if( $error_message[1] ){\r
+                                               $errorMessage = mb_convert_encoding($error_message[1], _CHARSET);\r
+                                               return 'An error occurred: '.htmlspecialchars($errorMessage, ENT_QUOTES);\r
+                                       } else {\r
+                                               return 'An error occurred: fatal error.';\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       return '';\r
+               } \r
+//modify end+++++++++\r
+\r
+               /* \r
+                *  Handle a Trackback ping sent to this website\r
+                */\r
+               function handlePing($tb_id = 0) {\r
+                       global $manager;\r
+                       \r
+                       // Defaults\r
+                       $spam       = false;\r
+                       $link       = false;\r
+//modify start+++++++++\r
+//                     $block      = true;\r
+                       $block      = false;\r
+//modify end+++++++++\r
+                       if ($tb_id == 0)\r
+                       $tb_id          = intRequestVar('tb_id');\r
+                       \r
+                       $rss            = requestVar('__mode') == 'rss'; \r
+//mod by cles\r
+                       $enableLinkCheck = $this->isEnableLinkCheck($tb_id);\r
+                       $block = ( $enableLinkCheck ) ? true : false ;\r
+//mod by cles end\r
+\r
+                       if (!$tb_id) {\r
+                               return 'TrackBack ID is missing (tb_id)';\r
+                       }\r
+                       \r
+                       if ((!$manager->existsItem($tb_id,0,0)) && ($this->getOption('CheckIDs') == 'yes')) {\r
+                               return _ERROR_NOSUCHITEM;\r
+                       }\r
+\r
+                       // 0. Check if we need to output the list as rss\r
+                       if ($rss) {\r
+                               $this->rssResponse($tb_id);\r
+                               return;\r
+                       }\r
+//mod by cles\r
+                       // check: accept pings.\r
+                       $blogId = getBlogIDFromItemID($tb_id);\r
+                       $isAcceptPing = $this->isAcceptTrackBack($tb_id);\r
+                               \r
+                       if (! $isAcceptPing)\r
+                               return 'Sorry, no trackback pings are accepted.';\r
+//mod by cles end\r
+\r
+                       // 1. Get attributes\r
+//modify start+++++++++\r
+                       $b =& $manager->getBlog($blogId);\r
+//modify end+++++++++\r
+                       $url            = requestVar('url');\r
+                       $title          = requestVar('title');\r
+                       $excerpt        = requestVar('excerpt');\r
+                       $blog_name      = requestVar('blog_name');\r
+                       \r
+                       if( $url && preg_match('/https?:\/\/([^\/]+)/', $url, $matches) ){\r
+                               if( gethostbynamel($matches[1]) === FALSE )\r
+                                       return 'URL is invalid (url)';\r
+                       } else {\r
+                               return 'URL is missing or invalid (url)';\r
+                       }\r
+\r
+                       // 2. Conversion of encoding...\r
+//modify start+++++++++\r
+/*                     if (preg_match ("/;\s*charset=([^\n]+)/is", $_SERVER["CONTENT_TYPE"], $regs))\r
+                               $encoding = strtoupper(trim($regs[1]));\r
+                       else\r
+                               $encoding = $this->_detect_encoding($excerpt);\r
+*/\r
+                       $encoding = $this->_detect_encoding($excerpt);\r
+//modify end+++++++++\r
+                       \r
+//modify start+++++++++\r
+                       if (_CHARSET != 'UTF-8'){\r
+                               $title = $this->_strip_controlchar(strip_tags(mb_convert_encoding($title, _CHARSET, $encoding)));\r
+                               $excerpt = $this->_strip_controlchar(strip_tags(mb_convert_encoding($excerpt, _CHARSET, $encoding)));\r
+                               $blog_name = $this->_strip_controlchar(strip_tags(mb_convert_encoding($blog_name, _CHARSET, $encoding)));\r
+                       }else{\r
+                               $title      = $this->_strip_controlchar($this->_convert_to_utf8($title, $encoding));\r
+                               $excerpt    = $this->_strip_controlchar($this->_convert_to_utf8($excerpt, $encoding));\r
+                               $blog_name  = $this->_strip_controlchar($this->_convert_to_utf8($blog_name, $encoding));\r
+\r
+                               $title      = $this->_decode_entities(strip_tags($title));\r
+                               $excerpt    = $this->_decode_entities(strip_tags($excerpt));\r
+                               $blog_name  = $this->_decode_entities(strip_tags($blog_name));\r
+                       }\r
+//modify end+++++++++\r
+\r
+                       // 4. Save data in the DB\r
+                       $res = @sql_query('\r
+                               SELECT \r
+                                       tb_id, block, spam\r
+                               FROM \r
+                                       '.sql_table('plugin_tb').' \r
+                               WHERE \r
+                                       url   = \''.mysql_real_escape_string($url).'\' AND \r
+                                       tb_id = \''.intval($tb_id).'\'\r
+                       ');\r
+                       \r
+                       if (mysql_num_rows($res) != 0) \r
+                       {\r
+                               // Existing TB, update it\r
+/*\r
+                               $res = @sql_query('\r
+                                       UPDATE\r
+                                               '.sql_table('plugin_tb').'\r
+                                       SET \r
+                                               title     = "'.mysql_real_escape_string($title).'", \r
+                                               excerpt   = "'.mysql_real_escape_string($excerpt).'", \r
+                                               blog_name = "'.mysql_real_escape_string($blog_name).'", \r
+                                               timestamp = '.mysqldate(time()).'\r
+                                       WHERE \r
+                                               url       = "'.mysql_real_escape_string($url).'" AND \r
+                                               tb_id     = "'.$tb_id.'"\r
+                               ');\r
+*/\r
+//modify start+++++++++\r
+                               $rows = mysql_fetch_assoc($res);\r
+                               $spam = ( $rows['block'] || $rows['spam'] ) ? true : false;\r
+                               $res = @sql_query('\r
+                                       UPDATE\r
+                                               '.sql_table('plugin_tb').'\r
+                                       SET \r
+                                               title     = \''.mysql_real_escape_string($title).'\', \r
+                                               excerpt   = \''.mysql_real_escape_string($excerpt).'\', \r
+                                               blog_name = \''.mysql_real_escape_string($blog_name).'\', \r
+                                               timestamp = '.mysqldate($b->getCorrectTime()).'\r
+                                       WHERE \r
+                                               url       = \''.mysql_real_escape_string($url).'\' AND \r
+                                               tb_id     = \''.mysql_real_escape_string(intval($tb_id)).'\'\r
+                               ');\r
+//modify end+++++++++\r
+\r
+                               if (!$res) {\r
+                                       return 'Could not update trackback data: '.mysql_error();\r
+                               }\r
+                       } \r
+                       else \r
+                       {\r
+//mod by cles\r
+                               // spam block\r
+                               $res = @sql_query('SELECT id FROM '.sql_table('plugin_tb').' WHERE block = 1 and url = \''.mysql_real_escape_string($url).'\'' );\r
+                               if (mysql_num_rows($res) != 0) {\r
+                                       // NP_Trackback has blocked tb !\r
+                                       ACTIONLOG :: add(INFO, "Trackback: Duplicated Blocked Trackback [ignore] (itemid:$tb_id from: $url)");\r
+                                       return 'Sorry, trackback ping is not accepted.';\r
+                               }\r
+//mod by cles end\r
+                                                       \r
+                               // 4. SPAM check (for SpamCheck API 2 /w compat. API 1)\r
+                               $spamcheck = array (\r
+                                       'type'          => 'trackback',\r
+                                       'id'            => $tb_id,\r
+                                       'title'         => $title,\r
+                                       'excerpt'       => $excerpt,\r
+                                       'blogname'      => $blog_name,\r
+                                       'url'           => $url,\r
+                                       'return'        => true,\r
+                                       'live'          => true,\r
+                                       \r
+                                       /* Backwards compatibility with SpamCheck API 1*/\r
+                                       'data'          => $url . "\n" . $title . "\n" . $excerpt . "\n" . $blog_name . "\n" . serverVar('HTTP_USER_AGENT'),\r
+                                       'ipblock'   => true,\r
+                               );\r
+                               \r
+                               $manager->notify('SpamCheck', array ('spamcheck' => & $spamcheck));\r
+                               \r
+                               if (isset($spamcheck['result']) && $spamcheck['result'] == true) \r
+                               {\r
+                                       $spam = true;\r
+                               }\r
+                               \r
+                               // 5. Content check (TO DO)\r
+                               if($spam == false || $enableLinkCheck == 'ignore' )     //modify\r
+                               {\r
+//mod by cles\r
+//                                     $contents = $this->retrieveUrl ($url);\r
+//                             \r
+//                                     if (preg_match("/(".preg_quote($_SERVER["REQUEST_URI"], '/').")|(".preg_quote($_SERVER["SERVER_NAME"], '/').")/i", $contents)) {        \r
+//                                             $link = true;\r
+//                                     }\r
+                                       if( $enableLinkCheck ){\r
+                                               $contents = $this->retrieveUrl($url);\r
+                                               \r
+                                               $linkArray = $this->getPermaLinksFromText($contents);\r
+                                               \r
+                                               if( defined('NP_TRACKBACK_LINKCHECK_STRICT') )\r
+                                                       $itemLink = $this->_createItemLink($tb_id, $b);\r
+                                               else\r
+                                                       $itemLink = $b->getURL();\r
+                                               \r
+                                               $itemLinkPat = '{^' . preg_quote($itemLink) .'}i';\r
+                                               $itemLinkPat = str_replace('&','&(amp;)?', $itemLinkPat);\r
+                                               \r
+                                               foreach($linkArray as $l) {\r
+                                                       if(preg_match($itemLinkPat, $l)){\r
+                                                               ACTIONLOG :: add(INFO, "Trackback: LinkCheck OK. (link: $l pat: $itemLinkPat )");\r
+                                                               $link = true;\r
+                                                               break;\r
+                                                       }\r
+                                               }\r
+                                               if( ! $link ){\r
+                                                       $cnt = @count($linkArray);\r
+                                                       if( $enableLinkCheck == 'ignore' ){\r
+                                                               ACTIONLOG :: add(INFO, "Trackback: LinkCheck NG. [ignore] (itemid:$tb_id from: $url cnt: $cnt pat: $itemLinkPat)");\r
+                                                               return 'Sorry, trackback ping is not accepted.';\r
+                                                       } else {\r
+                                                               ACTIONLOG :: add(INFO, "Trackback: LinkCheck NG. [block] (itemid:$tb_id from: $url cnt: $cnt pat: $itemLinkPat");\r
+                                                       }\r
+                                               }\r
+                                       }\r
+//mod by cles end\r
+                               }\r
+\r
+                               // 6. Determine if Trackback is safe...\r
+//modify start+++++++++\r
+//                             $block = $spam == true || $link == false;\r
+//                             $block = $spam == true ;\r
+//modify end+++++++++\r
+//mod by cles\r
+                               if ( $enableLinkCheck )\r
+                                       $block = ($spam == true || $link == false);\r
+                               else\r
+                                       $block = $spam == true ;\r
+//mod by cles end\r
+                               // New TB, insert it\r
+/*\r
+                               $query = '\r
+                                       INSERT INTO \r
+                                               '.sql_table('plugin_tb').' \r
+                                       SET\r
+                                               tb_id     = "'.$tb_id.'",\r
+                                               block     = "'.($block ? '1' : '0').'",\r
+                                               spam      = "'.($spam ? '1' : '0').'",\r
+                                               link      = "'.($link ? '1' : '0').'",\r
+                                               url       = "'.mysql_real_escape_string($url).'",\r
+                                               title     = "'.mysql_real_escape_string($title).'",\r
+                                               excerpt   = "'.mysql_real_escape_string($excerpt).'",\r
+                                               blog_name = "'.mysql_real_escape_string($blog_name).'",\r
+                                               timestamp = '.mysqldate(time()).'\r
+                               ';\r
+*/\r
+//modify start+++++++++\r
+                               $query = '\r
+                                       INSERT INTO \r
+                                               '.sql_table('plugin_tb').' \r
+                                       SET\r
+                                               tb_id     = \''.mysql_real_escape_string(intval($tb_id)).'\',\r
+                                               block     = \''.($block ? '1' : '0').'\',\r
+                                               spam      = \''.($spam ? '1' : '0').'\',\r
+                                               link      = \''.($link ? '1' : '0').'\',\r
+                                               url       = \''.mysql_real_escape_string($url).'\',\r
+                                               title     = \''.mysql_real_escape_string($title).'\',\r
+                                               excerpt   = \''.mysql_real_escape_string($excerpt).'\',\r
+                                               blog_name = \''.mysql_real_escape_string($blog_name).'\',\r
+                                               timestamp = '.mysqldate($b->getCorrectTime()).'\r
+                               ';\r
+//modify end+++++++++\r
+                               \r
+                               $res = @sql_query($query);\r
+\r
+                               if (!$res) {\r
+                                       return 'Could not save trackback data, possibly because of a double entry: ' . mysql_error() . $query;\r
+                               }\r
+                       }\r
+       \r
+                       // 7. Send notification e-mail if needed\r
+                       $notifyAddrs = $this->getOption('NotifyEmail');\r
+                       $notifyAddrs = ( $notifyAddrs ? $notifyAddrs . ';' : '') \r
+                                                       . $this->getBlogOption($blogId ,'NotifyEmailBlog');\r
+                                               \r
+                       if ($notifyAddrs && $spam == false) \r
+                       {\r
+                               \r
+                               $vars = array (\r
+                                       'tb_id'    => $tb_id,\r
+                                       'url'      => $url,\r
+                                       'title'    => $title,\r
+                                       'excerpt'  => $excerpt,\r
+                                       'blogname' => $blog_name\r
+                               );\r
+                               \r
+//modify start+++++++++\r
+/*\r
+                               $vars = array (\r
+                                       'tb_id'    => $tb_id,\r
+                                       'url'      => $url,\r
+                                       'title'    => mb_convert_encoding($title, 'ISO-2022-JP', _CHARSET),\r
+                                       'excerpt'  => mb_convert_encoding($excerpt, 'ISO-2022-JP', _CHARSET),\r
+                                       'blogname' => mb_convert_encoding($blog_name, 'ISO-2022-JP', _CHARSET)\r
+                               );\r
+*/                             \r
+//maybe not needed because japanese version has "mb_send_mail" in function notify\r
+//modify end+++++++++\r
+                               \r
+                               $mailto_title = TEMPLATE::fill($this->notificationMailTitle, $vars);\r
+                               $mailto_msg   = TEMPLATE::fill($this->notificationMail, $vars);\r
+       \r
+                               global $CONF, $DIR_LIBS;\r
+                               \r
+                               // make sure notification class is loaded\r
+                               if (!class_exists('notification'))\r
+                                       include($DIR_LIBS . 'NOTIFICATION.php');\r
+                               \r
+                               $notify = new NOTIFICATION($notifyAddrs);\r
+                               $notify->notify($mailto_title, $mailto_msg , $CONF['AdminEmail']);\r
+                               \r
+//mod by cles+++++++++++       \r
+                               if ($manager->pluginInstalled('NP_Cache')){\r
+                                       $p =& $manager->getPlugin('NP_Cache');\r
+                                       $p->setCurrentBlog($tb_id);\r
+                                       $p->cleanItem($tb_id);\r
+                                       $p->cleanArray(array('index'));\r
+                               }\r
+//mod by cles end +++++++++++  \r
+                       }\r
+\r
+                       if( $block )\r
+                               return 'Sorry, trackback ping is not accepted.';\r
+                       return '';\r
+               }       \r
+\r
+               function xmlResponse($errorMessage = '') \r
+               {\r
+                       header('Content-type: application/xml; charset=utf-8');\r
+                       echo "<"."?xml version='1.0' encoding='UTF-8'?".">\n";\r
+                       echo "<response>\n";\r
+\r
+                       if ($errorMessage){\r
+                               if (_CHARSET != 'UTF-8')\r
+                                       $errorMessage = mb_convert_encoding($errorMessage, 'UTF-8');\r
+                               echo "<error>1</error>\n";\r
+                               echo "<message>".htmlspecialchars($errorMessage, ENT_QUOTES)."</message>\n";\r
+                       } else {\r
+                               echo "<error>0</error>\n";\r
+                       }\r
+\r
+                       echo "</response>";\r
+                       exit;\r
+               }\r
+               \r
+               /*\r
+                * Check if member may send ping (check if logged in)\r
+                */\r
+               function canSendPing() {\r
+                       global $member;\r
+                       return $member->isLoggedIn() || $this->xmlrpc;\r
+               }\r
+\r
+\r
+//mod by cles\r
+               function redirect($tb_id, $urlHash){\r
+                       global $CONF;\r
+                       $query = 'SELECT url FROM '.sql_table('plugin_tb').' WHERE tb_id='.intval($tb_id).' and md5(url)="'.$urlHash.'"';\r
+                       $res = sql_query($query);\r
+                       \r
+                       $url = $CONF['SiteURL'];\r
+                       \r
+                       if ($o = mysql_fetch_object($res)) {\r
+                               $url = htmlspecialchars($o->url, ENT_QUOTES);\r
+                       }\r
+                       \r
+                       $url = stripslashes($url);\r
+                       $url = str_replace('&amp;','&',$url);\r
+                       $url = str_replace('&lt;','<',$url);\r
+                       $url = str_replace('&gt;','>',$url);\r
+                       $url = str_replace('&quot;','"',$url);\r
+                       \r
+                       header('Location: '.$url);\r
+               }\r
+                               \r
+               function getRequiredURL($itemid){\r
+                       global $manager;\r
+                       $blog = & $manager->getBlog(getBlogIDFromItemID($itemid));\r
+                       if( $this->isEnableLinkCheck($itemid) )\r
+                               return $this->_createItemLink($itemid, $blog);\r
+                       return null;\r
+               }\r
+               \r
+               function isEnableLinkCheck($itemid){\r
+                       $blogid = getBlogIDFromItemID($itemid);\r
+                       \r
+                       switch( $this->getItemOption($itemid, 'isAcceptW/OLink') ){\r
+                               case 'default':\r
+                                       $def = $this->getBlogOption($blogid, 'isAcceptW/OLinkDef');\r
+                                       if($def == 'yes')\r
+                                               return false;\r
+                                       else\r
+                                               return $def; // block or ignore\r
+                               case 'yes':\r
+                                       return false;\r
+                               case 'no':\r
+                                       return true;\r
+                               default :\r
+                                       ACTIONLOG :: add(INFO, "Trackback: Unknown Option (itemid:$itemid, value:$val)");\r
+                                       return false;\r
+                       }\r
+               }\r
+               \r
+               function isAcceptTrackBack($itemid = null){\r
+                       $ret = false;\r
+                       if( $this->getOption('AcceptPing') == 'yes' ){\r
+                               $bid = null;\r
+                               if($itemid){\r
+                                       $bid = getBlogIDFromItemID(intval($itemid));\r
+                               } else {\r
+                                       global $blog;\r
+                                       $bid = $blog->getID();\r
+                               }\r
+                               \r
+                               if( $this->getBlogOption($bid, "AllowTrackBack") == 'yes' ){\r
+                                       if( $itemid ){\r
+                                               $ret = ( $this->getItemOption(intval($itemid), 'ItemAcceptPing') == 'yes' ) ? true : false ;\r
+                                       } else {\r
+                                               $ret = true;\r
+                                       }\r
+                               } else {\r
+                                       $ret = false;\r
+                               }\r
+                       }\r
+                       return $ret;\r
+               }\r
+               \r
+//mod by cles end\r
+\r
+       /**************************************************************************************\r
+        * EVENTS\r
+                */\r
+\r
+               function event_SendTrackback($data) {\r
+                       global $manager;\r
+                       \r
+                       // Enable sending trackbacks for the XML-RPC API, otherwise we would \r
+                       // get an error because the current user is not exactly logged in.\r
+                       $this->xmlrpc = true;\r
+                       \r
+                       $itemid = $data['tb_id'];\r
+                       $item = &$manager->getItem($itemid, 0, 0);\r
+                       if (!$item) return; // don't ping for draft & future\r
+                       if ($item['draft']) return;   // don't ping on draft items\r
+                       \r
+                       // gather some more information, needed to send the ping (blog name, etc)      \r
+                       $blog =& $manager->getBlog(getBlogIDFromItemID($itemid));\r
+                       $blog_name      = $blog->getName();\r
+                       \r
+                       $title      = $data['title'] != '' ? $data['title'] : $item['title'];\r
+                       $title          = strip_tags($title);\r
+                       \r
+                       $excerpt    = $data['body'] != '' ? $data['body'] : $item['body'];\r
+                       $excerpt        = strip_tags($excerpt);\r
+                       $excerpt    = $this->_cut_string($excerpt, 200);\r
+                       \r
+                       $CONF['ItemURL'] = preg_replace('/\/$/', '', $blog->getURL());\r
+                       //$url = createItemLink($itemid);\r
+                       $url = $this->_createItemLink($itemid, $blog);\r
+                       \r
+                       while (list(,$url) = each($data['urls'])) {\r
+                               $res = $this->sendPing($itemid, $title, $url, $excerpt, $blog_name, $url);\r
+                               if ($res) ACTIONLOG::add(WARNING, 'TrackBack Error:' . $res . ' (' . $url . ')');\r
+                       }\r
+               }\r
+               \r
+               function event_RetrieveTrackback($data) {\r
+                       \r
+                       $res = sql_query('\r
+                       SELECT \r
+                       url, \r
+                       title, \r
+                       UNIX_TIMESTAMP(timestamp) AS timestamp \r
+                       FROM \r
+                       '.sql_table('plugin_tb').' \r
+                       WHERE \r
+                       tb_id = '.intval($data['tb_id']).' AND\r
+                       block = 0\r
+                       ORDER BY \r
+                       timestamp ASC\r
+                       ');\r
+                       \r
+                       while ($row = mysql_fetch_array($res)) {\r
+                               \r
+                               $trackback = array(\r
+                               'title' => $row['title'],\r
+                               'url'   => $row['url'],\r
+                               'ip'    => ''\r
+                               );\r
+                               \r
+                               $data['trackbacks'][] = $trackback;\r
+                       }\r
+               }\r
+/*\r
+               function event_BookmarkletExtraHead($data) {\r
+                       global $NP_TB_URL;\r
+                       list ($NP_TB_URL,) = $this->getURIfromLink(requestVar('loglink'));\r
+               } \r
+*/\r
+               function event_PrepareItemForEdit($data) {\r
+//                     if (!$this->getOption('AutoXMLHttp'))\r
+                       if ($this->getOption('AutoXMLHttp') == 'no')\r
+                       {\r
+                               // The space between body and more is to make sure we didn't join 2 words accidently....\r
+                               $this->larray = $this->autoDiscovery($data['item']['body'].' '.$data['item']['more']);\r
+                       }\r
+               } \r
+\r
+               /*\r
+                * After an item has been added to the database, send out a ping if requested\r
+                * (trackback_ping_url variable in request)\r
+                */\r
+               function event_PostAddItem($data) {\r
+                       $this->pingTrackback($data);\r
+               }\r
+       \r
+               function event_PreUpdateItem($data) {\r
+                       $this->pingTrackback($data);\r
+               }\r
+\r
+               /**\r
+                * Add trackback options to add item form/bookmarklet\r
+                */\r
+               function event_AddItemFormExtras($data) {\r
+               \r
+//                     global $NP_TB_URL;\r
+                       \r
+                       ?>\r
+                               <h3>TrackBack</h3>\r
+                               <p>\r
+<!--modify start+++++++++-->\r
+<!--                                   <label for="plug_tb_url">TrackBack Ping URL:</label>\r
+                                       <input type="text" value="<?php if (isSet($NP_TB_URL)) {echo $NP_TB_URL;} ?>" id="plug_tb_url" name="trackback_ping_url" size="60" />\r
+-->\r
+<!--modify end+++++++++-->\r
+                                       <label for="plug_tb_url">TrackBack URL:</label><br />\r
+                                       <textarea id="plug_tb_url" name="trackback_ping_url" cols="60" rows="5" style="font:normal xx-small Tahoma, Arial, verdana ;"></textarea>\r
+<input type="button" name="btnAdd" value="<?php echo _TB_LIST_IT?>" onClick="AddStart()" />\r
+\r
+               <br />\r
+       \r
+                       <?php\r
+//                             if ($this->getOption('AutoXMLHttp'))\r
+                               if ($this->getOption('AutoXMLHttp') == 'yes')\r
+                               {\r
+                       ?>\r
+                                       <div id="tb_auto">\r
+<input type="button" name="discoverit" value="Auto Discover" onclick="tbSetup();" />\r
+                                               <img id='tb_busy' src='<?php echo $this->getAdminURL(); ?>busy.gif' style="display:none;" /><br />\r
+                                       <div id="tb_auto_title"></div>\r
+                                               <table border="1"><tbody id="tb_ping_list"></tbody></table>\r
+                                               <input type="hidden" id="tb_url_amount" name="tb_url_amount" value="0" /> \r
+                                       </div>\r
+                                       \r
+                       <?php\r
+                                       $this->jsautodiscovery();\r
+                               }\r
+                       ?>\r
+                               </p>\r
+                       <?php\r
+               }\r
+\r
+               /**\r
+                * Add trackback options to edit item form/bookmarklet\r
+                */\r
+               function event_EditItemFormExtras($data) {\r
+                       global $CONF;\r
+                       ?>\r
+<!--                                   <input type="text" value="" id="plug_tb_url" name="trackback_ping_url" size="60" /><br />-->\r
+                               <h3>TrackBack</h3>\r
+                               <p>\r
+                                       <label for="plug_tb_url">TrackBack URL:</label><br />\r
+                                       <textarea id="plug_tb_url" name="trackback_ping_url" cols="60" rows="5" style="font:normal xx-small Tahoma, Arial, verdana ;"></textarea>\r
+<input type="button" name="btnAdd" value="<?php echo _TB_LIST_IT?>" onClick="AddStart()" />\r
+       \r
+                       <?php\r
+//                             if ($this->getOption('AutoXMLHttp'))\r
+                               if ($this->getOption('AutoXMLHttp') == 'yes')\r
+                               {\r
+                       ?>\r
+\r
+\r
+                                       <div id="tb_auto">\r
+<input type="button" name="discoverit" value="Auto Discover" onclick="tbSetup();" />\r
+                                               <img id='tb_busy' src='<?php echo $this->getAdminURL(); ?>busy.gif' style="display:none;" /><br />\r
+                                       <div id="tb_auto_title"></div>\r
+                                               <table border="1"><tbody id="tb_ping_list"></tbody></table>\r
+                                               <input type="hidden" id="tb_url_amount" name="tb_url_amount" value="0" /> \r
+                                       </div>\r
+\r
+                       <?php\r
+                                       $this->jsautodiscovery();\r
+                               }\r
+                               else\r
+                               {\r
+                                       if (count($this->larray) > 0) \r
+                                       {\r
+                       ?>\r
+                                       Auto Discovered Ping URL's:<br />\r
+                       <?php\r
+                                               echo '<input type="hidden" name="tb_url_amount" value="'.count($this->larray).'" />';\r
+       \r
+                                               $i = 0;\r
+                                               \r
+                                               while (list($url, $title) = each ($this->larray))\r
+                                               {\r
+//modify start+++++++++\r
+                                                       if (_CHARSET != 'UTF-8') {\r
+                                                               $title = $this->_utf8_to_entities($title);\r
+                                                               $title = mb_convert_encoding($title, _CHARSET, 'UTF-8');\r
+                                                       }\r
+//modify end+++++++++\r
+\r
+                                                       echo '<input type="checkbox" name="tb_url_'.$i.\r
+                                                                '" value="'.$url.'" id="tb_url_'.$i.\r
+                                                                '" /><label for="tb_url_'.$i.'" title="'.$url.'">'.$title.'</label><br />';\r
+                                                       \r
+                                                       $i++;\r
+                                               }\r
+                                       }\r
+                               }               \r
+                       ?>\r
+                               </p>\r
+                       <?php\r
+               }\r
+\r
+               /**\r
+                * Insert Javascript AutoDiscovery routines\r
+                */\r
+               function jsautodiscovery() \r
+               {\r
+                       global $CONF;\r
+               \r
+                       ?>\r
+                               <script type='text/javascript' src='<?php echo $this->getAdminURL(); ?>autodetect.php'></script>        \r
+                       <?php\r
+               }\r
+\r
+               /**\r
+                * Ping all URLs\r
+                */\r
+               function pingTrackback($data) {\r
+                       global $manager, $CONF;\r
+                       \r
+                       $ping_urls_count = 0;\r
+                       $ping_urls = array();\r
+                       $localflag = array();\r
+                       \r
+                       $ping_url = requestVar('trackback_ping_url');\r
+//modify start+++++++++\r
+/*\r
+                       if ($ping_url) {\r
+                               $ping_urls[0] = $ping_url;\r
+                               $ping_urls_count++;\r
+                       }\r
+*/\r
+                       if (trim($ping_url)) {\r
+                               $ping_urlsTemp = array();\r
+                               $ping_urlsTemp = preg_split("/[\s,]+/", trim($ping_url));\r
+                               for($i=0;$i<count($ping_urlsTemp);$i++){\r
+                                       $ping_urls[] = trim($ping_urlsTemp[$i]);\r
+                                       $ping_urls_count++;\r
+                               }\r
+                       }\r
+//modify end+++++++++\r
+       \r
+                       $tb_url_amount = requestVar('tb_url_amount');\r
+                       for ($i=0;$i<$tb_url_amount;$i++) {\r
+                               $tb_temp_url = requestVar('tb_url_'.$i);\r
+                               if ($tb_temp_url) {\r
+                                       $ping_urls[$ping_urls_count] = $tb_temp_url;\r
+                                       $localflag[$ping_urls_count] = (requestVar('tb_url_'.$i.'_local') == 'on')? 1: 0;\r
+                                       $ping_urls_count++;\r
+                               }\r
+                       }\r
+       \r
+                       if ($ping_urls_count <= 0) {\r
+                               return;\r
+                       }\r
+       \r
+                       $itemid = $data['itemid'];\r
+                       $item = &$manager->getItem($itemid, 0, 0);\r
+                       if (!$item) return; // don't ping for draft & future\r
+                       if ($item['draft']) return;   // don't ping on draft items\r
+       \r
+                       // gather some more information, needed to send the ping (blog name, etc)      \r
+                       $blog =& $manager->getBlog(getBlogIDFromItemID($itemid));\r
+                       $blog_name      = $blog->getName();\r
+\r
+                       $title      = $data['title'] != '' ? $data['title'] : $item['title'];\r
+                       $title          = strip_tags($title);\r
+\r
+                       $excerpt    = $data['body'] != '' ? $data['body'] : $item['body'];\r
+                       $excerpt        = strip_tags($excerpt);\r
+                       $excerpt    = $this->_cut_string($excerpt, 200);\r
+       \r
+/*\r
+                       $CONF['ItemURL'] = preg_replace('/\/$/', '', $blog->getURL());   \r
+                       $url = createItemLink($itemid);\r
+*/\r
+                       $url    = $this->_createItemLink($item['itemid'],$blog);        \r
+       \r
+                       // send the ping(s) (add errors to actionlog)\r
+                       for ($i=0; $i<count($ping_urls); $i++) {\r
+                               if( ! $localflag[$i] )\r
+                                       $res = $this->sendPing($itemid, $title, $url, $excerpt, $blog_name, $ping_urls[$i]);\r
+                               else\r
+                                       $res = $this->handleLocalPing($itemid, $title, $excerpt, $blog_name, $ping_urls[$i]);\r
+                               if ($res) ACTIONLOG::add(WARNING, 'TrackBack Error:' . $res . ' (' . $ping_urls[$i] . ')');\r
+                       }\r
+               }\r
+\r
+       \r
+       \r
+       \r
+       /**************************************************************************************\r
+        * AUTO-DISCOVERY\r
+                */\r
+\r
+               /*\r
+                * Auto-Discovery of TrackBack Ping URLs based on HTML story\r
+                */\r
+               function autoDiscovery($text) \r
+               {\r
+                       $links  = $this->getPermaLinksFromText($text);\r
+                       $result = array();\r
+       \r
+                       for ($i = 0; $i < count($links); $i++)\r
+                       {\r
+                               list ($url, $title) = $this->getURIfromLink($links[$i]);\r
+                               \r
+                               if ($url != '')\r
+                                       $result[$url] = $title;\r
+                       }\r
+                       \r
+                       return $result;\r
+               }\r
+               \r
+               /*\r
+                * Auto-Discovery of TrackBack Ping URLs based on single link\r
+                */\r
+               function getURIfromLink($link) \r
+               {\r
+                       \r
+                       // Check to see if the cache contains this link\r
+                       $res = sql_query('SELECT url, title FROM '.sql_table('plugin_tb_lookup').' WHERE link=\''.mysql_real_escape_string($link).'\'');\r
+\r
+                       if ($row = mysql_fetch_array($res)) \r
+                       {\r
+                               if ($row['title'] != '')\r
+                               {\r
+//modify start+++++++++\r
+                                       if (_CHARSET != 'UTF-8'){\r
+                                               $row['title'] = mb_convert_encoding($row['title'], 'UTF-8', _CHARSET);\r
+                                               $row['title'] = $this->_decode_entities($row['title']);\r
+                                       }\r
+//modify end+++++++++\r
+                                       return array (\r
+                                               $row['url'], $row['title']\r
+                                       );\r
+                               }\r
+                               else\r
+                               {\r
+                                       return array (\r
+                                               $row['url'], $row['url']\r
+                                       );\r
+                               }\r
+                       }\r
+                       \r
+                       // Retrieve RDF\r
+                       if (($rdf = $this->getRDFFromLink($link)) !== false) \r
+                       {\r
+                               // Get PING attribute\r
+                               if (($uri = $this->getAttributeFromRDF($rdf, 'trackback:ping')) !== false) \r
+                               {\r
+                                       // Get TITLE attribute\r
+                                       if (($title = $this->getAttributeFromRDF($rdf, 'dc:title')) !== false) \r
+                                       {\r
+                                               // Get CREATOR attribute\r
+                                               if (($author = $this->getAttributeFromRDF($rdf, 'dc:creator')) !== false) \r
+                                               {\r
+                                                       $title = $author. ": " . $title;\r
+                                               }\r
+       \r
+                                               $uri   = $this->_decode_entities($uri);\r
+//modify start+++++++++\r
+                                               if (_CHARSET != 'UTF-8')\r
+                                                       $convertedTitle = mb_convert_encoding($title, _CHARSET, 'UTF-8');\r
+                                               else\r
+                                                       $convertedTitle = $title;\r
+/*\r
+                                               // Store in cache\r
+                                               $res = sql_query("INSERT INTO ".sql_table('plugin_tb_lookup')." (link, url, title) VALUES ('".mysql_real_escape_string($link)."','".mysql_real_escape_string($uri)."','".mysql_real_escape_string($title)."')");\r
+*/\r
+                                               // Store in cache\r
+                                               $res = sql_query("INSERT INTO ".sql_table('plugin_tb_lookup')." (link, url, title) VALUES ('".mysql_real_escape_string($link)."','".mysql_real_escape_string($uri)."','".mysql_real_escape_string($convertedTitle)."')");\r
+//modify end+++++++++\r
+                                               $title = $this->_decode_entities($title);\r
+\r
+                                               return array (\r
+                                                       $uri, $title\r
+                                               );\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               $uri = html_entity_decode($uri, ENT_COMPAT);\r
+       \r
+                                               // Store in cache\r
+                                               $res = sql_query("INSERT INTO ".sql_table('plugin_tb_lookup')." (link, url, title) VALUES ('".mysql_real_escape_string($link)."','".mysql_real_escape_string($uri)."','')");\r
+       \r
+                                               return array (\r
+                                                       $uri, $uri\r
+                                               );\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       // Store in cache\r
+                       $res = sql_query("INSERT INTO ".sql_table('plugin_tb_lookup')." (link, url, title) VALUES ('".mysql_real_escape_string($link)."','','')");\r
+       \r
+                       return array ('', '');\r
+               }\r
+       \r
+               /*\r
+                * Detect links used in HTML code\r
+                */\r
+               function getPermaLinksFromText($text)\r
+               {\r
+                       $links = array();\r
+                       \r
+                       if (preg_match_all('/<a +([^>]+)>/i', $text, $array, PREG_SET_ORDER))\r
+                       {\r
+                               $count = count($array);\r
+                               for ($i = 0; $i < $count; $i++)\r
+                               {\r
+                                       if( preg_match('/https?:\/\/[-_.!~*\'()a-z0-9;\/?:@&=+$,%]+/i', $array[$i][1], $matches) )\r
+                                               $links[$matches[0]] = 1;\r
+                               }\r
+                       }\r
+                       \r
+                       return array_keys($links);\r
+               }\r
+       \r
+               /*\r
+                * Retrieve RDF code from external link\r
+                */\r
+               function getRDFFromLink($link) \r
+               {\r
+                       if ($content = $this->getContents($link))\r
+                       {\r
+                               preg_match_all('/(<rdf:RDF.*?<\/rdf:RDF>)/sm', $content, $rdfs, PREG_SET_ORDER);\r
+                               \r
+                               if (count($rdfs) > 1)\r
+                               {\r
+                                       for ($i = 0; $i < count($rdfs); $i++)\r
+                                       {\r
+                                               if (preg_match('|dc:identifier="'.preg_quote($link).'"|ms',$rdfs[$i][1])) \r
+                                               {\r
+                                                       return $rdfs[$i][1];\r
+                                               }\r
+                                       }\r
+                               }\r
+                               else\r
+                               {\r
+                                       // No need to check the identifier\r
+                                       return $rdfs[0][1];\r
+                               }\r
+                       }\r
+                       \r
+                       return false;\r
+               }\r
+       \r
+               /**\r
+                * Retrieve the contents of an external (X)HTML document\r
+                */\r
+               function getContents($link) {\r
+               \r
+                       // Use cURL extention if available\r
+                       if (function_exists("curl_init") && $this->useCurl == 2)\r
+                       {\r
+                               // Make HEAD request\r
+                               $ch = curl_init();\r
+                               @curl_setopt($ch, CURLOPT_URL, $link);\r
+                               @curl_setopt($ch, CURLOPT_HEADER, true);\r
+                               @curl_setopt($ch, CURLOPT_NOBODY, true);\r
+                               @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);\r
+                               @curl_setopt($ch, CURLOPT_MAXREDIRS, 5);\r
+                               @curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\r
+                               @curl_setopt($ch, CURLOPT_TIMEOUT, 20);\r
+                               @curl_setopt($ch, CURLOPT_USERAGENT, $this->userAgent);\r
+\r
+                               $headers = curl_exec($ch);\r
+                               curl_close($ch);\r
+                               \r
+                               // Check if the link points to a (X)HTML document\r
+                               if (preg_match('/Content-Type: (text\/html|application\/xhtml+xml)/i', $headers))\r
+                               {\r
+                                       return $this->retrieveUrl ($link);\r
+                               }\r
+                               \r
+                               return false;\r
+                       }\r
+                       else\r
+                       {\r
+                               return $this->retrieveUrl ($link);\r
+                       }\r
+               }\r
+       \r
+               /*\r
+                * Get a single attribute from RDF\r
+                */\r
+               function getAttributeFromRDF($rdf, $attribute)\r
+               {\r
+                       if (preg_match('/'.$attribute.'="([^"]+)"/', $rdf, $matches)) \r
+                       {\r
+                               return $matches[1];\r
+                       }\r
+                       \r
+                       return false;\r
+               }\r
+\r
+\r
+\r
+\r
+\r
+\r
+               /**************************************************************************************/\r
+               /* Internal helper functions for dealing with external file retrieval                 */\r
+       \r
+               function retrieveUrl ($url) {\r
+//mod by cles\r
+                       $ua = ini_set('user_agent', $this->userAgent);\r
+//mod by cles end\r
+                       if (function_exists('curl_init') && $this->useCurl > 0)\r
+                       {\r
+                               // Set options\r
+                               $ch = curl_init();\r
+                               @curl_setopt($ch, CURLOPT_URL, $url);\r
+                               @curl_setopt($ch, CURLOPT_HEADER, 1);\r
+                               @curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\r
+                               @curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1);\r
+                               @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);\r
+                               @curl_setopt($ch, CURLOPT_MAXREDIRS, 5);\r
+                               @curl_setopt($ch, CURLOPT_TIMEOUT, 20);\r
+                               @curl_setopt($ch, CURLOPT_USERAGENT, $this->userAgent);\r
+               \r
+                               // Retrieve response\r
+                               $raw  = curl_exec($ch);\r
+                               $info = curl_getinfo($ch);\r
+                       \r
+                               // Split into headers and contents\r
+                               $headers  = substr($raw, 0, $info['header_size']);\r
+                               $contents = substr($raw, $info['header_size']);\r
+\r
+                               curl_close($ch);\r
+                       }\r
+                       elseif ($fp = @fopen ($url, "rb"))\r
+                       {\r
+//mod by cles\r
+//                             $contents = fread($fp, 8192);\r
+                               $contents = '';\r
+                               while (!feof($fp)) {\r
+                                       $contents .= fread($fp, 8192);\r
+                               }\r
+//mod by cles end\r
+                               $headers  = '';\r
+                               \r
+                               fclose($fp);\r
+                       }               \r
+//mod by cles\r
+                       ini_set('user_agent', $ua);\r
+//mod by cles end\r
+                       \r
+                       // Next normalize the encoding to UTF8...\r
+                       $contents = $this->_convert_to_utf8_auto($contents, $headers);\r
+       \r
+                       return $contents;\r
+               }\r
+               \r
+\r
+               /**************************************************************************************/\r
+               /* Internal helper functions for dealing with encodings and entities                  */\r
+       \r
+               var $entities_default = array (\r
+                       '&quot;'                => '&#34;',             \r
+                       '&amp;'                 => '&#38;',             \r
+                       '&apos;'                => '&#39;',             \r
+                       '&lt;'                  => '&#60;',             \r
+                       '&gt;'                  => '&#62;',             \r
+               );\r
+       \r
+//modify start+++++++++\r
+               function _restore_to_utf8($contents)\r
+               {\r
+                       if (_CHARSET != 'UTF-8')\r
+                       {\r
+                               $contents = mb_convert_encoding($contents, 'UTF-8', _CHARSET);\r
+                       }\r
+                       $contents = $this->_decode_entities(strip_tags($contents));\r
+                       return $contents;\r
+               }\r
+//modify end+++++++++\r
+               function _detect_encoding($string)\r
+               {\r
+//modify start+++++++++\r
+                       if (function_exists('mb_convert_encoding')) {\r
+                               $encoding = (preg_match ("/;\s*charset=([^\n]+)/is", serverVar("CONTENT_TYPE"), $regs))? \r
+                                       strtoupper(trim($regs[1])):\r
+                                       '';\r
+\r
+                               if ( ($encoding !="") && ((mb_http_input("P") == "") || ( strtolower( ini_get("mbstring.http_input") ) == "pass")) ) {\r
+                                       return $encoding;\r
+                               } else { \r
+                                       $encoding = mb_detect_encoding($string, NP_TRACKBACK_ENCODING_DETECT_ORDER);\r
+                               }\r
+                               return ( $encoding ) ? $encoding : _CHARSET;\r
+                       }\r
+//modify end+++++++++\r
+                       if (!ereg("[\x80-\xFF]", $string) && !ereg("\x1B", $string))\r
+                       return 'US-ASCII';\r
+                       \r
+                       if (!ereg("[\x80-\xFF]", $string) && ereg("\x1B", $string))\r
+                       return 'ISO-2022-JP';\r
+                       \r
+                       if (preg_match("/^([\x01-\x7F]|[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF][\x80-\xBF])+$/", $string) == 1)\r
+                       return 'UTF-8';\r
+                       \r
+                       if (preg_match("/^([\x01-\x7F]|\x8E[\xA0-\xDF]|\x8F[xA1-\xFE][\xA1-\xFE]|[\xA1-\xFE][\xA1-\xFE])+$/", $string) == 1)\r
+                       return 'EUC-JP';\r
+                       \r
+                       if (preg_match("/^([\x01-\x7F]|[\xA0-\xDF]|[\x81-\xFC][\x40-\xFC])+$/", $string) == 1)\r
+                       return 'Shift_JIS';\r
+                       \r
+                       return 'ISO-8859-1';\r
+               }\r
+\r
+               function _convert_to_utf8($contents, $encoding)\r
+               {\r
+                       $done = false;\r
+                       \r
+//modify start+++++++++\r
+//                     if (!$done && function_exists('iconv'))  \r
+//                     {\r
+//                     \r
+//                             $result = @iconv($encoding, 'UTF-8//IGNORE', $contents);\r
+//     \r
+//                             if ($result) \r
+//                             {\r
+//                                     $contents = $result;\r
+//                                     $done = true;\r
+//                             }\r
+//                     }\r
+                       \r
+                       if(!$done && function_exists('mb_convert_encoding')) \r
+                       {\r
+                               \r
+                               if( function_exists('mb_substitute_character') ){\r
+                                       @mb_substitute_character('none');\r
+                               }\r
+                               $result = @mb_convert_encoding($contents, 'UTF-8', $encoding );\r
+       \r
+                               if ($result) \r
+                               {\r
+                                       $contents = $result;\r
+                                       $done = true;\r
+                               }\r
+                       }\r
+\r
+                       if (!$done && function_exists('iconv'))  \r
+                       {\r
+                       \r
+                               $result = @iconv($encoding, 'UTF-8//IGNORE', $contents);\r
+       \r
+                               if ($result) \r
+                               {\r
+                                       $contents = $result;\r
+                                       $done = true;\r
+                               }\r
+                       }\r
+//modify end+++++++++\r
+                       return $contents;\r
+               }\r
+               \r
+               function _convert_to_utf8_auto($contents, $headers = '')\r
+               {\r
+                       /* IN:  string in unknown encoding, headers received during transfer\r
+                        * OUT: string in UTF-8 encoding\r
+                        */\r
+       \r
+                       $str = substr($contents, 0, 4096);\r
+                       $len = strlen($str);\r
+                       $pos = 0;\r
+                       $out = '';\r
+                       \r
+                       while ($pos < $len)\r
+                       {\r
+                               $ord = ord($str[$pos]);\r
+                               \r
+                               if ($ord > 32 && $ord < 128)\r
+                                       $out .= $str[$pos];\r
+                                       \r
+                               $pos++;\r
+                       }\r
+       \r
+                       // Detection of encoding, check headers\r
+                       if (preg_match ("/;\s*charset=([^\n]+)/is", $headers, $regs))\r
+                               $encoding = strtoupper(trim($regs[1]));\r
+       \r
+                       // Then check meta inside document\r
+                       if (preg_match ("/;\s*charset=([^\"']+)/is", $out, $regs))\r
+                               $encoding = strtoupper(trim($regs[1]));\r
+                               \r
+                       // Then check xml declaration\r
+                       if (preg_match("/<\?xml.+encoding\s*=\s*[\"|']([^\"']+)[\"|']\s*\?>/i", $out, $regs))\r
+                               $encoding = strtoupper(trim($regs[1]));         \r
+       \r
+                       // Converts\r
+                       return $this->_convert_to_utf8($contents, $encoding);\r
+               }\r
+               \r
+               function _decode_entities($string)\r
+               {\r
+                       /* IN:  string in UTF-8 containing entities\r
+                        * OUT: string in UTF-8 without entities\r
+                        */\r
+                        \r
+                       /// Convert all hexadecimal entities to decimal entities\r
+                       $string = preg_replace('/&#[Xx]([0-9A-Fa-f]+);/e', "'&#'.hexdec('\\1').';'", $string);          \r
+                       \r
+                       global $_entities;\r
+                       // Deal with invalid cp1251 numeric entities    \r
+                       $string = strtr($string, $_entities['cp1251']);\r
+\r
+                       // Convert all named entities to numeric entities\r
+                       $string = strtr($string, $this->entities_default);\r
+                       $string = strtr($string, $_entities['named']);\r
+\r
+                       // Convert all numeric entities to UTF-8\r
+                       $string = preg_replace('/&#([0-9]+);/e', "'&#x'.dechex('\\1').';'", $string);\r
+                       $string = preg_replace('/&#[Xx]([0-9A-Fa-f]+);/e', "NP_TrackBack::_hex_to_utf8('\\1')", $string);               \r
+\r
+                       return $string;\r
+               }\r
+       \r
+               function _hex_to_utf8($s){\r
+                       return entity::_hex_to_utf8($s);\r
+               }               \r
+\r
+               function _utf8_to_entities($string)\r
+               {\r
+                       /* IN:  string in UTF-8 encoding\r
+                        * OUT: string consisting of only characters ranging from 0x00 to 0x7f, \r
+                        *      using numeric entities to represent the other characters \r
+                        */\r
+                        \r
+                       $len = strlen ($string);\r
+                       $pos = 0;\r
+                       $out = '';\r
+                               \r
+                       while ($pos < $len) \r
+                       {\r
+                               $ascii = ord (substr ($string, $pos, 1));\r
+                               \r
+                               if ($ascii >= 0xF0) \r
+                               {\r
+                                       $byte[1] = ord(substr ($string, $pos, 1)) - 0xF0;\r
+                                       $byte[2] = ord(substr ($string, $pos + 1, 1)) - 0x80;\r
+                                       $byte[3] = ord(substr ($string, $pos + 2, 1)) - 0x80;\r
+                                       $byte[4] = ord(substr ($string, $pos + 3, 1)) - 0x80;\r
+       \r
+                                       $char_code = ($byte[1] << 18) + ($byte[2] << 12) + ($byte[3] << 6) + $byte[4];\r
+                                       $pos += 4;\r
+                               }\r
+                               elseif (($ascii >= 0xE0) && ($ascii < 0xF0)) \r
+                               {\r
+                                       $byte[1] = ord(substr ($string, $pos, 1)) - 0xE0;\r
+                                       $byte[2] = ord(substr ($string, $pos + 1, 1)) - 0x80;\r
+                                       $byte[3] = ord(substr ($string, $pos + 2, 1)) - 0x80;\r
+       \r
+                                       $char_code = ($byte[1] << 12) + ($byte[2] << 6) + $byte[3];\r
+                                       $pos += 3;\r
+                               }\r
+                               elseif (($ascii >= 0xC0) && ($ascii < 0xE0)) \r
+                               {\r
+                                       $byte[1] = ord(substr ($string, $pos, 1)) - 0xC0;\r
+                                       $byte[2] = ord(substr ($string, $pos + 1, 1)) - 0x80;\r
+       \r
+                                       $char_code = ($byte[1] << 6) + $byte[2];\r
+                                       $pos += 2;\r
+                               }\r
+                               else \r
+                               {\r
+                                       $char_code = ord(substr ($string, $pos, 1));\r
+                                       $pos += 1;\r
+                               }\r
+       \r
+                               if ($char_code < 0x80)\r
+                                       $out .= chr($char_code);\r
+                               else\r
+                                       $out .=  '&#'. str_pad($char_code, 5, '0', STR_PAD_LEFT) . ';';\r
+                       }\r
+       \r
+                       return $out;    \r
+               }                       \r
+\r
+               function _utf8_to_javascript($string)\r
+               {\r
+                       /* IN:  string in UTF-8 encoding\r
+                        * OUT: string consisting of only characters ranging from 0x00 to 0x7f, \r
+                        *      using javascript escapes to represent the other characters \r
+                        */\r
+                        \r
+                       $len = strlen ($string);\r
+                       $pos = 0;\r
+                       $out = '';\r
+                               \r
+                       while ($pos < $len) \r
+                       {\r
+                               $ascii = ord (substr ($string, $pos, 1));\r
+                               \r
+                               if ($ascii >= 0xF0) \r
+                               {\r
+                                       $byte[1] = ord(substr ($string, $pos, 1)) - 0xF0;\r
+                                       $byte[2] = ord(substr ($string, $pos + 1, 1)) - 0x80;\r
+                                       $byte[3] = ord(substr ($string, $pos + 2, 1)) - 0x80;\r
+                                       $byte[4] = ord(substr ($string, $pos + 3, 1)) - 0x80;\r
+       \r
+                                       $char_code = ($byte[1] << 18) + ($byte[2] << 12) + ($byte[3] << 6) + $byte[4];\r
+                                       $pos += 4;\r
+                               }\r
+                               elseif (($ascii >= 0xE0) && ($ascii < 0xF0)) \r
+                               {\r
+                                       $byte[1] = ord(substr ($string, $pos, 1)) - 0xE0;\r
+                                       $byte[2] = ord(substr ($string, $pos + 1, 1)) - 0x80;\r
+                                       $byte[3] = ord(substr ($string, $pos + 2, 1)) - 0x80;\r
+       \r
+                                       $char_code = ($byte[1] << 12) + ($byte[2] << 6) + $byte[3];\r
+                                       $pos += 3;\r
+                               }\r
+                               elseif (($ascii >= 0xC0) && ($ascii < 0xE0)) \r
+                               {\r
+                                       $byte[1] = ord(substr ($string, $pos, 1)) - 0xC0;\r
+                                       $byte[2] = ord(substr ($string, $pos + 1, 1)) - 0x80;\r
+       \r
+                                       $char_code = ($byte[1] << 6) + $byte[2];\r
+                                       $pos += 2;\r
+                               }\r
+                               else \r
+                               {\r
+                                       $char_code = ord(substr ($string, $pos, 1));\r
+                                       $pos += 1;\r
+                               }\r
+       \r
+                               if ($char_code < 0x80)\r
+                                       $out .= chr($char_code);\r
+                               else\r
+                                       $out .=  '\\u'. str_pad(dechex($char_code), 4, '0', STR_PAD_LEFT);\r
+                       }\r
+       \r
+                       return $out;    \r
+               }                       \r
+/*             \r
+               function _cut_string($string, $dl = 0) {\r
+               \r
+                       $defaultLength = $dl > 0 ? $dl : $this->getOption('defaultLength');\r
+                       \r
+                       if ($defaultLength < 1)\r
+                               return $string;\r
+       \r
+                       $border    = 6;\r
+                       $count     = 0;\r
+                       $lastvalue = 0;\r
+       \r
+                       for ($i = 0; $i < strlen($string); $i++)\r
+                       {\r
+                               $value = ord($string[$i]);\r
+          \r
+                               if ($value > 127)\r
+                       {\r
+                               if ($value >= 192 && $value <= 223)\r
+                                       $i++;\r
+                               elseif ($value >= 224 && $value <= 239)\r
+                                       $i = $i + 2;\r
+                               elseif ($value >= 240 && $value <= 247)\r
+                                       $i = $i + 3;\r
+                                       \r
+                                       if ($lastvalue <= 223 && $value >= 223 && \r
+                                               $count >= $defaultLength - $border)\r
+                                       {\r
+                                               return substr($string, 0, $i) . '...';\r
+                                       }\r
+\r
+                                       // Chinese and Japanese characters are\r
+                                       // wider than Latin characters\r
+                                       if ($value >= 224)\r
+                                               $count++;\r
+                                       \r
+                       }\r
+                               elseif ($string[$i] == '/' || $string[$i] == '?' ||\r
+                                               $string[$i] == '-' || $string[$i] == ':' ||\r
+                                               $string[$i] == ',' || $string[$i] == ';')\r
+                               {\r
+                                       if ($count >= $defaultLength - $border)\r
+                                               return substr($string, 0, $i) . '...';\r
+                               }\r
+                               elseif ($string[$i] == ' ')\r
+                               {\r
+                                       if ($count >= $defaultLength - $border)\r
+                                               return substr($string, 0, $i) . '...';\r
+                               }\r
+                               \r
+                               if ($count == $defaultLength)\r
+                                       return substr($string, 0, $i + 1) . '...';\r
+      \r
+                               $lastvalue = $value;\r
+                               $count++;\r
+                       }\r
+\r
+                       return $string;\r
+               }\r
+*/\r
+\r
+function _cut_string($string, $dl = 0) {\r
+       $maxLength = $dl > 0 ? $dl : $this->getOption('defaultLength');\r
+       \r
+       if ($maxLength < 1)\r
+               return $string;\r
+       if (strlen($string) > $maxLength)\r
+               $string = mb_strimwidth($string, 0, $maxLength, '...', _CHARSET);\r
+\r
+       return $string;\r
+}\r
+\r
+function _strip_controlchar($string){\r
+       $string = preg_replace("/[\x01-\x08\x0b\x0c\x0e-\x1f\x7f]+/","",$string);\r
+       $string = str_replace("\0","",$string);\r
+       return $string;\r
+}\r
+\r
+//modify start+++++++++\r
+       function checkTableVersion(){\r
+                               $res = sql_query("SHOW FIELDS from ".sql_table('plugin_tb') );\r
+                               $fieldnames = array();\r
+                               while ($co = mysql_fetch_assoc($res)) {\r
+                                       if($co['Field'] == 'block') return true;\r
+                               }\r
+                               return false;\r
+       }\r
+//modify end+++++++++\r
+\r
+/*---------------------------------------------------------------------------------- */\r
+/*   LOCAL                                                                           */\r
+/*---------------------------------------------------------------------------------- */\r
+       /**\r
+         * Handle an incoming TrackBack ping and save the data in the database\r
+         */\r
+       function handleLocalPing($itemid, $title, $excerpt, $blog_name, $ping_url){\r
+               global $manager;\r
+               $ping_url = trim($ping_url);\r
+               \r
+               if( preg_match("/^.+tb_id=([0-9]+)$/",$ping_url,$idnum) ){\r
+                       $tb_id = intval($idnum[1]);\r
+               } elseif ( preg_match("/([0-9]+)\.trackback/",$ping_url,$idnum) ){\r
+                       $tb_id = intval($idnum[1]);\r
+               } elseif ( preg_match("/itemid=([0-9]+)/",$ping_url,$idnum) ){\r
+                       $tb_id = intval($idnum[1]);\r
+               }\r
+\r
+               if ((!$manager->existsItem($tb_id,0,0)) && ($this->getOption('CheckIDs') == 'yes'))\r
+                       return _ERROR_NOSUCHITEM . "[ $tb_id ]";\r
+                       \r
+               // save data in the DB\r
+               $query = 'INSERT INTO ' . sql_table('plugin_tb_lc') . " (tb_id, from_id) VALUES ('".intval($tb_id)."','".intval($itemid)."')";\r
+               $res = @sql_query($query);\r
+               if (!$res) \r
+                       return 'Could not save trackback data, possibly because of a double entry: ' . mysql_error();\r
+       }\r
+       \r
+       /**\r
+         * Show the list of TrackBack pings for a certain Trackback ID\r
+         */\r
+       function showLocalList($tb_id) {\r
+               global $CONF, $manager;\r
+               \r
+               // create SQL query\r
+               $query = 'SELECT t.from_id as from_id , i.ititle as ititle, i.ibody as ibody, i.itime as itime, i.iblog as iblog FROM '.sql_table('plugin_tb_lc').' as t, '.sql_table('item').' as i WHERE t.tb_id='.intval($tb_id) .' and i.inumber=t.from_id ORDER BY i.itime DESC';\r
+               $res = sql_query($query);\r
+               \r
+               $vars = array(\r
+                       'tburl' => $this->getTrackBackUrl($tb_id)\r
+               );\r
+\r
+               // when no TrackBack pings are found\r
+               if (!$res || mysql_num_rows($res) == 0) {\r
+                       echo TEMPLATE::fill($this->getOption('tplLocalEmpty'), $vars);\r
+                       return;\r
+               }\r
+               \r
+               // when TrackBack pings are found\r
+               echo TEMPLATE::fill($this->getOption('tplLocalHeader'), $vars);\r
+               \r
+               while ($o = mysql_fetch_object($res)) {\r
+                       $canDelete = $this->canDelete($tb_id);\r
+                       $data = array(\r
+                               'url' => createItemLink($o->from_id),\r
+                               'blogname' => htmlspecialchars(getBlogNameFromID($o->iblog)),\r
+                               'timestamp' => strftime('%Y-%m-%d',strtotime($o->itime)),\r
+                               'title' => htmlspecialchars($o->ititle),\r
+                               'excerpt' => htmlspecialchars(shorten(strip_tags($o->ibody),200,'...')),\r
+                               'delete' => $canDelete?'<a href="'. $manager->addTicketToUrl($CONF['ActionURL'].'?action=plugin&amp;name=TrackBack&amp;type=deletelc&amp;tb_id='.intval($tb_id).'&amp;from_id='.intval($o->from_id)).'">[delete]</a>':'',\r
+                               'tburl' => $this->getTrackBackUrl($tb_id),\r
+                               'commentcount'=> quickQuery('SELECT COUNT(*) as result FROM '.sql_table('comment').' WHERE citem=' . intval($o->from_id))\r
+                       );\r
+                       echo TEMPLATE::fill($this->getOption('tplLocalItem'), $data);\r
+               }\r
+               echo TEMPLATE::fill($this->getOption('tplLocalFooter'), $vars);\r
+       }\r
+       \r
+       /**\r
+         * Delete a TrackBack item, redirect to referer\r
+         */\r
+       function deleteLocal($tb_id, $from_id) {\r
+               if (!$this->canDelete($tb_id))\r
+                       return 'You\'re not allowed to delete this trackback item';\r
+               $query = 'DELETE FROM ' . sql_table('plugin_tb_lc') . " WHERE tb_id='" . intval($tb_id) . "' and from_id='" . intval($from_id) ."'";\r
+               sql_query($query);\r
+               return '';\r
+       }\r
+       \r
+       function canDelete($tb_id) {\r
+               global $member, $manager;\r
+               \r
+               if ( ! $member->isLoggedIn() ) return 0;\r
+               \r
+               $checkIDs = $this->getOption('CheckIDs');\r
+               $itemExists =& $manager->existsItem($tb_id,0,0);\r
+               \r
+               // if CheckIDs option is set, check if member canEdit($tb_id)\r
+               // if CheckIDs option is not set, and item exists, check if member canEdit($tb_id)\r
+               // if CheckIDs option is not set, and item does not exists, check if member isAdmin()\r
+               \r
+               if (($checkIDs == 'yes') || ($itemExists))\r
+                       return $member->canAlterItem($tb_id);\r
+               else\r
+                       return $member->isAdmin();\r
+       }\r
+\r
+               /**************************************************************************************/\r
+               /* Plugin API calls, for installation, configuration and setup                        */\r
+       \r
+               function getName()        {             return 'TrackBack';   }\r
+               function getAuthor()      {             return 'rakaz + nakahara21 + hsur'; }\r
+               function getURL()         {             return 'http://blog.cles.jp/np_cles/category/31/subcatid/3'; }\r
+               function getVersion()     {             return '2.0.3 jp13'; }\r
+               function getDescription() {             return '[$Revision: 1.311 $]<br />' . _TB_DESCRIPTION; }\r
+       \r
+//modify start+++++++++\r
+/*\r
+               function getTableList()   {             return array(sql_table("plugin_tb"), sql_table("plugin_tb_lookup")); }\r
+               function getEventList()   {             return array('QuickMenu','PostAddItem','AddItemFormExtras','EditItemFormExtras','PreUpdateItem','PrepareItemForEdit', 'BookmarkletExtraHead'); }\r
+*/\r
+               function getTableList()   {             return array(sql_table("plugin_tb"), sql_table("plugin_tb_lookup"), sql_table('plugin_tb_lc')); }\r
+\r
+               function getEventList()   {             return array('QuickMenu','PostAddItem','AddItemFormExtras','EditItemFormExtras','PreUpdateItem','PrepareItemForEdit', 'BookmarkletExtraHead', 'RetrieveTrackback', 'SendTrackback', 'InitSkinParse'); }\r
+//modify end+++++++++\r
+               function getMinNucleusVersion() {       return 330; }\r
+       \r
+               function supportsFeature($feature) {\r
+                       switch($feature) {\r
+                               case 'SqlTablePrefix':\r
+                                       return 1;\r
+//modify start+++++++++\r
+//                             case 'HelpPage':\r
+//                                     return 1;\r
+//modify end+++++++++\r
+                               default:\r
+                                       return 0;\r
+                       }\r
+               }\r
+\r
+       \r
+               function hasAdminArea() {                       return 1; }\r
+\r
+               function event_QuickMenu(&$data) {\r
+                       global $member, $nucleus, $blogid;\r
+                       \r
+                       // only show to admins\r
+                       if (!$member->isLoggedIn()) return;\r
+\r
+                       array_push(\r
+                               $data['options'],\r
+                               array(\r
+                                       'title' => 'Trackback',\r
+                                       'url' => $this->getAdminURL(),\r
+                                       'tooltip' => 'Manage your trackbacks'\r
+                               )\r
+                       );\r
+               }\r
+                       \r
+               function install() {\r
+                       $this->createOption('AcceptPing',  _TB_AcceptPing,'yesno','yes');\r
+                       $this->createOption('SendPings',   _TB_SendPings,'yesno','yes');\r
+                       $this->createOption('AutoXMLHttp', _TB_AutoXMLHttp, 'yesno', 'yes');\r
+                       $this->createOption('CheckIDs',    _TB_CheckIDs,'yesno','yes');\r
+\r
+                       $this->createOption('tplHeader',   _TB_tplHeader, 'textarea', _TB_tplHeader_VAL);\r
+                       $this->createOption('tplEmpty',    _TB_tplEmpty, 'textarea', _TB_tplEmpty_VAL);\r
+                       $this->createOption('tplItem',     _TB_tplItem, 'textarea', _TB_tplItem_VAL);\r
+                       $this->createOption('tplFooter',   _TB_tplFooter, 'textarea', _TB_tplFooter_VAL);\r
+//mod by cles\r
+                       $this->createOption('tplLocalHeader',   _TB_tplLocalHeader, 'textarea', _TB_tplLocalHeader_VAL);\r
+                       $this->createOption('tplLocalEmpty',       _TB_tplLocalEmpty, 'textarea', _TB_tplLocalEmpty_VAL);\r
+                       $this->createOption('tplLocalItem',        _TB_tplLocalItem, 'textarea', _TB_tplLocalItem_VAL);\r
+                       $this->createOption('tplLocalFooter',   _TB_tplLocalFooter, 'textarea', _TB_tplLocalFooter_VAL);\r
+//mod by cles end\r
+\r
+                       $this->createOption('tplTbNone',   _TB_tplTbNone, 'text', "No Trackbacks");\r
+                       $this->createOption('tplTbOne',    _TB_tplTbOne, 'text', "1 Trackback");\r
+                       $this->createOption('tplTbMore',   _TB_tplTbMore, 'text', "<%number%> Trackbacks");\r
+                       $this->createOption('dateFormat',  _TB_dateFormat, 'text', _TB_dateFormat_VAL);\r
+       \r
+                       $this->createOption('NotifyEmail', _TB_NotifyEmail,'text','');\r
+                       $this->createOption('DropTable',   _TB_DropTable,'yesno','no');\r
+//mod by cles\r
+                       $this->createOption('HideUrl',_TB_HideUrl,'yesno','yes');\r
+                       $this->createOption('ajaxEnabled',_TB_ajaxEnabled,'yesno','no');\r
+\r
+                       $this->createItemOption('ItemAcceptPing',_TB_ItemAcceptPing,'yesno','yes');\r
+                       $this->createItemOption('isAcceptW/OLink',_TB_isAcceptWOLink,'select','default', _TB_isAcceptWOLink_VAL);\r
+\r
+                       $this->createBlogOption('NotifyEmailBlog', _TB_NotifyEmailBlog,'text','');      \r
+                       $this->createBlogOption('isAcceptW/OLinkDef',_TB_isAcceptWOLinkDef,'select','block', _TB_isAcceptWOLinkDef_VAL);\r
+                       $this->createBlogOption('AllowTrackBack',_TB_AllowTrackBack,'yesno','yes');\r
+//mod by cles end\r
+\r
+                       /* Create tables */\r
+                       sql_query("\r
+                               CREATE TABLE IF NOT EXISTS \r
+                                       ".sql_table('plugin_tb')."\r
+                               (\r
+                                       `id`        INT(11)         NOT NULL       AUTO_INCREMENT,\r
+                                       `tb_id`     INT(11)         NOT NULL, \r
+                                       `url`       TEXT            NOT NULL, \r
+                                       `block`     TINYINT(4)      NOT NULL, \r
+                                       `spam`      TINYINT(4)      NOT NULL, \r
+                                       `link`      TINYINT(4)      NOT NULL, \r
+                                       `title`     TEXT,       \r
+                                       `excerpt`   TEXT, \r
+                                       `blog_name` TEXT, \r
+                                       `timestamp` DATETIME, \r
+                                       \r
+                                       PRIMARY KEY (`id`)\r
+                               )\r
+                       ");\r
+                                               \r
+                       sql_query("\r
+                               CREATE TABLE IF NOT EXISTS\r
+                                       ".sql_table('plugin_tb_lookup')."\r
+                               (\r
+                                       `link`      TEXT            NOT NULL, \r
+                                       `url`       TEXT            NOT NULL, \r
+                                       `title`     TEXT, \r
+                                       \r
+                                       PRIMARY KEY (`link` (100))\r
+                               )\r
+                       ");\r
+//modify start+++++++++\r
+                       @sql_query('ALTER TABLE `' . sql_table('plugin_tb') . '` ADD INDEX `tb_id_block_timestamp_idx` ( `tb_id`, `block`, `timestamp` DESC )');\r
+                       @sql_query('CREATE TABLE IF NOT EXISTS ' . sql_table('plugin_tb_lc'). ' (tb_id int(11) not null, from_id int(11) not null, PRIMARY KEY (tb_id,from_id))');\r
+//modify end+++++++++\r
+               }\r
+       \r
+               function uninstall() {\r
+                       if ($this->getOption('DropTable') == 'yes') {\r
+                               sql_query ('DROP TABLE '.sql_table('plugin_tb'));\r
+                               sql_query ('DROP TABLE '.sql_table('plugin_tb_lookup'));\r
+                               sql_query ("DROP table ".sql_table('plugin_tb_lc'));\r
+                       }\r
+               }\r
+\r
+               function init() {\r
+                       // include language file for this plugin \r
+                       $language = ereg_replace( '[\\|/]', '', getLanguageName()); \r
+                       if (file_exists($this->getDirectory().'language/'.$language.'.php')) \r
+                               include_once($this->getDirectory().'language/'.$language.'.php'); \r
+      else \r
+                               include_once($this->getDirectory().'language/'.'english.php'); \r
+                       $this->notificationMail = _TB_NORTIFICATION_MAIL_BODY;\r
+                       $this->notificationMailTitle = _TB_NORTIFICATION_MAIL_TITLE;\r
+                       \r
+                       $this->userAgent = 'NucleusCMS NP_TrackBack plugin ( '.$this->getVersion().' )';\r
+               }\r
+       }\r
diff --git a/NP_TrackBack/trunk/trackback/CHANGELOG.txt b/NP_TrackBack/trunk/trackback/CHANGELOG.txt
new file mode 100644 (file)
index 0000000..299c1d4
--- /dev/null
@@ -0,0 +1,123 @@
+       \r
+       \r
+       0.9.0   Initial version of Referer by Xiffy\r
+       0.9.1   Added the possibility to call pop, 10 to show the most populair pages\r
+                       Minor bugfixes for htmlspecialchars\r
+                       Added substr to trim the line. Internet Explorer makes long lines instead of braking them\r
+       0.9.2   Added timeoffset functionality to refWhen ...\r
+       0.9.3   Added three display options (needs reinstallation!)\r
+                       Bugfixes and another calling option; lastall\r
+                       Added "-" on each refer*\r
+       \r
+       ------------------------------------------------------------\r
+       \r
+       1.0     Initial release\r
+       1.1     Version that takes advantage of the new features in Nucleus v2.0\r
+                       (v1.55 users still need to addapt their templates)\r
+       1.2             -  Fix: Typo (cechkids)\r
+                       -  Fix: $CONF['ActionURL'] instead of $CONF['SiteURL'] . 'action.php'\r
+                       -  Fix: also works with php option 'short_open_tags' set to Off\r
+                       -  Added: manualpingform\r
+                       -  Requires Nucleus v2.0...\r
+                       -  Support for tableprefix (Nucleus versions > 2.0)\r
+       \r
+       1.3     Release by caw\r
+                       -  Removed: Table backwards compatibility code\r
+                       -  Added: Support for adding TrackBack when editing item\r
+                       -  Change: Table name changed from [nucleus_]plugin_tb to [nucleus_]plug_trackback\r
+\r
+       1.4     Release by TeRanEX \r
+                       (didn't wrote anything myself, only merged some modifications)\r
+                       -  Added: Table backwards compatibility code (was removed in 1.3 but I don't \r
+                          see any reason why)\r
+                       -  Change: Table name changed from [nucleus_]plug_trackback to [nucleus_]plugin_tb \r
+                          again (what was the reason for the change in 1.3?\r
+                       -  Added all fixes/mods/additions of thread http://forum.nucleuscms.org/viewtopic.php?t=3247\r
+                       -  Send a ping on edit item\r
+                       -  sendPing with POST instead of GET\r
+                       -  "Retrieving TrackBack Pings" Implementation \r
+                       -  "Auto-Discovery of TrackBack Ping URLs" Implementation \r
+                       -  automatically-detecting trackbackURL of permalink linked by item\r
+                       -  Change: the RDF output so that it looks the same as in the MT TrackBack Spec\r
+                          (see http://forum.nucleuscms.org/viewtopic.php?t=1974)\r
+       \r
+       1.5             Release by admun and TeRanEX\r
+                       -  Added: Trackback updates, sending the newest data\r
+                       -  Added: Autodiscovery to the bookmarklet\r
+                       -  Added: Autodiscovery to the pingform\r
+                       -  Fixed: Autodiscovery now looks also in the 'more'-part of an item\r
+                       -  Changed/fixed: autodiscovery when editing an item, now you can check a checkbox for\r
+                          every trackback that was discovered and you want to ping\r
+                       -  Added: License info\r
+                       -  Changed: The description of the plugin\r
+       \r
+       ------------------------------------------------------------\r
+       \r
+       2.0a    Release by Niels Leenheer (rakaz)\r
+                       -  Added: Caching of auto-detected trackback URLs in a database table\r
+                       -  Added: If the cURL extension is present a HEAD request is send first, to make\r
+                          sure we are dealing with a (X)HTML page and not some large binary file format.\r
+                       -  Added: The auto-detection of trackback URLs now happen in real time - as you type -\r
+                          thanks to client-side Javascript and the XmlHttpRequest object which requests\r
+                          the required data from the plugin.\r
+       \r
+       2.0b    -  Added spinning auto-detection indicator\r
+                   -  Added support for multiple character encoding methods. The plugin\r
+                          works internally fully in UTF-8 (Unicode) and can convert other\r
+                          character encodings. The output of the plugin is in UTF-8 or in US-ASCII\r
+                          with unicode characters encoded using numeric entities.\r
+                       -  Added spam protection using the Blacklist plugin (thanks to Xiffy for\r
+                          helping me out by adding a generic spam check API to his plugin).\r
+                       -  Added a check to see if the page which send the trackback actually\r
+                          contains a link to our server. If not, then it is probably a spamming\r
+                          attempt and block by default.\r
+                       -  The output of this plugin is now fully configurable. You can specify\r
+                          you're own (X)HTML code.\r
+                       -  Added a admin interface which can be used to manage trackbacks and \r
+                          manually send trackbacks to other sites. It is possible to delete \r
+                          trackbacks, but also to block and unblock trackbacks. All trackbacks\r
+                          which are marked as spam are not deleted automatically, but they end\r
+                          up in a list called 'Blocked trackbacks'. You can manually verify this\r
+                          list and unblock any trackback which is marked as spam by mistake.\r
+                       -  Fixed a number of bugs, including missing hostnames and double // in\r
+                          URLs. Fixed a bug introduced in 2.0a which prevented the title and \r
+                          excerpt from showing up when sending trackbacks from a newly created\r
+                          story. Also filtering of tags is more stringent.\r
+                          \r
+                       -  REMOVED: Manual ping forms. The form which is need to ping other\r
+                          weblogs is now integrated into the admin interface. The form needed\r
+                          for other weblog authors to manually add trackbacks to your website\r
+                          will return in the next release.\r
+                       -  REMOVED: The ability to show a list of trackbacks in a popup window.\r
+                          This will probably return in the next release.\r
+                       -  REMOVED: The ability to delete trackbacks directly from the list\r
+                          shown to administrators. This is now handled by the admin interface.\r
+\r
+       2.0     final   \r
+                       -  Made the help page Nucleus 3.2 compatible\r
+                       -  Added a manual ping form, which allows weblog authors to add a trackback\r
+                          to your stories even when their software doesn't support trackbacks.\r
+                       -  Removed <language>en</language> from the RSS output, because we can't\r
+                          be sure about the language of the contents of the RSS stream.\r
+\r
+       2.0.1   -  Security fix: Plugin admin interface was exposed to all logged in users,\r
+                          not only to users with admin rights.\r
+\r
+       2.0.2   -  Added autodetection of the encoding of trackbacks, which is needed when\r
+                          a trackback is send in a foreign encoding by a sender which does not \r
+                          support version 1.2 of the trackback specifications. Supported encodings:\r
+                          US-ASCII, ISO-2022-JP, UTF-8, EUC-JP, Shift_JIS. If the encoding is not\r
+                          specified according to version 1.2 of the specs AND it is not one of the\r
+                          encodings specified above, the plugin will assume it is encoding using\r
+                          ISO-8859-1.\r
+                       -  Added two new events SendTrackback and RetrieveTrackback which can be\r
+                          used by other plugins or the XML-RPC APIs to allow external blog editors\r
+                          to send trackbacks. (This functionality does require a modification to\r
+                          the XML-RPC APIs).\r
+                       -  Added support for more clean Trackback URLs, for example:\r
+                          http://www.rakaz.nl/nucleus/item/84.trackback  or \r
+                          http://www.rakaz.nl/nucleus/item.php?id=84&format=trackback\r
+                          \r
+       2.0.3   -  Added support for a more advanced version of the SpamCheck API\r
+                          \r
+                          
\ No newline at end of file
diff --git a/NP_TrackBack/trunk/trackback/autodetect.php b/NP_TrackBack/trunk/trackback/autodetect.php
new file mode 100644 (file)
index 0000000..a3f107d
--- /dev/null
@@ -0,0 +1,259 @@
+<?php
+       $strRel = '../../../'; 
+       include($strRel . 'config.php');
+       
+       global $manager, $CONF;
+       $action = $manager->addTicketToUrl($CONF['ActionURL'] . '?action=plugin&name=TrackBack&type=detect')    
+?>
+       var xmlhttp = false;
+       var inProgress = false;
+       
+       var TrackbackAction = "<?php echo $action; ?>";
+       var TrackbackSource = new Array;
+       var TrackbackName   = new Array;
+       var TrackbackURL    = new Array;
+       
+       var LookupTable     = new Array;
+       var Lookup                      = '';
+       var countTotal                  = 0;
+       
+       var regexp = /href\s*=\s*([\"\'])(http:[^\"\'>]+)([\"\'])/ig;
+               
+               
+       function tbParseLinks ()
+       {
+               oinputbody = document.getElementById('inputbody');
+               oinputmore = document.getElementById('inputmore');
+               full = oinputbody.value + ' ' + oinputmore.value;
+
+               while (vArray = regexp.exec(full)) 
+               {
+                       unused = true;
+                       
+                       if (Lookup == vArray[2])
+                               unused = false;
+
+                       for (var i = 0; i < LookupTable.length; i++)
+                               if (LookupTable[i] == vArray[2])
+                                       unused = false;
+
+                       for (var i = 0; i < TrackbackSource.length; i++) 
+                               if (TrackbackSource[i] == vArray[2])
+                                       unused = false;
+                       
+                       if (unused == true)
+                               LookupTable.push(vArray[2]);
+               }
+       }
+       
+       function tbAutoDetect()
+       {
+               if (LookupTable.length > 0)
+               {
+                       tbBusy(true);
+
+                       if (!inProgress)
+                       {
+                               // We have something to do and the connection is free
+                               Lookup = LookupTable.shift();
+                               inProgress = true;
+       
+                               // The reason we use GET instead of POST is because
+                               // Opera does not properly support setting headers yet,
+                               // which is a requirement for using POST.
+                               xmlhttp.open("GET", TrackbackAction + "&tb_link=" + escape(Lookup), true);
+                               xmlhttp.onreadystatechange = tbStateChange;
+                               xmlhttp.send('');
+                       }
+                       else
+                       {
+                               // Still busy... simply wait until next turn
+                       }
+               }
+               else
+               {
+                       // Nothing to do, check back later...
+                       if (Lookup == '')
+                       {
+                               tbBusy(false);
+                       }
+               }
+       }
+
+       function tbStateChange ()
+       {
+               if (inProgress == true && xmlhttp.readyState == 4 && xmlhttp.status == 200) 
+               {
+                       eval (xmlhttp.responseText);
+                       inProgress = false;
+                       Lookup = '';
+               }
+       }
+
+       function tbBusy(toggle)
+       {
+
+               if (toggle)
+               {
+                               document.forms[0].discoverit.style.color = "#888888";
+                               document.forms[0].discoverit.style.fontWeight="bold";
+                               document.forms[0].discoverit.value = "  Loading ....";
+               }
+               else
+               {
+                               document.forms[0].discoverit.style.color = "#888888";
+                               document.forms[0].discoverit.style.fontWeight="bold";
+                               document.forms[0].discoverit.value = "  D o n e !  ";
+               }
+
+               o = document.getElementById('tb_busy');
+               
+               if (o)
+               {
+                       if (toggle)
+                               o.style.display = '';
+                       else
+                               o.style.display = 'none'
+               }
+       }
+
+       function tbDone(source, url, name)
+       {
+               TrackbackSource.push(source);
+               TrackbackURL.push(url);
+               TrackbackName.push(name);
+                       
+//             var parent = document.getElementById('tb_auto');
+               var amount = document.getElementById('tb_url_amount');
+               var subtitle = document.getElementById('tb_auto_title');
+               var listtable = document.getElementById('tb_ping_list');
+
+               if (url != '')
+               {
+//                     count = parseInt(amount.value);
+                       count = countTotal;
+
+                       mycurrent_row=document.createElement("TR");
+
+                       checkbox = document.createElement("input");
+                       checkbox.type = 'checkbox';
+                       checkbox.name = "tb_url_" + count;
+                       checkbox.id = "tb_url_" + count;
+                       checkbox.value = url;
+                       checkbox.defaultChecked = true;
+
+                       label = document.createElement("label"); 
+                       label.htmlFor = "tb_url_" + count;
+                       label.title = source;
+                       
+                       text = document.createTextNode(name);
+                       label.appendChild(text);
+                       
+                       
+//                     br = document.createElement("br"); 
+
+//                     subtitle.innerHTML = "Auto Discovered Ping URL's:";
+//                     parent.appendChild(checkbox);
+//                     parent.appendChild(label);
+
+                       mycurrent_cell=document.createElement("TD");
+                       mycurrent_cell.appendChild(checkbox);
+                       mycurrent_row.appendChild(mycurrent_cell);
+                       mycurrent_cell=document.createElement("TD");
+                       mycurrent_cell.appendChild(label);
+                       mycurrent_row.appendChild(mycurrent_cell);
+                       
+                       mycurrent_row.appendChild(mycurrent_cell);
+
+
+                       if(url.indexOf("<?php echo $CONF['IndexURL'];?>",0) != -1)
+                       {
+                               //local?
+                               checkboxL = document.createElement("input");
+                               checkboxL.type = 'checkbox';
+                               checkboxL.name = "tb_url_" + count + "_local";
+                               checkboxL.id = "tb_url_" + count + "_local";
+                               checkboxL.defaultChecked = true;
+
+                               labelL =        document.createElement("label"); 
+                               labelL.htmlFor = "tb_url_" + count + "_local";
+                               labelL.title = "local?";
+
+                               text = document.createTextNode("local?");
+                               labelL.appendChild(text);
+//                             parent.appendChild(checkboxL);
+//                             parent.appendChild(labelL);
+                               mycurrent_cell=document.createElement("TD");
+                               mycurrent_cell.appendChild(checkboxL);
+                               mycurrent_cell.appendChild(labelL);
+                               mycurrent_row.appendChild(mycurrent_cell);
+
+                       }
+                       else
+                       {
+                               mycurrent_cell=document.createElement("TD");
+                               mycurrent_row.appendChild(mycurrent_cell);
+                       }
+//                     parent.appendChild(br);
+                       listtable.appendChild(mycurrent_row);
+
+//                     amount.value = count + 1;
+                       countTotal++;
+                       amount.value = countTotal;
+               }
+               else
+               {
+                       subtitle.innerHTML = "No Trackbak URLs.";
+               }
+       }
+
+       function tbSetup() 
+       {
+               try 
+               {
+                       xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
+               } 
+               catch (e) 
+               {
+                       try 
+                       {
+                               xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
+                       } 
+                       catch (e) 
+                       {
+                               xmlhttp = false;
+                       }
+               }
+
+               if (!xmlhttp && typeof XMLHttpRequest!='undefined') 
+               {
+                       xmlhttp = new XMLHttpRequest();
+               }
+               
+               setInterval ('tbParseLinks();', 500);
+               setInterval ('tbAutoDetect();', 500);
+               
+               if (window.onloadtrackback)
+                       window.onloadtrackback();                               
+       }
+
+       function AddStart()
+       {
+               var strString = "";
+               strString = document.forms[0].trackback_ping_url.value;
+               strArray = strString.split("\n");
+                               for (var i = 0; i < strArray.length; i++)
+                               {
+                                       strTemp = trim(strArray[i]);
+                                       if (strTemp != "" && strTemp.match(/^http/))
+                                       {
+                                               tbDone(strTemp,strTemp,strTemp);
+                                       }
+                               }
+               document.forms[0].trackback_ping_url.value = '';
+       }
+
+       function trim(string) 
+       { 
+               return string.replace(/(^\s*)|(\s*$)/g,''); 
+       }
diff --git a/NP_TrackBack/trunk/trackback/detectlist.php b/NP_TrackBack/trunk/trackback/detectlist.php
new file mode 100644 (file)
index 0000000..c3affe6
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+       $strRel = '../../../'; 
+       include($strRel . 'config.php');
+
+?>
+       var TrackbackAction = "<?php echo $CONF['ActionURL'];?>";
+       var tb_id = "<?php echo intRequestVar('tb_id')?>";
+       var tb_amount = "<?php echo intRequestVar('amount')?>";
+       var xmlhttp = false;
+
+       function resttbStart() 
+       {
+               document.getElementById("tbhidenavi").style.display = "block";
+               document.getElementById("tbshownavi").style.display = "none";
+               try 
+               {
+                       xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
+               } 
+               catch (e) 
+               {
+                       try 
+                       {
+                               xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
+                       } 
+                       catch (e) 
+                       {
+                               xmlhttp = false;
+                       }
+               }
+
+               if (!xmlhttp && typeof XMLHttpRequest!='undefined') 
+               {
+                       xmlhttp = new XMLHttpRequest();
+               }
+               
+               if (xmlhttp)
+               {
+                       loadXMLDoc();
+               }
+               
+       }
+
+       function loadXMLDoc()
+       {
+
+               var url =  TrackbackAction + '?action=plugin&name=TrackBack&type=left&tb_id=' + tb_id + '&amount=' + tb_amount;
+               
+               xmlhttp.onreadystatechange=xmlhttpChange
+               xmlhttp.open("GET",url,true)
+               xmlhttp.send('')
+       }
+
+       function xmlhttpChange()
+       {
+               if (xmlhttp.readyState == 4 && xmlhttp.status == 200) 
+               {
+                       var result = document.getElementById("resttb");
+                       result.innerHTML = xmlhttp.responseText;
+               }
+       }
+
+       function hideresttb()
+       {
+               var result = document.getElementById("resttb");
+               result.innerHTML = "";
+               
+               document.getElementById("tbhidenavi").style.display = "none";
+               document.getElementById("tbshownavi").style.display = "block";
+               
+       }
diff --git a/NP_TrackBack/trunk/trackback/grid.php b/NP_TrackBack/trunk/trackback/grid.php
new file mode 100644 (file)
index 0000000..5b3ed15
--- /dev/null
@@ -0,0 +1,238 @@
+<?php
+
+       $strRel = '../../../'; 
+       include($strRel . 'config.php');
+       include($DIR_LIBS . 'PLUGINADMIN.php');
+       include('template.php');
+
+       // Send out Content-type
+       header('Pragma: no-cache');     
+       header("Content-Type: text/xml");
+       sendContentType('text/xml', 'admin-trackback', _CHARSET);       
+
+       $oPluginAdmin = new PluginAdmin('TrackBack');
+
+       if ( ! $member->isLoggedIn() )
+       {
+               $oPluginAdmin->start();
+               echo '<p>' . _ERROR_DISALLOWED . '</p>';
+               $oPluginAdmin->end();
+               exit;
+       }
+       
+       // Actions
+       $action = requestVar('action');
+       $aActionsNotToCheck = array(
+               '',
+       );
+       if (!in_array($action, $aActionsNotToCheck)) {
+               if (!$manager->checkTicket()) doError(_ERROR_BADTICKET);
+       }
+       
+//modify start+++++++++
+               $plug =& $oPluginAdmin->plugin;
+               $tableVersion = $plug->checkTableVersion();
+
+               // include language file for this plugin 
+               $language = ereg_replace( '[\\|/]', '', getLanguageName()); 
+               if (file_exists($plug->getDirectory().'language/'.$language.'.php')) 
+                       include_once($plug->getDirectory().'language/'.$language.'.php'); 
+               else 
+                       include_once($plug->getDirectory().'language/'.'english.php');
+//modify end+++++++++
+
+       $oTemplate = new Trackback_Template();
+       $oTemplate->set ('CONF', $CONF);
+       $oTemplate->set ('plugindirurl', $oPluginAdmin->plugin->getAdminURL());
+       $oTemplate->set ('ticket', $manager->_generateTicket());
+               
+       $whereClause = '';
+       if( ! $member->isAdmin() ){
+               // where clause
+               $res = sql_query('SELECT tblog FROM '.sql_table('team').' WHERE tadmin = 1 AND tmember = '.$member->getID() );
+               $adminBlog = array();
+               while ($row = mysql_fetch_array($res)){
+                       $adminBlog[] = $row[0];
+               }
+               if($adminBlog)
+                       $whereClause =  ' i.iblog in (' . implode(', ', $adminBlog) . ') ';
+                       
+               if( $whereClause )
+                       $whereClause = ' AND ( i.iauthor = '.$member->getID().' OR ' . $whereClause . ' )';
+               else
+                       $whereClause = ' AND i.iauthor = '.$member->getID();
+       }
+                       
+       $requiredItemEditRights = array(
+               'dodelete',
+               'doblock',
+               'dounblock',
+       );
+       $safeids = array();
+       if (in_array($action, $requiredItemEditRights)) {
+               $ids = explode(',', requestVar('ids'));
+               $safeids = array();
+               foreach( $ids as $id ){
+                       $id = trim($id);
+                       if( is_numeric($id) )
+                               $safeids[] = $id;
+               }       
+               if( ! $member->isAdmin() ){
+                       $query = 'SELECT t.id  FROM ' . sql_table('plugin_tb') . ' t, ' . sql_table('item') . ' i WHERE t.tb_id = i.inumber AND t.id in ( '. implode(',', $safeids) . ' ) '. $whereClause ;
+                       $res = sql_query($query);
+                       $safeids = array();
+                       while ($row = mysql_fetch_array($res)){
+                               $safeids[] = $row[0];
+                       }
+               }
+       }
+       
+       // Pages 
+       switch($action) {
+               
+               case 'ajax':
+                       $type = requestVar('type') == 'all' ? 'all' : 'blocked' ;
+                       $filter['all'] = ' t.block = 0 ';
+                       $filter['blocked'] = ' t.block = 1 ';
+
+                       $start  = intRequestVar('offset') ? intRequestVar('offset') : 0;
+                       $amount = intRequestVar('page_size') ? intRequestVar('page_size') : 25;
+
+                       $colname = array();
+                       $colname['date'] = 'timestamp';
+                       $colname['item'] = 'story_id';
+                       $colname['title'] = 'title';
+                       
+                       $sort_col = requestVar('sort_col');
+                       $sort_col = $colname[$sort_col];
+                       if( !$sort_col ) $sort_col = $colname['date'];
+
+                       $sort_dir = ( requestVar('sort_dir') == 'ASC' ) ? 'ASC' : 'DESC';
+
+                       $rres = sql_query ("
+                       SELECT
+                       count(*) as count
+                       FROM
+                       ".sql_table('plugin_tb')." AS t,
+                       ".sql_table('item')." AS i
+                       WHERE
+                       t.tb_id = i.inumber AND
+                       ".$filter[$type].$whereClause);
+                       $rrow = mysql_fetch_array($rres);
+                       $count = $rrow['count'];
+                       
+                       $rres = sql_query ("
+                       SELECT
+                       i.ititle AS story,
+                       i.inumber AS story_id,
+                       t.id AS id,
+                       t.title AS title,
+                       t.blog_name AS blog_name,
+                       t.excerpt AS excerpt,
+                       t.url AS url,
+                       t.spam AS spam,
+                       UNIX_TIMESTAMP(t.timestamp) AS timestamp
+                       FROM
+                       ".sql_table('plugin_tb')." AS t,
+                       ".sql_table('item')." AS i
+                       WHERE
+                       t.tb_id = i.inumber AND
+                       ".$filter[$type].$whereClause."
+                       ORDER BY
+                       ".$sort_col." ".$sort_dir." 
+                       LIMIT
+                       ".$start.",".$amount."
+                       ");
+                       
+                       $items = array();
+                       
+                       while ($rrow = mysql_fetch_array($rres))
+                       {
+                               $rrow['title']          = $oPluginAdmin->plugin->_cut_string($rrow['title'], 50);
+                               $rrow['title']          = $oPluginAdmin->plugin->_strip_controlchar($rrow['title']);
+                               $rrow['title']          = htmlspecialchars($rrow['title']);
+                               $rrow['title']          = preg_replace("/-+/","-",$rrow['title']);
+                               
+                               $rrow['blog_name']      = $oPluginAdmin->plugin->_cut_string($rrow['blog_name'], 50);
+                               $rrow['blog_name']      = $oPluginAdmin->plugin->_strip_controlchar($rrow['blog_name']);
+                               $rrow['blog_name']      = htmlspecialchars($rrow['blog_name']);
+                               $rrow['blog_name']              = preg_replace("/-+/","-",$rrow['blog_name']);
+                               
+                               $rrow['excerpt']        = $oPluginAdmin->plugin->_cut_string($rrow['excerpt'], 100);
+                               $rrow['excerpt']        = $oPluginAdmin->plugin->_strip_controlchar($rrow['excerpt']);
+                               $rrow['excerpt']        = htmlspecialchars($rrow['excerpt']);
+                               $rrow['excerpt']                = preg_replace("/-+/","-",$rrow['excerpt']);
+                               
+                               $rrow['url']            = htmlspecialchars($rrow['url'], ENT_QUOTES);
+                               
+                               $blog = & $manager->getBlog(getBlogIDFromItemID($rrow['story_id']));
+                               $rrow['story_url'] = $oPluginAdmin->plugin->_createItemLink($rrow['story_id'], $blog);
+                               $rrow['story'] = htmlspecialchars(strip_tags($rrow['story']), ENT_QUOTES);
+                               
+                               $items[] = $rrow;
+                       }
+                       
+                       $oTemplate->set ('amount', $amount);
+                       $oTemplate->set ('count', $count);
+                       $oTemplate->set ('start', $start);
+                       $oTemplate->set ('items', $items);
+                       $oTemplate->template('templates/response_'.$type.'.xml');                       
+                       break;
+                       
+               case 'dodelete':
+                       if( count($safeids) > 0 ){              
+                               $safeids = implode(',',$safeids);
+                               
+                               $res = sql_query(
+                                               ' DELETE FROM '
+                                               . sql_table('plugin_tb')
+                                               . ' WHERE id in (' . $safeids. ')'
+                               );
+                               $oTemplate->set ('message', $safeids . ' deleted.');
+                       } else {
+                               $oTemplate->set ('message', 'no rows deleted.');
+                       }
+                       
+                       $oTemplate->template('templates/response_dodelete.xml');
+                       break;
+                       
+               case 'doblock':
+                       if( count($safeids) > 0 ){              
+                               $safeids = implode(',',$safeids);
+                               
+                               $res = sql_query(
+                                               ' UPDATE '
+                                               . sql_table('plugin_tb')
+                                               .' SET block = 1 '
+                                               . ' WHERE id in (' . $safeids. ')'
+                               );
+                               $oTemplate->set ('message', $safeids . ' blocked.');
+                       } else {
+                               $oTemplate->set ('message', 'no rows blocked.');
+                       }
+                       
+                       $oTemplate->template('templates/response_doblock.xml');
+                       break;
+                                               
+               case 'dounblock':
+                       if( count($safeids) > 0 ){              
+                               $safeids = implode(',',$safeids);
+                               
+                               $res = sql_query(
+                                               ' UPDATE '
+                                               . sql_table('plugin_tb')
+                                               .' SET block = 0 '
+                                               . ' WHERE id in (' . $safeids. ')'
+                               );
+                               $oTemplate->set ('message', $safeids . ' unblocked.');
+                       } else {
+                               $oTemplate->set ('message', 'no rows unblocked.');
+                       }
+                       
+                       $oTemplate->template('templates/response_dounblock.xml');
+                       break;
+       }
+
+       // Create the admin area page
+       echo $oTemplate->fetch();
+       
diff --git a/NP_TrackBack/trunk/trackback/help.html b/NP_TrackBack/trunk/trackback/help.html
new file mode 100644 (file)
index 0000000..41a60ae
--- /dev/null
@@ -0,0 +1,111 @@
+<h2>Help</h2>
+
+
+<h3>Skin variables</h3>
+
+<p>
+       <code>&lt;%TrackBack(list)%&gt;</code>
+</p>
+<p>
+       Show a list of trackbacks for the current page. Optionally it is also possible to
+       add an additional parameter, the story id number, which makes it possible to
+       show a list of trackbacks from a different page. The output of this variable is
+       fully configurable in the plugin options.
+</p>
+
+<p>
+       <code>&lt;%TrackBack(code)%&gt;</code>
+</p>
+<p>
+       Inserts a small piece of invisible RDF code in the page, which is used by other
+       weblogs to auto-detect the trackback URL. If you do not include this variable in
+       your skin for item pages, other weblogs will not be able to auto-detect that your
+       weblog is able to accept trackbacks.
+</p>
+
+<p>
+       <code>&lt;%TrackBack(form)%&gt;</code>
+</p>
+<p>
+       Insert the URL to a manual ping form. Using this form authors of other weblogs
+       can add trackbacks to your stories, even when their software does not support
+       trackbacks, such as Blogger. A link to this form is included in the footer of
+       the trackback list by default. If you want to place this link somewhere else 
+       on your page, simply remove the <code>&lt;%form%&gt;</code> variable from the 
+       <em>Footer</em> field in the plugin settings and add the skinvar somewhere on
+       your webpage.
+</p>
+
+<p>
+       <code>&lt;%TrackBack(url)%&gt;</code>
+</p>
+<p>
+       Insert the Trackback Ping URL. A this URL is also included in the footer of
+       the trackback list by default. If you want to place this link somewhere else 
+       on your page, simply remove the <code>&lt;%action%&gt;</code> variable from the 
+       <em>Footer</em> field in the plugin settings and add the skinvar somewhere on
+       your webpage.
+</p>
+       
+<h3>Template variables</h3>
+
+<p>
+       All the skin variables documented above are also available as template variables.
+</p>
+
+<p>
+       <code>&lt;%TrackBack(count)%&gt;</code>
+</p>
+<p>
+       If you want to include an indication of how many trackbacks are present for each
+       page you can use this variable. By default this variable will show: "No Trackbacks", 
+       "1 Trackback", "2 Trackbacks", etc. This is however also fully configurable in
+       the plugin options.
+</p>
+
+
+<h3>Changing the output</h3>
+
+<p>
+       It is possible to fully change the output of this plugin. You can manually change
+       the XHTML code in the plugin options, but you can also use CSS to style the default
+       output to your liking. Below you will find an example of what a little snippet of
+       CSS can do.
+</p>
+
+<pre>div.tb {
+       border: 1px solid #000; background: #FFF;
+}
+div.tb div.head {
+       padding: 4px;
+       background: #000; color: #FFF;
+       font-weight: bold; text-transform: lowercase; letter-spacing: 0.6em;
+}
+div.tb div.empty {
+       padding: 4px;
+       font-size: 95%;
+}
+div.tb div.item {
+       padding: 4px;
+}
+div.tb div.item div.name {
+       margin-bottom: 8px;
+       font-size: 120%; font-weight: bold;
+}
+div.tb div.item div.body {
+       font-size: 95%;
+}
+div.tb div.item div.body a {
+       font-weight: bold;
+}
+div.tb div.item div.date {
+       margin-bottom: 8px;
+       color: #888;
+       font-size: 85%; text-align: right;
+}
+div.tb div.info {
+       padding: 4px;
+       color: #FFF; background: #888;
+       font-size: 85%; font-style: italic;
+}</pre>
+
diff --git a/NP_TrackBack/trunk/trackback/index.php b/NP_TrackBack/trunk/trackback/index.php
new file mode 100644 (file)
index 0000000..ef09755
--- /dev/null
@@ -0,0 +1,496 @@
+<?php
+
+       $strRel = '../../../'; 
+       include($strRel . 'config.php');
+       include($DIR_LIBS . 'PLUGINADMIN.php');
+       include('template.php');
+       
+       
+       $oPluginAdmin = new PluginAdmin('TrackBack');
+
+       if ( !$member->isLoggedIn() )
+       {
+               $oPluginAdmin->start();
+               echo '<p>' . _ERROR_DISALLOWED . '</p>';
+               $oPluginAdmin->end();
+               exit;
+       }
+       
+       // Actions
+       $action = requestVar('action');
+       $aActionsNotToCheck = array(
+               '',
+               'ping',
+       );
+       if (!in_array($action, $aActionsNotToCheck)) {
+               if (!$manager->checkTicket()) doError(_ERROR_BADTICKET);
+       }
+
+       $oPluginAdmin->start();
+       
+//modify start+++++++++
+               $plug =& $oPluginAdmin->plugin;
+               $tableVersion = $plug->checkTableVersion();
+
+               // include language file for this plugin 
+               $language = ereg_replace( '[\\|/]', '', getLanguageName()); 
+               if (file_exists($plug->getDirectory().'language/'.$language.'.php')) 
+                       include_once($plug->getDirectory().'language/'.$language.'.php'); 
+               else 
+                       include_once($plug->getDirectory().'language/'.'english.php');
+//modify end+++++++++
+
+       $mTemplate = new Trackback_Template();
+       $mTemplate->set ('CONF', $CONF);
+       $mTemplate->set ('plugid', $plug->getID());
+       $mTemplate->set ('plugindirurl', $oPluginAdmin->plugin->getAdminURL());
+       $mTemplate->template('templates/menu.html');
+       echo $mTemplate->fetch();
+
+       $oTemplate = new Trackback_Template();
+       $oTemplate->set ('CONF', $CONF);
+       $oTemplate->set ('plugindirurl', $oPluginAdmin->plugin->getAdminURL());
+       $oTemplate->set ('ticket', $manager->_generateTicket());
+       $ajaxEnabled = ($oPluginAdmin->plugin->getOption('ajaxEnabled') == 'yes') ? true : false;
+       $oTemplate->set ('ajaxEnabled', $ajaxEnabled);
+       
+       $whereClause = '';
+       if( ! $member->isAdmin() ){
+               // where clause
+               $res = sql_query('SELECT tblog FROM '.sql_table('team').' WHERE tadmin = 1 AND tmember = '.$member->getID() );
+               $adminBlog = array();
+               while ($row = mysql_fetch_array($res)){
+                       $adminBlog[] = $row[0];
+               }
+               if($adminBlog)
+                       $whereClause =  ' i.iblog in (' . implode(', ', $adminBlog) . ') ';
+                       
+               if( $whereClause )
+                       $whereClause = ' AND ( i.iauthor = '.$member->getID().' OR ' . $whereClause . ' )';
+               else
+                       $whereClause = ' AND i.iauthor = '.$member->getID();
+       }
+       //echo "<p>Debug: $whereClause<p>";
+       
+       $requiredAdminRights = array(
+               'tableUpgrade',
+               'blocked_clear',
+               'blocked_spamclear',
+       );
+       if (in_array($action, $requiredAdminRights)) {
+               if( ! $member->isAdmin() ){
+                       echo '<p>' . _ERROR_DISALLOWED . '</p>';
+                       echo '<p>Reason: ' . __LINE__ . '</p>';
+                       $oPluginAdmin->end();
+                       exit;
+               }
+       }
+       
+       $requiredItemEditRights = array(
+               'block',
+               'unblock',
+               'delete',
+       );
+       if (in_array($action, $requiredItemEditRights)) {
+               if( ! $member->isAdmin() ){
+                       $tb = intRequestVar('tb');
+                       $query = 'SELECT i.inumber FROM ' . sql_table('plugin_tb') . ' t, ' . sql_table('item') . ' i WHERE t.tb_id = i.inumber AND t.id = '. $tb . $whereClause ;
+                       $res = sql_query($query);
+                       if( ! @mysql_num_rows($res) ){
+                               echo '<p>' . _ERROR_DISALLOWED . '</p>';
+                               echo '<p>Reason: ' . __LINE__ . '</p>';
+                               $oPluginAdmin->end();
+                               exit;
+                       }
+               }
+       }
+
+       switch($action) {
+
+//modify start+++++++++
+               case 'tableUpgrade':
+                       sql_query("
+                               CREATE TABLE IF NOT EXISTS
+                                       ".sql_table('plugin_tb_lookup')."
+                               (
+                                       `link`      TEXT            NOT NULL, 
+                                       `url`       TEXT            NOT NULL, 
+                                       `title`     TEXT, 
+                                       
+                                       PRIMARY KEY (`link` (100))
+                               )
+                       ");
+                       echo $q = "ALTER TABLE ".sql_table('plugin_tb')."
+                                ADD `block` TINYINT( 4 ) NOT NULL AFTER `url` ,
+                                ADD `spam` TINYINT( 4 ) NOT NULL AFTER `block` ,
+                                ADD `link` TINYINT( 4 ) NOT NULL AFTER `spam` ,
+                                CHANGE `url` `url` TEXT NOT NULL,
+                                CHANGE `title` `title` TEXT NOT NULL,
+                                CHANGE `excerpt` `excerpt` TEXT NOT NULL,
+                                CHANGE `blog_name` `blog_name` TEXT NOT NULL,
+                                DROP PRIMARY KEY,
+                                ADD `id` INT( 11 ) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST ;";
+                       $res = @sql_query($q);
+                       if (!$res){
+                               echo 'Could not alter table: ' . mysql_error();
+                       }else{
+                               $tableVersion = 1;
+                               $oTemplate->template('templates/updatetablefinished.html');
+                       }
+                       @sql_query('ALTER TABLE `' . sql_table('plugin_tb') . '` ADD INDEX `tb_id_block_timestamp_idx` ( `tb_id`, `block`, `timestamp` DESC )');
+                       break;
+//modify end+++++++++
+
+               case 'block':
+                       $tb = intRequestVar('tb');
+
+                       $res = sql_query ("
+                               UPDATE
+                                       ".sql_table('plugin_tb')."
+                               SET
+                                       block = 1
+                               WHERE
+                                       id = '".$tb."'
+                       ");
+
+                       $action = requestVar('next');
+                       break;
+                       
+               case 'blocked_clear':
+                       $res = sql_query ("DELETE FROM ".sql_table('plugin_tb')." WHERE block = 1");
+                       $action = requestVar('next');
+                       break;
+                       
+               case 'blocked_spamclear':
+                       $res = sql_query ("DELETE FROM ".sql_table('plugin_tb')." WHERE block = 1 and spam = 1");
+                       $action = requestVar('next');
+                       break;
+
+               case 'unblock':
+                       $tb = intRequestVar('tb');
+
+                       $res = sql_query ("
+                               UPDATE
+                                       ".sql_table('plugin_tb')."
+                               SET
+                                       block = 0
+                               WHERE
+                                       id = '".$tb."'
+                       ");
+
+                       $action = requestVar('next');
+                       break;
+
+               case 'delete':
+                       $tb = intRequestVar('tb');
+
+                       $res = sql_query ("
+                               DELETE FROM
+                                       ".sql_table('plugin_tb')."
+                               WHERE
+                                       id = '".$tb."'
+                       ");
+
+                       $action = requestVar('next');
+                       break;
+
+               case 'sendping':
+                       $title     = requestVar('title');
+                       $url       = requestVar('url');
+                       $excerpt   = requestVar('excerpt');
+                       $blog_name = requestVar('blog_name');
+                       $ping_url  = requestVar('ping_url');            
+
+                       // No charset conversion needs to be done here, because
+                       // the charset used to receive the info is used to send
+                       // it...
+
+                       if ($ping_url) {
+                               $error = $oPluginAdmin->plugin->sendPing(0, $title, $url, $excerpt, $blog_name, $ping_url);
+                               
+                               if ($error) {
+                                       echo '<b>TrackBack Error:' . $error . '</b>';
+                               }
+                       }               
+                       
+                       $action = requestVar('next');
+                       break;
+                       
+               case 'ping':
+                       $id  = intRequestVar('id');
+                       
+                       $usePathInfo = ($CONF['URLMode'] == 'pathinfo');
+                       if ($usePathInfo)
+                       @ include($strRel . 'fancyurls.config.php');
+                       
+                       global $manager;
+                       $itemData = $manager->getItem($id, 0, 0);
+                       
+                       if(is_array($itemData)){
+                               $blog =& $manager->getBlog($itemData['blogid']);
+                               $CONF['ItemURL'] = ($usePathInfo)? preg_replace('/\/$/', '', $blog->getURL()): $blog->getURL();
+                               $itemData['url'] = createItemLink($id);
+                               $itemData['excerpt'] = shorten(strip_tags($itemData['body'].$itemData['more']), 250, '...');
+                               $itemData['blogname'] = $blog->getName();
+                       }else{
+                               $itemData = array();
+                               $itemData['url'] = $CONF['IndexURL'];
+                               $itemData['blogname'] = $CONF['SiteName'];
+                       }
+                       $oTemplate->set('item', $itemData);
+                       
+                       $oTemplate->template('templates/ping.html');
+                       break;                  
+       }
+
+       // Pages 
+       switch($action) {
+               
+               case 'help':
+                       $oTemplate->template('help.html');                      
+                       break;
+
+               case 'ping':
+                       $oTemplate->template('templates/ping.html');                    
+                       break;
+
+               case 'blocked':
+               case 'all':     
+                       $rres = sql_query ("
+                               SELECT
+                                       COUNT(*) AS count
+                               FROM
+                                       ".sql_table('plugin_tb')." AS t,
+                                       ".sql_table('item')." AS i
+                               WHERE
+                                       t.tb_id = i.inumber AND
+                                       t.block = " . (( $action == 'all') ? 0 : 1) . $whereClause );                           
+                                               
+                       if ($row = mysql_fetch_array($rres))
+                               $count = $row['count'];
+                       else
+                               $count = 0;
+                       $oTemplate->set('count', $count);
+
+                       if($ajaxEnabled){
+                               if( $action == 'all') 
+                                       $oTemplate->template('templates/all_ajax.html');
+                               else                    
+                                       $oTemplate->template('templates/blocked_ajax.html');
+                       } else {
+                               $start  = intRequestVar('start') ? intRequestVar('start') : 0;
+                               $amount = intRequestVar('amount') ? intRequestVar('amount') : 25;
+
+                               $rres = sql_query ("
+                                       SELECT
+                                       i.ititle AS story,
+                                       i.inumber AS story_id,
+                                       t.id AS id,
+                                       t.title AS title,
+                                       t.blog_name AS blog_name,
+                                       t.excerpt AS excerpt,
+                                       t.url AS url,
+                                       UNIX_TIMESTAMP(t.timestamp) AS timestamp,
+                                       t.spam AS spam,
+                                       t.link AS link
+                                       FROM
+                                       ".sql_table('plugin_tb')." AS t,
+                                       ".sql_table('item')." AS i
+                                       WHERE
+                                       t.tb_id = i.inumber AND
+                                       t.block = " . (( $action == 'all') ? 0 : 1) . $whereClause ."
+                                       ORDER BY
+                                       timestamp DESC
+                                       LIMIT
+                                       ".$start.",".$amount);                          
+                               
+                               $items = array();
+                               
+                               while ($rrow = mysql_fetch_array($rres)){
+                                       $rrow['title']          = $oPluginAdmin->plugin->_cut_string($rrow['title'], 50);
+                                       $rrow['title']          = $oPluginAdmin->plugin->_strip_controlchar($rrow['title']);
+                                       $rrow['title']          = htmlspecialchars($rrow['title']);
+                                       
+                                       $rrow['blog_name']      = $oPluginAdmin->plugin->_cut_string($rrow['blog_name'], 50);
+                                       $rrow['blog_name']      = $oPluginAdmin->plugin->_strip_controlchar($rrow['blog_name']);
+                                       $rrow['blog_name']      = htmlspecialchars($rrow['blog_name']);
+                                       
+                                       $rrow['excerpt']        = $oPluginAdmin->plugin->_cut_string($rrow['excerpt'], 800);
+                                       $rrow['excerpt']        = $oPluginAdmin->plugin->_strip_controlchar($rrow['excerpt']);
+                                       $rrow['excerpt']        = htmlspecialchars($rrow['excerpt']);
+                                       
+                                       $rrow['url']            = htmlspecialchars($rrow['url'], ENT_QUOTES);
+                                       $rrow['timestamp']              = htmlspecialchars($rrow['timestamp'], ENT_QUOTES);
+                                       
+                                       $blog = & $manager->getBlog(getBlogIDFromItemID($item['itemid']));
+                                       $rrow['story_url'] = $oPluginAdmin->plugin->_createItemLink($rrow['story_id'], $blog);
+                                       $rrow['story'] = htmlspecialchars(strip_tags($rrow['story']), ENT_QUOTES);
+                                       
+                                       $items[] = $rrow;
+                               }
+                               
+                               $oTemplate->set('amount', $amount);
+                               $oTemplate->set('start', $start);
+                               $oTemplate->set('items', $items);
+                               
+                               if( $action == 'all') 
+                                       $oTemplate->template('templates/all.html');
+                               else                    
+                                       $oTemplate->template('templates/blocked.html');
+                       }
+                       break;
+                       
+               case 'list':
+                       $id     = requestVar('id');
+                       $start  = intRequestVar('start') ? intRequestVar('start') : 0;
+                       $amount = intRequestVar('amount') ? intRequestVar('amount') : 25;
+
+                       $ires = sql_query ("
+                               SELECT
+                                       i.ititle,
+                                       i.inumber
+                               FROM
+                                       ".sql_table('item')." i 
+                               WHERE
+                                       i.inumber = '".$id."'
+                       ". $whereClause );
+                       
+                       if ($irow = mysql_fetch_array($ires))
+                       {
+                               $story['id']    = $id;
+                               $story['title'] = $irow['ititle'];
+
+                               $rres = sql_query ("
+                                       SELECT
+                                               COUNT(*) AS count
+                                       FROM
+                                               ".sql_table('plugin_tb')." AS t
+                                       WHERE
+                                               t.tb_id = '".$id."' AND
+                                               t.block = 0
+                               ");                             
+                                                       
+                               if ($row = mysql_fetch_array($rres))
+                                       $count = $row['count'];
+                               else
+                                       $count = 0;
+                                       
+                               $rres = sql_query ("
+                                       SELECT
+                                               t.id AS id,
+                                               t.title AS title,
+                                               t.blog_name AS blog_name,
+                                               t.excerpt AS excerpt,
+                                               t.url AS url,
+                                       UNIX_TIMESTAMP(t.timestamp) AS timestamp
+                                       FROM
+                                               ".sql_table('plugin_tb')." AS t
+                                       WHERE
+                                               t.tb_id = '".$id."' AND
+                                               t.block = 0
+                                       ORDER BY
+                                               timestamp DESC
+                                       LIMIT
+                                               ".$start.",".$amount."
+                               ");                             
+                               
+                               $items = array();
+       
+                               while ($rrow = mysql_fetch_array($rres))
+                               {
+                                       $rrow['title']          = $oPluginAdmin->plugin->_cut_string($rrow['title'], 50);
+                                       $rrow['title']          = $oPluginAdmin->plugin->_strip_controlchar($rrow['title']);
+                                       $rrow['title']          = htmlspecialchars($rrow['title']);
+//                                     $rrow['title']          = _CHARSET == 'UTF-8' ? $rrow['title'] : $oPluginAdmin->plugin->_utf8_to_entities($rrow['title']);
+       
+                                       $rrow['blog_name']      = $oPluginAdmin->plugin->_cut_string($rrow['blog_name'], 50);
+                                       $rrow['blog_name']      = $oPluginAdmin->plugin->_strip_controlchar($rrow['blog_name']);
+                                       $rrow['blog_name']      = htmlspecialchars($rrow['blog_name']);
+//                                     $rrow['blog_name']      = _CHARSET == 'UTF-8' ? $rrow['blog_name'] : $oPluginAdmin->plugin->_utf8_to_entities($rrow['blog_name']);
+       
+                                       $rrow['excerpt']        = $oPluginAdmin->plugin->_cut_string($rrow['excerpt'], 800);
+                                       $rrow['excerpt']        = $oPluginAdmin->plugin->_strip_controlchar($rrow['excerpt']);
+                                       $rrow['excerpt']        = htmlspecialchars($rrow['excerpt']);
+//                                     $rrow['excerpt']        = _CHARSET == 'UTF-8' ? $rrow['excerpt'] : $oPluginAdmin->plugin->_utf8_to_entities($rrow['excerpt']);
+       
+                                       $rrow['url']            = htmlspecialchars($rrow['url'], ENT_QUOTES);
+                                       $rrow['story'] = htmlspecialchars(strip_tags($rrow['story']), ENT_QUOTES);
+                                       $items[] = $rrow;
+                               }
+                               
+                               $oTemplate->set ('amount', $amount);
+                               $oTemplate->set ('count', $count);
+                               $oTemplate->set ('start', $start);
+                               $oTemplate->set ('items', $items);
+                               $oTemplate->set ('story', $story);
+                               $oTemplate->template('templates/list.html');                    
+                       }
+                       
+                       break;
+                                                       
+               
+               case 'index':
+                       $bres = sql_query ("
+                               SELECT
+                                       bnumber AS bnumber,
+                                       bname AS bname,
+                                       burl AS burl
+                               FROM
+                                       ".sql_table('blog')."
+                               ORDER BY
+                                       bname
+                       ");
+                       
+                       $blogs = array();
+                       
+                       while ($brow = mysql_fetch_array($bres))
+                       {
+                               if( !$member->isTeamMember($brow['bnumber']) ) continue;
+                               $ires = sql_query ("
+                                       SELECT
+                                               i.inumber AS inumber,
+                                           i.ititle AS ititle,
+                                           COUNT(*) AS total
+                                       FROM
+                                               ".sql_table('item')." AS i,
+                                               ".sql_table('plugin_tb')." AS t
+                                       WHERE
+                                               i.iblog = ".$brow['bnumber']." AND
+                                               t.tb_id = i.inumber AND
+                                               t.block = 0 ".$whereClause." 
+                                       GROUP BY
+                                               i.inumber
+                    ORDER BY
+                       i.inumber DESC
+                               ");                             
+
+                               $items = array();
+
+                               while ($irow = mysql_fetch_array($ires))
+                               {
+                                       $items[] = $irow;
+                               }
+
+                               $brow['items'] = $items;
+                               $blogs[] = $brow;
+                       }
+
+                       $oTemplate->set ('blogs', $blogs);
+                       $oTemplate->template('templates/index.html');
+                       break;
+
+               default:
+                       //modify start+++++++++
+                       if(!$tableVersion){
+                               $oTemplate->template('templates/updatetable.html');
+                       }
+                       //modify end+++++++++
+                       break;
+       }
+
+       // Create the admin area page
+       echo $oTemplate->fetch();
+       
+       echo '<div align="right">Powered by <a href="http://www.famfamfam.com/lab/icons/silk/">Silk icon</a></div>';
+       $oPluginAdmin->end();   
+
diff --git a/NP_TrackBack/trunk/trackback/japanese-euc.help.html b/NP_TrackBack/trunk/trackback/japanese-euc.help.html
new file mode 100644 (file)
index 0000000..aa0b185
--- /dev/null
@@ -0,0 +1,116 @@
+<h2>¥Ø¥ë¥×</h2>\r
+\r
+\r
+<h3>¥¹¥­¥ó¤Ø¤Îµ­½ÒÎã</h3>\r
+<p>\r
+Nucleus CMS Japan Wiki¤Î<a href="http://japan.nucleuscms.org/wiki/plugins:trackback">NP_TrackBack¤Î¥Ú¡¼¥¸</a>¤ò»²¾È¤·¤Æ¤¯¤À¤µ¤¤¡£\r
+</p>\r
+\r
+<h3>ɽ¼¨¤Î¥«¥¹¥¿¥Þ¥¤¥º</h3>\r
+<p>\r
+       ¥È¥é¥Ã¥¯¥Ð¥Ã¥¯´ØÏ¢¤Îɽ¼¨ÊýË¡¤Ï¥Æ¥ó¥×¥ì¡¼¥È¤òÊÔ½¸¤¹¤ë¤³¤È¤Ë¤è¤Ã¤Æ¥«¥¹¥¿¥Þ¥¤¥º¤Ç¤­¤Þ¤¹¤¬¡¢¿§¤ä;Çò¤Ê¤É¤Î¥Ç¥¶¥¤¥ó¤ÏCSS¦¤Ç»ØÄꤹ¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£¥Æ¥ó¥×¥ì¡¼¥ÈÆâ¤ËÆþÎϤ·¤¿¥¯¥é¥¹Ì¾¤Î¥×¥í¥Ñ¥Æ¥£¤Ïcss¤Ç»ØÄꤷ¤Æ¤¯¤À¤µ¤¤¡£¥Ç¥Õ¥©¥ë¥È¤Î¥Æ¥ó¥×¥ì¡¼¥È¤ò¾þ¤ëCSS¥×¥í¥Ñ¥Æ¥£¤ÎÎã¤ò¤¢¤²¤Æ¤ª¤­¤Þ¤¹¡£\r
+</p>\r
+\r
+<pre>div.tb {\r
+       border: 1px solid #000; background: #FFF;\r
+}\r
+div.tb div.head {\r
+       padding: 4px;\r
+       background: #000; color: #FFF;\r
+       font-weight: bold; text-transform: lowercase; letter-spacing: 0.6em;\r
+}\r
+div.tb div.empty {\r
+       padding: 4px;\r
+       font-size: 95%;\r
+}\r
+div.tb div.item {\r
+       padding: 4px;\r
+}\r
+div.tb div.item div.name {\r
+       margin-bottom: 8px;\r
+       font-size: 120%; font-weight: bold;\r
+}\r
+div.tb div.item div.body {\r
+       font-size: 95%;\r
+}\r
+div.tb div.item div.body a {\r
+       font-weight: bold;\r
+}\r
+div.tb div.item div.date {\r
+       margin-bottom: 8px;\r
+       color: #888;\r
+       font-size: 85%; text-align: right;\r
+}\r
+div.tb div.info {\r
+       padding: 4px;\r
+       color: #FFF; background: #888;\r
+       font-size: 85%; font-style: italic;\r
+}</pre>\r
+\r
+<h3>ÆüËܸìÈǹ¹¿·ÍúÎò</h3>\r
+\r
+<ul>\r
+       <li>Version 2.0.3jp13 : (2008/12/14)</li>\r
+       <li>¡¡[Fixed] Ping¥Õ¥©¡¼¥à¤Ø¤Î¥ê¥ó¥¯¤ò³«¤³¤¦¤È¤¹¤ë¤ÈInvalid or expired ticket.¤Ë¤Ê¤ëÌäÂê¤ò½¤Àµ</li>\r
+       <li>¡¡[Fixed] TrackBackÁ÷¿®»þ¤Î¥¨¥é¡¼¥Ï¥ó¥É¥ê¥ó¥°¤ò²þÎɤ·¤¿</li>\r
+       <li>¡¡[Fixed] TrackBack¤Îʸ»ú¥³¡¼¥É¤Î¸¡½Ð¤¬Àµ¤·¤¯¹Ô¤ï¤ì¤Ê¤¤¾ì¹ç¤¬¤¢¤ëÌäÂê¤ò½¤Àµ</li>\r
+       \r
+       <li>Version 2.0.3jp12 : (2008/01/12)</li>\r
+       <li>¡¡[Fixed] ¥¨¥é¡¼¥á¥Ã¥»¡¼¥¸¤Îʸ»ú¥³¡¼¥É¤¬Å¬ÀڤǤʤ«¤Ã¤¿ÌäÂê¤ò½¤Àµ</li>\r
+       <li>¡¡[Fixed] ¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤ÎÁ÷¿®Ê¸»ú¥³¡¼¥É¤òUTF-8¸ÇÄê¤Ë¤·¤¿</li>\r
+       <li>¡¡[Fixed] ¥È¥é¥Ã¥¯¥Ð¥Ã¥¯²Ä/ÉԲĤÎȽÄ꤬¤ª¤«¤·¤«¤Ã¤¿ÌäÂê¤ò½¤Àµ</li>\r
+       \r
+       <li>Version 2.0.3jp11 : (2007/09/30)</li>\r
+       <li>¡¡[Added] SuperAdmin°Ê³°¤Ç¤âTrackBack¤¬´ÉÍý¤Ç¤­¤ë¤è¤¦¤Ë¤·¤¿</li>\r
+       \r
+       <li>Version 2.0.3jp10 : (2007/06/30)</li>\r
+       <li>¡¡[Fixed] mysql_query()¤òsql_query()¤ËÊѹ¹</li>\r
+       <li>¡¡[Changed] ¼ÂÂλ²¾È¥Æ¡¼¥Ö¥ë¤Ë¤Ä¤¤¤ÆNucleusɸ½à¤â¤Î¤ò»È¤¦¤è¤¦¤Ë¤·¤¿</li>\r
+       <li>¡¡[Changed] ¥¤¥ó¥¹¥È¡¼¥ë¤Ç¤­¤ë¥Ð¡¼¥¸¥ç¥ó¤ò3.3°Ê¹ß¤·¤¿</li>\r
+       <li>¡¡[Changed] Rico¤ò2.0¤Ë¥¢¥Ã¥×¥Ç¡¼¥È¤·¤¿¤Î¤Ëȼ¤¤¡¢´ÉÍý²èÌ̤ε¡Ç½¤òÁý¶¯</li>\r
+       <li>¡¡[Added] ¥³¥á¥ó¥ÈÉôʬ¤Ç¤â¥Æ¥ó¥×¥ì¡¼¥ÈÊÑ¿ô¤¬»È¤¨¤ë¤è¤¦¤Ë¤·¤¿</li>\r
+       <li>¡¡[Fixed] ¥È¥é¥Ã¥¯¥Ð¥Ã¥¯Á÷¿®Éôʬ¤ÎÉÔ¶ñ¹ç¤ò½¤Àµ(FC2Âкö)</li>\r
+       <li>¡¡[Changed] UserAgent¤ò¥ª¥ê¥¸¥Ê¥ëÈǤˤ¢¤ï¤»¤ÆÊѹ¹</li>\r
+       <li>¡¡[Changed] TrackBack¤Î¥ì¥¹¥Ý¥ó¥¹¤Î²òÀϤËXML¥Ñ¡¼¥µ¡¼¤ò»È¤¦¤è¤¦¤Ë¤·¤¿</li>\r
+       <li>¡¡[Fixed] ¸ÀµÚ¥ê¥ó¥¯¥Á¥§¥Ã¥¯¤ÎÉÔ¶ñ¹ç¤ò½¤Àµ</li>\r
+       <li>¡¡[Added] ÊÝα¤Ë¤·¤Æ¤¤¤ëURL¤ÈƱ¤¸URL¤Î¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤ò̵»ë¤¹¤ë¤è¤¦¤Ë¤·¤¿</li>\r
+       <!-- 10.1, 10.2, 10.3 -->\r
+       <li>¡¡[Fixed] ¸ÀµÚ¥ê¥ó¥¯¥Á¥§¥Ã¥¯¤ÎÍѤÎURLÀ¸À®¥ë¡¼¥Á¥ó¤ÎÉÔ¶ñ¹ç¤ò½¤Àµ</li>\r
+       <!-- 10.4 -->\r
+       <li>¡¡[Added] spam¥È¥é¥Ã¥¯¥Ð¥Ã¥¯°ì³ç¾Ãµî»þ¤Ë³Îǧ¤¬½Ð¤ë¤è¤¦¤Ë¤·¤¿</li>\r
+       \r
+       <li>Version 2.0.3jp9 : (2007/05/04)</li>\r
+       <li>¡¡[Added] doIf()¤òÄɲÃ(Nucleus 3.3¸þ¤±)</li>\r
+       <li>¡¡[Added] URL¤¬Ìµ¸ú¤Ê¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤ò̵»ë¤¹¤ë¤è¤¦¤Ë¤·¤¿</li>\r
+       \r
+       <li>Version 2.0.3jp8 : (2007/03/18)</li>\r
+       <li>¡¡[Fixed] ´ÉÍý²èÌ̤ÇStory¤Î¥ê¥ó¥¯¤ÎÉÔ¶ñ¹ç¤ò½¤Àµ</li>\r
+       <li>¡¡[Changed] URLÃê½Ð¥ë¡¼¥Á¥ó¤ò²þÎÉ</li>\r
+       <li>¡¡[Changed] ´ÉÍý²èÌ̤ؤΥê¥ó¥¯¤ò½¤Àµ</li>\r
+       <li>¡¡[Changed] ´ÉÍý²èÌ̤Υڡ¼¥¸¥ó¥°¤òAjaxÂбþ¤Ë¤·¤¿</li>\r
+       <li>¡¡[Changed] ¥È¥é¥Ã¥¯¥Ð¥Ã¥¯ÄÌÃÎ¥¢¥É¥ì¥¹¤ò¥Ö¥í¥°¤´¤È¤ËÀßÄê¤Ç¤­¤ë¤è¤¦¤Ë¤·¤¿</li>\r
+       <li>¡¡[Fixed] php¤Îshort open¥¿¥°¤ò½¤Àµ</li>\r
+       <li>¡¡[Fixed] ´ÉÍý²èÌ̤Υȥé¥Ã¥¯¥Ð¥Ã¥¯°ìÍ÷¤ÇÆüÉÕ¤¬Àµ¾ï¤Ëɽ¼¨¤µ¤ì¤Ê¤¤ÌäÂê¤ò½¤Àµ</li>\r
+       \r
+       <li>Version 2.0.3jp7 : (2006/11/26)</li>\r
+       <li>¡¡[Changed] SpamChek¤Ë¤Ä¤¤¤ÆÈùÄ´À°</li>\r
+       <li>¡¡[Added] Ticket½èÍý¤òÄɲÃ(CSRFÂкö)</li>\r
+       <li>¡¡[Fixed] URL¤Ë&amp;¤¬Æþ¤Ã¤Æ¤¤¤ë¤È¤­¤ÎÆ°ºî¤òÊѹ¹</li>\r
+       <li>¡¡[Added] ´ÉÍý²èÌ̤˥¢¥¤¥³¥ó¤òÄɲÃ</li>\r
+       \r
+       <li>Version 2.0.3jp6 : (2006/09/30)</li>\r
+       <li>¡¡[Fixed] ¥»¥­¥å¥ê¥Æ¥£¤Î¸þ¾å</li>\r
+       \r
+       <li>Version 2.0.3jp5 : (2006/09/16)</li>\r
+       <li>¡¡[Fixed] getPermaLinksFromText()Æâ¤ÎURLÃê½Ð¥ë¡¼¥Á¥ó¤ÎÉÔ¶ñ¹ç¤ò½¤Àµ</li>\r
+       <li>¡¡[Fixed] Auto-Discovery»þ¤ËSQL¥¨¥é¡¼¤¬½Ð¤ëÉÔ¶ñ¹ç¤ò½¤Àµ</li>\r
+       <li>¡¡[Fixed] ´ÉÍý²èÌ̤ǥѡ¼¥¹¥¨¥é¡¼¤¬½Ð¤ë¾ì¹ç¤¬¤¢¤ëÉÔ¶ñ¹ç¤ò½¤Àµ</li>\r
+       <li>¡¡[Changed] SQL¤Î¥¯¥©¡¼¥È¤ò"¤«¤é'¤ËÊѹ¹</li>\r
+       <li>¡¡[Fixed] mb_emulator´Ä¶­¤Ë¤Æ¥¨¥é¡¼¤¬½Ð¤ëÌäÂê¤ò½¤Àµ</li>\r
+       <li>¡¡[Fixed] curl¤¬Í­¸ú¤Ê´Ä¶­¤Ç¥¨¥é¡¼¤¬½Ð¤ëÌäÂê¤ò½¤Àµ</li>\r
+       \r
+       <li>Version 2.0.3jp4 : (2006/07/15)</li>\r
+       <li>¡¡[Added] AutoDiscoveryURL½ÐÎÏ»þ¤ËSpamCheck¤ò¹Ô¤¦¤è¤¦¤Ë¤·¤¿</li>\r
+       <li>¡¡[Added] ¥á¥Ã¥»¡¼¥¸¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤòÆüËܸ첽</li>\r
+       <li>¡¡[Added] Âç¼êASP¤Î¸ÀµÚ¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¥Á¥§¥Ã¥¯¤ÎºÝ¤ËURL¤Î¥ê¥À¥¤¥ì¥¯¥È¤ò²ò½ü¤¹¤ë¤è¤¦¤Ë¤·¤¿</li>\r
+</ul>
\ No newline at end of file
diff --git a/NP_TrackBack/trunk/trackback/japanese-euc.templates/all.html b/NP_TrackBack/trunk/trackback/japanese-euc.templates/all.html
new file mode 100644 (file)
index 0000000..67ea054
--- /dev/null
@@ -0,0 +1,106 @@
+<?php global $manager; ?>\r
+<h2>\r
+       All trackbacks\r
+       <?php if ($count > $amount): ?>\r
+               (Page <?php echo ceil($start / $amount) + 1;?> of <?php echo ceil($count / $amount);?>)\r
+       <?php endif; ?>\r
+</h2>\r
+\r
+<?php if(count($items)): ?>\r
+<?php if ($count > $amount): ?>\r
+<table class="navigation">\r
+       <tr>\r
+               <td style='padding: 0;'>\r
+                       <?php if ($start > 0): ?>\r
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">\r
+                               <div>\r
+                                       <input type="submit" value="&lt;&lt; Previous" />       \r
+                                       <input type="hidden" name="action" value="all" />\r
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />\r
+                                       <?php $manager->addTicketHidden(); ?>\r
+                               </div>\r
+                       </form>\r
+                       <?php endif; ?>\r
+               </td>\r
+               <td style='padding: 0; text-align: right;'>     \r
+                       <?php if ($start + $amount < $count): ?>\r
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">\r
+                               <div>\r
+                                       <input type="submit" value="Next &gt; &gt;" />\r
+                                       <input type="hidden" name="action" value="all" />\r
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />\r
+                                       <?php $manager->addTicketHidden(); ?>\r
+                               </div>\r
+                       </form>\r
+                       <?php endif; ?>\r
+               </td>\r
+       </tr>\r
+</table>\r
+<?php endif; ?>\r
+\r
+<table>\r
+       <thead>\r
+               <tr>\r
+                       <th>Date</th>\r
+                       <th>Story</th>\r
+                       <th>Title, Blog and Excerpt</th>\r
+                       <th colspan="2">Actions</th>\r
+               </tr>\r
+       </thead>\r
+       <tbody>\r
+               <?php while (list(,$item) = each ($items)): ?>\r
+               <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>\r
+                       <td>\r
+                               <?php echo str_replace(' ', '&nbsp;', date("Y-m-d @ H:i",$item['timestamp']));?>\r
+                       </td>\r
+                       <td>\r
+                               <a href="<?php echo $item['story_url']; ?>"><?php echo $item['story'];?></a>\r
+                       </td>\r
+                       <td>\r
+                               <a href="<?php echo $item['url'];?>"><img alt="Visit" border="0" src="<?php echo $plugindirurl?>silk/house_go.png" /></a>\r
+                               <strong><?php echo $item['title'];?></strong> \r
+                               <em>(<?php echo $item['blog_name'];?>)</em><br />\r
+                               <?php echo $item['excerpt'];?>\r
+                       </td>\r
+                       <td>\r
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=block&tb='.$item['id'].'&next=all&start='.$start),ENT_QUOTES);?>"><img alt="Block" border="0" src="<?php echo $plugindirurl?>silk/delete.png" /></a>\r
+                       </td>\r
+                       <td>\r
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=delete&tb='.$item['id'].'&next=all&start='.$start),ENT_QUOTES);?>"><img alt="Delete" border="0" src="<?php echo $plugindirurl?>silk/cross.png" /></a>\r
+                       </td>\r
+               </tr>\r
+               <?php endwhile; ?>\r
+       </tbody>\r
+</table>\r
+\r
+<?php if ($count > $amount): ?>\r
+<table class="navigation">\r
+       <tr>\r
+               <td style='padding: 0;'>\r
+                       <?php if ($start > 0): ?>\r
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">\r
+                               <div>\r
+                                       <input type="submit" value="&lt;&lt; Previous" />       \r
+                                       <input type="hidden" name="action" value="all" />\r
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />\r
+                                       <?php $manager->addTicketHidden(); ?>\r
+                               </div>\r
+                       </form>\r
+                       <?php endif; ?>\r
+               </td>\r
+               <td style='padding: 0; text-align: right;'>     \r
+                       <?php if ($start + $amount < $count): ?>\r
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">\r
+                               <div>\r
+                                       <input type="submit" value="Next &gt; &gt;" />\r
+                                       <input type="hidden" name="action" value="all" />\r
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />\r
+                                       <?php $manager->addTicketHidden(); ?>\r
+                               </div>\r
+                       </form>\r
+                       <?php endif; ?>\r
+               </td>\r
+       </tr>\r
+</table>\r
+<?php endif; ?>\r
+<?php endif; ?>
\ No newline at end of file
diff --git a/NP_TrackBack/trunk/trackback/japanese-euc.templates/all_ajax.html b/NP_TrackBack/trunk/trackback/japanese-euc.templates/all_ajax.html
new file mode 100644 (file)
index 0000000..f75b50c
--- /dev/null
@@ -0,0 +1,130 @@
+<?php global $manager; ?>\r
+<h2>\r
+       All trackbacks\r
+</h2>\r
+\r
+<div id="message" style="color: red;"></div>\r
+\r
+<div style="width: 95%">\r
+<span id="tb_grid_bookmark"></span>\r
+\r
+<table id="tb_grid" style="border:0; margin:0;">\r
+       <colgroup>\r
+               <col style="width:25px;" />\r
+               <col style="width:40px;" />\r
+               <col style="width:70px;" />\r
+               <col style="width:150px;" />\r
+               <col style="width:200px;"/>\r
+               <col style="width:25px;" />\r
+       </colgroup>\r
+       <thead>\r
+               <tr>\r
+                       <th>&#160;</th>\r
+                       <th>id</th>\r
+                       <th>Date</th>\r
+                       <th>Story</th>\r
+                       <th>Title, Blog and Excerpt</th>\r
+                       <th>&#160;</th>\r
+               </tr>\r
+       </thead>\r
+</table>\r
+\r
+¾åµ­¤ÇÁªÂò¤·¤¿¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤ò°ì³ç¤·¤Æ½èÍý¤·¤Þ¤¹\r
+<a href="javascript:doDelete()" onclick=""><img alt="Delete" border="0" src="<?php echo $plugindirurl?>silk/cross.png" /></a>\r
+<a href="javascript:doBlock()" onclick=""><img alt="Block" border="0" src="<?php echo $plugindirurl?>silk/delete.png" /></a>\r
+</div>\r
+\r
+<!--\r
+<textarea id='tb_grid_debugmsgs' rows='5' cols='80' style='font-size:smaller;'></textarea>\r
+-->\r
+\r
+<script type="text/javascript">\r
+//<![CDATA[\r
+       Rico.loadModule('LiveGridAjax');\r
+       Rico.loadModule('LiveGridMenu');\r
+       Rico.include('translations/livegrid_ja.js');\r
+       Rico.include('ricoAjaxEngine.js');\r
+       \r
+       Rico.onLoad( function() {\r
+               var params = [\r
+                       'action=ajax',\r
+                       'type=all',\r
+                       'ticket=<?php echo $ticket ;?>'\r
+               ]; \r
+               \r
+               var cb = new Rico.TableColumn.checkbox('1','0');\r
+               var colspec = [\r
+                       {canHide:false, type:'control', control:cb, ClassName:'aligncenter'},\r
+                       {type:'raw'},\r
+                       {type:'raw'},\r
+                       ,\r
+                       ,\r
+                       ,\r
+               ];\r
+               \r
+               var opts = {\r
+                       saveColumnInfo   : {width:true, filter:false, sort:false}, \r
+                       menuEvent       : 'none',\r
+                       frozenColumns   : 2,\r
+                       canSortDefault  : false,\r
+                       canHideDefault  : true,\r
+                       allowColResize  : true,\r
+                       canFilterDefault: false,\r
+                       highlightElem   : 'none',\r
+                       columnSpecs     : colspec\r
+               };\r
+               \r
+               buffer = new Rico.Buffer.AjaxSQL('<?php echo $CONF['PluginURL'].'trackback/';?>grid.php',\r
+                               {TimeOut:10, requestParameters:params, sortParmFmt: 'displayName'}\r
+               );\r
+               orderGrid=new Rico.LiveGrid ('tb_grid', buffer, opts);\r
+               orderGrid.menu=new Rico.GridMenu({});\r
+               \r
+               // ajaxEngine\r
+               ajaxEngine = new Rico.AjaxEngine;\r
+               ajaxEngine.registerRequest('updateData', '<?php echo $CONF['PluginURL'].'trackback/';?>grid.php' );\r
+               ajaxEngine.registerAjaxElement('message');\r
+       });\r
+\r
+       function checkUpdateIds(){\r
+               var updateIds = [];\r
+               Rico.writeDebugMsg('check updated rows');\r
+               for(var i = 0; i < buffer.size; i++){\r
+                       row = buffer.rows[i];\r
+                       if( row[0].content && row[0].content == '1' ){\r
+                               updateIds.push(row[1].content);\r
+                               Rico.writeDebugMsg('id: '+row[1].content+' updated');\r
+                       }\r
+               }\r
+               return updateIds;\r
+       }\r
+       \r
+       function doBlock(){\r
+               var ids = checkUpdateIds();\r
+               if( !(ids.length && ids.length > 0) ) return ;\r
+               var params = [\r
+                       'action=doblock',\r
+                       'ticket=<?php echo $ticket ;?>',\r
+                       'ids='+ids.join(',')\r
+               ]; \r
+               ajaxEngine.sendRequest('updateData', {parameters: ajaxEngine._createQueryString(params, 0)});\r
+               orderGrid.resetContents('tb_grid');\r
+               buffer.fetch(-1);\r
+       }\r
+       \r
+       function doDelete(){\r
+               var ids = checkUpdateIds();\r
+               if( !(ids.length && ids.length > 0) ) return ;\r
+               if( !confirm('ËÜÅö¤Ëºï½ü¤·¤Þ¤¹¤«¡©') ) return ;\r
+               \r
+               var params = [\r
+                       'action=dodelete',\r
+                       'ticket=<?php echo $ticket ;?>',\r
+                       'ids='+ids.join(',')\r
+               ];\r
+               ajaxEngine.sendRequest('updateData', {parameters: ajaxEngine._createQueryString(params, 0)});\r
+               orderGrid.resetContents('tb_grid');\r
+               buffer.fetch(-1);\r
+       }\r
+//]]>\r
+</script>\r
diff --git a/NP_TrackBack/trunk/trackback/japanese-euc.templates/blocked.html b/NP_TrackBack/trunk/trackback/japanese-euc.templates/blocked.html
new file mode 100644 (file)
index 0000000..e510f86
--- /dev/null
@@ -0,0 +1,119 @@
+<?php global $manager; ?>\r
+<h2>\r
+       ¥Ö¥í¥Ã¥¯¤µ¤ì¤¿¥È¥é¥Ã¥¯¥Ð¥Ã¥¯\r
+       <?php if ($count > $amount): ?>\r
+               (Page <?php echo ceil($start / $amount) + 1;?> of <?php echo ceil($count / $amount);?>)\r
+       <?php endif; ?>\r
+</h2>\r
+\r
+<ul>\r
+       <li><a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=blocked_clear&next=blocked'),ENT_QUOTES); ?>" onClick="return confirm('¥Ö¥í¥Ã¥¯¤µ¤ì¤¿¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤ò¥¯¥ê¥¢¤·¤Æ¤â¤è¤í¤·¤¤¤Ç¤¹¤«¡©');">¥Ö¥í¥Ã¥¯¤µ¤ì¤¿¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤Î¥¯¥ê¥¢</a></li>\r
+       <li><a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=blocked_spamclear&next=blocked'),ENT_QUOTES); ?>" onClick="return confirm('spamȽÄꤵ¤ì¤¿¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤ò¥¯¥ê¥¢¤·¤Æ¤â¤è¤í¤·¤¤¤Ç¤¹¤«¡©');">spamȽÄꤵ¤ì¤¿¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤Î¥¯¥ê¥¢</a></li> \r
+</ul>\r
+\r
+<?php if(count($items)): ?>\r
+<?php if ($count > $amount): ?>\r
+<table class="navigation">\r
+       <tr>\r
+               <td style='padding: 0;'>\r
+                       <?php if ($start > 0): ?>\r
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">\r
+                               <div>\r
+                                       <input type="submit" value="&lt;&lt; Previous" />       \r
+                                       <input type="hidden" name="action" value="blocked" />\r
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />\r
+                                       <?php $manager->addTicketHidden(); ?>\r
+                               </div>\r
+                       </form>\r
+                       <?php endif; ?>\r
+               </td>\r
+               <td style='padding: 0; text-align: right;'>     \r
+                       <?php if ($start + $amount < $count): ?>\r
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">\r
+                               <div>\r
+                                       <input type="submit" value="Next &gt; &gt;" />\r
+                                       <input type="hidden" name="action" value="blocked" />\r
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />\r
+                                       <?php $manager->addTicketHidden(); ?>\r
+                               </div>\r
+                       </form>\r
+                       <?php endif; ?>\r
+               </td>\r
+       </tr>\r
+</table>\r
+<?php endif; ?>\r
+\r
+<table>\r
+       <thead>\r
+               <tr>\r
+                       <th>Date</th>\r
+                       <th>Story</th>\r
+                       <th>Title, Blog and Excerpt</th>\r
+                       <th colspan="2">Actions</th>\r
+               </tr>\r
+       </thead>\r
+       <tbody>\r
+               <?php while (list(,$item) = each ($items)): ?>\r
+               <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>\r
+                       <td>\r
+                               <?php echo str_replace(' ', '&nbsp;', date("Y-m-d @ H:i",$item['timestamp']));?>\r
+                       </td>\r
+                       <td>\r
+                               <a href="<?php echo $item['story_url']; ?>"><?php echo $item['story'];?></a>\r
+                       </td>\r
+                       <td>\r
+                               <a href="<?php echo $item['url'];?>"><img alt="Visit" border="0" src="<?php echo $plugindirurl;?>silk/house_go.png" /></a>\r
+                               <strong><?php echo $item['title'];?></strong> \r
+                               <em>(<?php echo $item['blog_name'];?>)</em>\r
+                               <?php echo $item['spam'] ? \r
+                                       '<img alt="spam" border="0" src="' . $plugindirurl . 'silk/delete.png" />' : \r
+                                       '';?>\r
+                               <?php echo $item['link'] ? \r
+                                       '' : \r
+                                       '<img alt="NOT Linked" border="0" src="' . $plugindirurl . 'silk/link_break.png" />';?>\r
+                               <br />\r
+                               <?php echo $item['excerpt'];?>\r
+                       </td>\r
+                       <td>\r
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=unblock&tb='.$item['id'].'&next=blocked&start='.$start),ENT_QUOTES);?>"><img alt="Unblock" border="0" src="<?php echo $plugindirurl;?>silk/accept.png" /></a>\r
+                       </td>\r
+                       <td>\r
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=delete&tb='.$item['id'].'&next=blocked&start='.$start),ENT_QUOTES);?>"><img alt="Delete" border="0" src="<?php echo $plugindirurl;?>silk/cross.png" /></a>\r
+                       </td>\r
+               </tr>\r
+               <?php endwhile; ?>\r
+       </tbody>\r
+</table>\r
+\r
+<?php if ($count > $amount): ?>\r
+<table class="navigation">\r
+       <tr>\r
+               <td style='padding: 0;'>\r
+                       <?php if ($start > 0): ?>\r
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">\r
+                               <div>\r
+                                       <input type="submit" value="&lt;&lt; Previous" />       \r
+                                       <input type="hidden" name="action" value="blocked" />\r
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />\r
+                                       <?php $manager->addTicketHidden(); ?>\r
+                               </div>\r
+                       </form>\r
+                       <?php endif; ?>\r
+               </td>\r
+               <td style='padding: 0; text-align: right;'>     \r
+                       <?php if ($start + $amount < $count): ?>\r
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">\r
+                               <div>\r
+                                       <input type="submit" value="Next &gt; &gt;" />\r
+                                       <input type="hidden" name="action" value="blocked" />\r
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />\r
+                                       <?php $manager->addTicketHidden(); ?>\r
+                               </div>\r
+                       </form>\r
+                       <?php endif; ?>\r
+               </td>\r
+       </tr>\r
+</table>\r
+<?php endif; ?>\r
+<?php endif; ?>\r
+\r
diff --git a/NP_TrackBack/trunk/trackback/japanese-euc.templates/blocked_ajax.html b/NP_TrackBack/trunk/trackback/japanese-euc.templates/blocked_ajax.html
new file mode 100644 (file)
index 0000000..7f5aa82
--- /dev/null
@@ -0,0 +1,134 @@
+<?php global $manager; ?>\r
+<h2>\r
+       ¥Ö¥í¥Ã¥¯¤µ¤ì¤¿¥È¥é¥Ã¥¯¥Ð¥Ã¥¯\r
+</h2>\r
+\r
+<ul>\r
+       <li><a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=blocked_clear&next=blocked'),ENT_QUOTES); ?>" onClick="return confirm('¥Ö¥í¥Ã¥¯¤µ¤ì¤¿¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤ò¥¯¥ê¥¢¤·¤Æ¤â¤è¤í¤·¤¤¤Ç¤¹¤«¡©');">¥Ö¥í¥Ã¥¯¤µ¤ì¤¿¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤Î¥¯¥ê¥¢</a></li>\r
+       <li><a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=blocked_spamclear&next=blocked'),ENT_QUOTES); ?>" onClick="return confirm('spamȽÄꤵ¤ì¤¿¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤ò¥¯¥ê¥¢¤·¤Æ¤â¤è¤í¤·¤¤¤Ç¤¹¤«¡©');">spamȽÄꤵ¤ì¤¿¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤Î¥¯¥ê¥¢</a></li> \r
+</ul>\r
+\r
+<div id="message" style="color: red;"></div>\r
+\r
+<div style="width: 95%">\r
+<span id="tb_grid_bookmark"></span>\r
+\r
+<table id="tb_grid" style="border:0; margin:0;">\r
+       <colgroup>\r
+               <col style="width:25px;" />\r
+               <col style="width:40px;" />\r
+               <col style="width:70px;" />\r
+               <col style="width:150px;" />\r
+               <col style="width:200px;"/>\r
+               <col style="width:25px;" />\r
+       </colgroup>\r
+       <thead>\r
+               <tr>\r
+                       <th>&#160;</th>\r
+                       <th>id</th>\r
+                       <th>Date</th>\r
+                       <th>Story</th>\r
+                       <th>Title, Blog and Excerpt</th>\r
+                       <th>&#160;</th>\r
+               </tr>\r
+       </thead>\r
+</table>\r
+¾åµ­¤ÇÁªÂò¤·¤¿¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤ò°ì³ç¤·¤Æ½èÍý¤·¤Þ¤¹\r
+<a href="javascript:doUnBlock()" onclick=""><img alt="Unblock" border="0" src="<?php echo $plugindirurl;?>silk/accept.png" /></a>\r
+<a href="javascript:doDelete()" onclick=""><img alt="Delete" border="0" src="<?php echo $plugindirurl?>silk/cross.png" /></a>\r
+</div>\r
+\r
+<!--\r
+<textarea id='tb_grid_debugmsgs' rows='5' cols='80' style='font-size:smaller;'></textarea>\r
+-->\r
+\r
+<script type="text/javascript">\r
+//<![CDATA[\r
+       Rico.loadModule('LiveGridAjax');\r
+       Rico.loadModule('LiveGridMenu');\r
+       Rico.include('translations/livegrid_ja.js');\r
+       Rico.include('ricoAjaxEngine.js');\r
+       \r
+       Rico.onLoad( function() {\r
+               var params = [\r
+                       'action=ajax',\r
+                       'type=blocked',\r
+                       'ticket=<?php echo $ticket ;?>'\r
+               ]; \r
+               \r
+               var cb = new Rico.TableColumn.checkbox('1','0');\r
+               var colspec = [\r
+                       {canHide:false, type:'control', control:cb, ClassName:'aligncenter'},\r
+                       {type:'raw'},\r
+                       {type:'raw'},\r
+                       ,\r
+                       ,\r
+                       ,\r
+               ];\r
+               \r
+               var opts = {\r
+                       saveColumnInfo   : {width:true, filter:false, sort:false}, \r
+                       menuEvent       : 'none',\r
+                       frozenColumns   : 2,\r
+                       canSortDefault  : false,\r
+                       canHideDefault  : true,\r
+                       allowColResize  : true,\r
+                       canFilterDefault: false,\r
+                       highlightElem   : 'none',\r
+                       columnSpecs     : colspec\r
+               };\r
+               \r
+               buffer = new Rico.Buffer.AjaxSQL('<?php echo $CONF['PluginURL'].'trackback/';?>grid.php',\r
+                               {TimeOut:10, requestParameters:params, sortParmFmt: 'displayName'}\r
+               );\r
+               orderGrid=new Rico.LiveGrid ('tb_grid', buffer, opts);\r
+               orderGrid.menu=new Rico.GridMenu({});\r
+               \r
+               // ajaxEngine\r
+               ajaxEngine = new Rico.AjaxEngine;\r
+               ajaxEngine.registerRequest('updateData', '<?php echo $CONF['PluginURL'].'trackback/';?>grid.php' );\r
+               ajaxEngine.registerAjaxElement('message');\r
+       });\r
+\r
+       function checkUpdateIds(){\r
+               var updateIds = [];\r
+               Rico.writeDebugMsg('check updated rows');\r
+               for(var i = 0; i < buffer.size; i++){\r
+                       row = buffer.rows[i];\r
+                       if( row[0].content && row[0].content == '1' ){\r
+                               updateIds.push(row[1].content);\r
+                               Rico.writeDebugMsg('id: '+row[1].content+' updated');\r
+                       }\r
+               }\r
+               return updateIds;\r
+       }\r
+       \r
+       function doUnBlock(){\r
+               var ids = checkUpdateIds();\r
+               if( !(ids.length && ids.length > 0) ) return ;\r
+               var params = [\r
+                       'action=dounblock',\r
+                       'ticket=<?php echo $ticket ;?>',\r
+                       'ids='+ids.join(',')\r
+               ]; \r
+               ajaxEngine.sendRequest('updateData', {parameters: ajaxEngine._createQueryString(params, 0)});\r
+               orderGrid.resetContents('tb_grid');\r
+               buffer.fetch(-1);\r
+       }\r
+       \r
+       function doDelete(){\r
+               var ids = checkUpdateIds();\r
+               if( !(ids.length && ids.length > 0) ) return ;\r
+               if( !confirm('ËÜÅö¤Ëºï½ü¤·¤Þ¤¹¤«¡©') ) return ;\r
+               \r
+               var params = [\r
+                       'action=dodelete',\r
+                       'ticket=<?php echo $ticket ;?>',\r
+                       'ids='+ids.join(',')\r
+               ];\r
+               ajaxEngine.sendRequest('updateData', {parameters: ajaxEngine._createQueryString(params, 0)});\r
+               orderGrid.resetContents('tb_grid');\r
+               buffer.fetch(-1);\r
+       }\r
+//]]>\r
+</script>\r
diff --git a/NP_TrackBack/trunk/trackback/japanese-euc.templates/form.html b/NP_TrackBack/trunk/trackback/japanese-euc.templates/form.html
new file mode 100644 (file)
index 0000000..24ce702
--- /dev/null
@@ -0,0 +1,56 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\r
+<html>\r
+       <head>\r
+               <title>TrackBack¤Î¼êÆ°Á÷¿®</title>\r
+               <link rel="stylesheet" type="text/css" href="<?php echo $CONF['AdminURL']?>styles/bookmarklet.css" />\r
+       </head>\r
+       \r
+       <body>\r
+               <h1>TrackBack¤Î¼êÆ°Á÷¿®</h1>\r
+<?php if ($success): ?>\r
+               <p>\r
+                       <strong>¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤ÎÁ÷¿®¤¬´°Î»¤·¤Þ¤·¤¿</strong>\r
+               </p>\r
+<?php endif; ?>\r
+<?php if ($error): ?>\r
+               <p>\r
+                       <strong><?php echo $status; ?></strong>\r
+               </p>\r
+<?php endif; ?>\r
+<?php if ($form): ?>           \r
+               <form method="post" action="<?php echo $CONF['ActionURL'] ?>">\r
+               \r
+               <div>\r
+                       <input type="hidden" name="tb_id" value="<?php echo $itemid;?>" />\r
+                       <input type="hidden" name="action" value="plugin" />\r
+                       <input type="hidden" name="name" value="TrackBack" />\r
+                       <input type="hidden" name="type" value="ping" />\r
+                       \r
+                       <table>\r
+                               <tr>\r
+                                       <td>¤¢¤Ê¤¿¤Îµ­»ö¤Îurl</td>\r
+                                       <td><input type="text" value="" name="url" size="60" /></td>\r
+                               </tr>\r
+                               <tr>\r
+                                       <td>µ­»ö¤Î¥¿¥¤¥È¥ë</td>\r
+                                       <td><input type="text" value="" name="title" size="60" /></td>\r
+                               </tr>\r
+                               <tr>\r
+                                       <td>µ­»ö¤ÎÍ×Ìóʸ</td>\r
+                                       <td><textarea name="excerpt" cols="40" rows="5"></textarea></td>\r
+                               </tr>\r
+                               <tr>\r
+                                       <td>¤¢¤Ê¤¿¤Îblog¤Î̾Á°</td>\r
+                                       <td><input type="text" value="" name="blog_name" size="60" /></td>\r
+                               </tr>\r
+                               <tr>\r
+                                       <td>¥È¥é¥Ã¥¯¥Ð¥Ã¥¯Á÷¿®</td>\r
+                                       <td><input type="submit" value="¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤òÁ÷¿®¤¹¤ë" /></td>\r
+                               </tr>\r
+                       </table>\r
+               </div>\r
+               \r
+               </form>\r
+<?php endif; ?>\r
+       </body>\r
+</html>
\ No newline at end of file
diff --git a/NP_TrackBack/trunk/trackback/japanese-euc.templates/index.html b/NP_TrackBack/trunk/trackback/japanese-euc.templates/index.html
new file mode 100644 (file)
index 0000000..a2a00ed
--- /dev/null
@@ -0,0 +1,34 @@
+<?php global $manager; ?>\r
+<h2>Overview of all items</h2>\r
+\r
+<?php if(count($blogs)): ?>\r
+\r
+<table>\r
+<?php while (list(,$blog) = each ($blogs)): ?>\r
+<?php if(count($blog['items'])): ?>\r
+       <thead>\r
+               <tr>\r
+                       <th>Blog: <?php echo htmlspecialchars($blog['bname']);?></th>\r
+                       <th>Total</th>\r
+                       <th>Action</th>\r
+               </tr>\r
+       </thead>\r
+       <tbody>\r
+               <?php while (list(,$item) = each ($blog['items'])): ?>\r
+               <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>\r
+                       <td>\r
+                               <?php echo $item['ititle'];?>\r
+                       </td>\r
+                       <td>\r
+                               <?php echo htmlspecialchars($item['total']);?>\r
+                       </td>\r
+                       <td>\r
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=list&id='.$item['inumber']),ENT_QUOTES);?>">Trackbacks</a>\r
+                       </td>\r
+               </tr>\r
+               <?php endwhile; ?>\r
+       </tbody>\r
+<?php endif; ?>\r
+<?php endwhile; ?>\r
+</table>\r
+<?php endif; ?>
\ No newline at end of file
diff --git a/NP_TrackBack/trunk/trackback/japanese-euc.templates/list.html b/NP_TrackBack/trunk/trackback/japanese-euc.templates/list.html
new file mode 100644 (file)
index 0000000..05eeeb8
--- /dev/null
@@ -0,0 +1,107 @@
+<?php global $manager; ?>\r
+<h2>\r
+       All trackbacks for &quot;<?php echo $story['title'];?>&quot;\r
+       <?php if ($count > $amount): ?>\r
+               (Page <?php echo ceil($start / $amount) + 1;?> of <?php echo ceil($count / $amount);?>)\r
+       <?php endif; ?>\r
+</h2>\r
+\r
+<?php if(count($items)): ?>\r
+<?php if ($count > $amount): ?>\r
+<table class="navigation">\r
+       <tr>\r
+               <td style='padding: 0;'>\r
+                       <?php if ($start > 0): ?>\r
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">\r
+                               <div>\r
+                                       <input type="submit" value="&lt;&lt; Previous" />       \r
+                                       <input type="hidden" name="action" value="list" />\r
+                                       <input type="hidden" name="id" value="<?php echo $story['id'];?>" />\r
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />\r
+                                       <?php $manager->addTicketHidden(); ?>\r
+                               </div>\r
+                       </form>\r
+                       <?php endif; ?>\r
+               </td>\r
+               <td style='padding: 0; text-align: right;'>     \r
+                       <?php if ($start + $amount < $count): ?>\r
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">\r
+                               <div>\r
+                                       <input type="submit" value="Next &gt; &gt;" />\r
+                                       <input type="hidden" name="action" value="list" />\r
+                                       <input type="hidden" name="id" value="<?php echo $story['id'];?>" />\r
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />\r
+                                       <?php $manager->addTicketHidden(); ?>\r
+                               </div>\r
+                       </form>\r
+                       <?php endif; ?>\r
+               </td>\r
+       </tr>\r
+</table>\r
+<?php endif; ?>\r
+\r
+<table>\r
+       <thead>\r
+               <tr>\r
+                       <th>Date</th>\r
+                       <th>Title, Blog and Excerpt</th>\r
+                       <th colspan="2">Actions</th>\r
+               </tr>\r
+       </thead>\r
+       <tbody>\r
+               <?php while (list(,$item) = each ($items)): ?>\r
+               <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>\r
+                       <td>\r
+                               <?php echo str_replace(' ', '&nbsp;', date("Y-m-d @ H:i",$item['timestamp']));?>\r
+                       </td>\r
+                       <td>\r
+                               <a href="<?php echo $item['url'];?>"><img alt="Visit" border="0" src="<?php echo $plugindirurl?>silk/house_go.png" /></a>\r
+                               <strong><?php echo $item['title'];?></strong> \r
+                               <em>(<?php echo $item['blog_name'];?>)</em><br />\r
+                               <?php echo $item['excerpt'];?>\r
+                       </td>\r
+                       <td>\r
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=block&tb='.$item['id'].'&next=list&id='.$story['id'].'&start='.$start),ENT_QUOTES);?>"><img alt="Block" border="0" src="<?php echo $plugindirurl?>silk/delete.png" /></a>\r
+                       </td>\r
+                       <td>\r
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=delete&tb='.$item['id'].'&next=list&id='.$story['id'].'&start='.$start),ENT_QUOTES);?>"><img alt="Delete" border="0" src="<?php echo $plugindirurl?>silk/cross.png" /></a>\r
+                       </td>\r
+               </tr>\r
+               <?php endwhile; ?>\r
+       </tbody>\r
+</table>\r
+\r
+<?php if ($count > $amount): ?>\r
+<table class="navigation">\r
+       <tr>\r
+               <td style='padding: 0;'>\r
+                       <?php if ($start > 0): ?>\r
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">\r
+                               <div>\r
+                                       <input type="submit" value="&lt;&lt; Previous" />       \r
+                                       <input type="hidden" name="action" value="list" />\r
+                                       <input type="hidden" name="id" value="<?php echo $story['id'];?>" />\r
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />\r
+                                       <?php $manager->addTicketHidden(); ?>\r
+                               </div>\r
+                       </form>\r
+                       <?php endif; ?>\r
+               </td>\r
+               <td style='padding: 0; text-align: right;'>     \r
+                       <?php if ($start + $amount < $count): ?>\r
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">\r
+                               <div>\r
+                                       <input type="submit" value="Next &gt; &gt;" />\r
+                                       <input type="hidden" name="action" value="list" />\r
+                                       <input type="hidden" name="id" value="<?php echo $story['id'];?>" />\r
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />\r
+                                       <?php $manager->addTicketHidden(); ?>\r
+                               </div>\r
+                       </form>\r
+                       <?php endif; ?>\r
+               </td>\r
+       </tr>\r
+</table>\r
+<?php endif; ?>\r
+<?php endif; ?>\r
+\r
diff --git a/NP_TrackBack/trunk/trackback/japanese-euc.templates/menu.html b/NP_TrackBack/trunk/trackback/japanese-euc.templates/menu.html
new file mode 100644 (file)
index 0000000..e98b6af
--- /dev/null
@@ -0,0 +1,32 @@
+<?php global $manager?>\r
+<h2>Trackback</h2>\r
+\r
+<script type="text/javascript" src="<?php echo $CONF['PluginURL']?>trackback/js/prototype.js"></script>\r
+<script type="text/javascript" src="<?php echo $CONF['PluginURL']?>trackback/js/rico/rico.js"></script>\r
+\r
+<ul>\r
+       <li>\r
+               <img border="0" src="<?php echo $plugindirurl?>silk/application_view_list.png" />\r
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=index'),ENT_QUOTES);?>">Overview of all items</a>\r
+       </li>\r
+       <li>\r
+               <img border="0" src="<?php echo $plugindirurl?>silk/tick.png" />\r
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=all'),ENT_QUOTES);?>">¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤ÎÁ´¥Ç¡¼¥¿</a>\r
+       </li>\r
+       <li>\r
+               <img border="0" src="<?php echo $plugindirurl?>silk/exclamation.png" />\r
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=blocked'),ENT_QUOTES);?>">¥Ö¥í¥Ã¥¯¤µ¤ì¤¿¥È¥é¥Ã¥¯¥Ð¥Ã¥¯</a>\r
+       </li> \r
+       <li>\r
+               <img border="0" src="<?php echo $plugindirurl?>silk/transmit_go.png" />\r
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=ping'),ENT_QUOTES);?>">¼êÆ°ping</a>\r
+       </li>\r
+       <li>\r
+               <img border="0" src="<?php echo $plugindirurl?>silk/help.png" />\r
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=help'),ENT_QUOTES);?>">¥Ø¥ë¥×</a>\r
+       </li>\r
+    <li>\r
+               <img border="0" src="<?php echo $plugindirurl?>silk/plugin_edit.png" />\r
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['AdminURL'].'index.php?action=pluginoptions&plugid='.$plugid),ENT_QUOTES);?>">¥×¥é¥°¥¤¥ó¥ª¥×¥·¥ç¥óÀßÄê</a>\r
+       </li>\r
+</ul>\r
diff --git a/NP_TrackBack/trunk/trackback/japanese-euc.templates/ping.html b/NP_TrackBack/trunk/trackback/japanese-euc.templates/ping.html
new file mode 100644 (file)
index 0000000..0d993ec
--- /dev/null
@@ -0,0 +1,50 @@
+<?php global $manager; ?>\r
+<h2>¼êÆ°ping¥Õ¥©¡¼¥à</h2>\r
+\r
+<form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">\r
+\r
+    <input type="hidden" name="action" value="sendping" />\r
+    <input type="hidden" name="next" value="ping" />\r
+    <?php $manager->addTicketHidden(); ?>\r
+       \r
+    <table>\r
+        <tr>\r
+            <th colspan='2'>¼êÆ°ping</th>\r
+        </tr>\r
+        <tr>\r
+            <td>¤¢¤Ê¤¿¤Îurl</td>\r
+            <td>\r
+                <input type="text" name="url" size="60" value="<?php echo htmlspecialchars($item['url']);?>" />\r
+            </td>\r
+        </tr>\r
+        <tr>\r
+            <td>µ­»ö¤Î¥¿¥¤¥È¥ë</td>\r
+            <td>\r
+                <input type="text" name="title" size="60" value="<?php echo htmlspecialchars($item['title']);?>" />\r
+            </td>\r
+        </tr>\r
+        <tr>\r
+            <td>µ­»ö¤ÎÍ×Ìóʸ</td>\r
+            <td>\r
+                    <textarea name="excerpt" cols="40" rows="5"><?php echo $item['excerpt'];?></textarea>\r
+            </td>\r
+        </tr>\r
+        <tr>\r
+            <td>Blog̾</td>\r
+            <td>\r
+                <input type="text" name="blog_name" size="60" value="<?php echo htmlspecialchars($item['blogname']);?>" />\r
+            </td>\r
+        </tr>\r
+        <tr>\r
+            <td>pingÀèurl</td>\r
+            <td>\r
+                <input type="text" value="" name="ping_url" size="60" />\r
+            </td>\r
+        </tr>\r
+        <tr>\r
+            <td>Á÷¿®</td>\r
+            <td><input type="submit" value="Á÷¿®" /></td>\r
+        </tr>\r
+    </table>\r
+\r
+</form>
\ No newline at end of file
diff --git a/NP_TrackBack/trunk/trackback/japanese-euc.templates/response_all.xml b/NP_TrackBack/trunk/trackback/japanese-euc.templates/response_all.xml
new file mode 100644 (file)
index 0000000..4ae2445
--- /dev/null
@@ -0,0 +1,35 @@
+<?php echo '<'.'?xml version="1.0" encoding="UTF-8"?'.'>';?>\r
+<?php global $manager; ?>\r
+\r
+<ajax-response>\r
+       <response type="object" id="tb_grid_updater">\r
+               <rowcount><?php echo $count; ?></rowcount>\r
+               <rows update_ui="true" offset="<?php echo $start; ?>" >\r
+                       <?php while (list(,$item) = each ($items)): ?>\r
+                       <tr>\r
+                               <td>0</td>\r
+                               <td><?php echo $item['id'];?></td>\r
+                               <td>\r
+                                       <?php echo date("Y-m-d H:i:s",$item['timestamp']);?>\r
+                               </td>\r
+                               <td>\r
+                                       <![CDATA[\r
+                                       <a href="<?php echo $item['story_url']; ?>"><?php echo $item['story'];?></a>\r
+                                       ]]>\r
+                               </td>\r
+                               <td>\r
+                                       <![CDATA[\r
+                                       <a href="<?php echo $item['url'];?>">\r
+                                               <img alt="Visit" border="0" src="<?php echo $plugindirurl?>silk/house_go.png" />\r
+                                       </a>\r
+                                       <strong><?php echo $item['title'];?></strong>\r
+                                       <?php echo $item['excerpt'];?>\r
+                                       <em>(<?php echo $item['blog_name'];?>)</em>\r
+                                       ]]>\r
+                               </td>\r
+                               <td></td>\r
+                       </tr>\r
+                       <?php endwhile; ?>\r
+               </rows> \r
+       </response>\r
+</ajax-response>\r
diff --git a/NP_TrackBack/trunk/trackback/japanese-euc.templates/response_blocked.xml b/NP_TrackBack/trunk/trackback/japanese-euc.templates/response_blocked.xml
new file mode 100644 (file)
index 0000000..29faac9
--- /dev/null
@@ -0,0 +1,41 @@
+<?php echo '<'.'?xml version="1.0" encoding="UTF-8"?'.'>';?>\r
+<?php global $manager; ?>\r
+\r
+<ajax-response>\r
+       <response type="object" id='tb_grid_updater'>\r
+               <rowcount><?php echo $count; ?></rowcount>\r
+               <rows update_ui='true' >\r
+                       <?php while (list(,$item) = each ($items)): ?>\r
+                       <tr>\r
+                               <td>0</td>\r
+                               <td><?php echo $item['id'];?></td>\r
+                               <td>\r
+                                       <?php echo date("Y-m-d H:i:s",$item['timestamp']);?>\r
+                               </td>\r
+                               <td>\r
+                                       <![CDATA[\r
+                                       <a href="<?php echo $item['story_url']; ?>"><?php echo $item['story'];?></a>\r
+                                       ]]>\r
+                               </td>\r
+                               <td>\r
+                                       <![CDATA[\r
+                                       <a href="<?php echo $item['url'];?>">\r
+                                               <img alt="Visit" border="0" src="<?php echo $plugindirurl?>silk/house_go.png" />\r
+                                       </a>\r
+                                       <strong><?php echo $item['title'];?></strong>\r
+                                       <?php echo $item['spam'] ? \r
+                                               '<img alt="spam" border="0" src="' . $plugindirurl . 'silk/delete.png" />' : \r
+                                               '';?>\r
+                                       <?php echo $item['link'] ? \r
+                                               '' : \r
+                                               '<img alt="NOT Linked" border="0" src="' . $plugindirurl . 'silk/link_break.png" />';?>\r
+                                       <?php echo $item['excerpt'];?>\r
+                                       <em>(<?php echo $item['blog_name'];?>)</em>\r
+                                       ]]>\r
+                               </td>\r
+                               <td></td>\r
+                       </tr>\r
+                       <?php endwhile; ?>\r
+               </rows> \r
+       </response>\r
+</ajax-response>\r
diff --git a/NP_TrackBack/trunk/trackback/japanese-euc.templates/response_doblock.xml b/NP_TrackBack/trunk/trackback/japanese-euc.templates/response_doblock.xml
new file mode 100644 (file)
index 0000000..fdceb49
--- /dev/null
@@ -0,0 +1,8 @@
+<?echo '<'.'?xml version="1.0" encoding="UTF-8"?'.'>';?>\r
+<?php global $manager; ?>\r
+\r
+<ajax-response>\r
+   <response type="element" id="message">\r
+               <?php echo $message; ?>\r
+   </response>\r
+</ajax-response>
\ No newline at end of file
diff --git a/NP_TrackBack/trunk/trackback/japanese-euc.templates/response_dodelete.xml b/NP_TrackBack/trunk/trackback/japanese-euc.templates/response_dodelete.xml
new file mode 100644 (file)
index 0000000..fdceb49
--- /dev/null
@@ -0,0 +1,8 @@
+<?echo '<'.'?xml version="1.0" encoding="UTF-8"?'.'>';?>\r
+<?php global $manager; ?>\r
+\r
+<ajax-response>\r
+   <response type="element" id="message">\r
+               <?php echo $message; ?>\r
+   </response>\r
+</ajax-response>
\ No newline at end of file
diff --git a/NP_TrackBack/trunk/trackback/japanese-euc.templates/response_dounblock.xml b/NP_TrackBack/trunk/trackback/japanese-euc.templates/response_dounblock.xml
new file mode 100644 (file)
index 0000000..fdceb49
--- /dev/null
@@ -0,0 +1,8 @@
+<?echo '<'.'?xml version="1.0" encoding="UTF-8"?'.'>';?>\r
+<?php global $manager; ?>\r
+\r
+<ajax-response>\r
+   <response type="element" id="message">\r
+               <?php echo $message; ?>\r
+   </response>\r
+</ajax-response>
\ No newline at end of file
diff --git a/NP_TrackBack/trunk/trackback/japanese-euc.templates/updatetable.html b/NP_TrackBack/trunk/trackback/japanese-euc.templates/updatetable.html
new file mode 100644 (file)
index 0000000..7435a03
--- /dev/null
@@ -0,0 +1,13 @@
+<?php global $manager; ?>\r
+<blockquote style="color: red;border:1px solid red;padding:1em;"><b>¥¢¥Ã¥×¥Ç¡¼¥È¤¬É¬ÍפǤ¹:</b><br />\r
+¤³¤Î¥Ð¡¼¥¸¥ç¥ó¤Ç±¿ÍѤ¹¤ë¤¿¤á¤Ë¤ÏDBÆâ¤Î¥Æ¡¼¥Ö¥ë¤Î¥¢¥Ã¥×¥Ç¡¼¥È¤¬É¬ÍפǤ¹¡£<br />\r
+º£¤Þ¤Ç¤Î¥Ç¡¼¥¿¤¬ºï½ü¤µ¤ì¤ë¤³¤È¤Ï¤¢¤ê¤Þ¤»¤ó¡£\r
+²¼¤Î¥¢¥Ã¥×¥Ç¡¼¥È¥Ü¥¿¥ó¤ò²¡¤·¤Æ¤¯¤À¤µ¤¤¡£\r
+\r
+                       <form method="post"><div>\r
+                               <input type="hidden" name="action" value="tableUpgrade" />\r
+                               <input type="submit" tabindex="10" value="upgrade table" />\r
+                               <?php $manager->addTicketHidden(); ?>\r
+                       </div></form>\r
+</blockquote>\r
+       \r
diff --git a/NP_TrackBack/trunk/trackback/japanese-euc.templates/updatetablefinished.html b/NP_TrackBack/trunk/trackback/japanese-euc.templates/updatetablefinished.html
new file mode 100644 (file)
index 0000000..a16df97
--- /dev/null
@@ -0,0 +1,5 @@
+<?php global $manager; ?>\r
+<blockquote style="color: red;border:1px solid red;padding:1em;">\r
+¥Æ¡¼¥Ö¥ë¤Î¥¢¥Ã¥×¥Ç¡¼¥È¤Ï´°Î»¤·¤Þ¤·¤¿¡£\r
+</blockquote>\r
+       \r
diff --git a/NP_TrackBack/trunk/trackback/japanese-utf8.help.html b/NP_TrackBack/trunk/trackback/japanese-utf8.help.html
new file mode 100644 (file)
index 0000000..752f956
--- /dev/null
@@ -0,0 +1,116 @@
+<h2>ヘルプ</h2>
+
+
+<h3>スキンへの記述例</h3>
+<p>
+Nucleus CMS Japan Wikiの<a href="http://japan.nucleuscms.org/wiki/plugins:trackback">NP_TrackBackのページ</a>を参照してください。
+</p>
+
+<h3>表示のカスタマイズ</h3>
+<p>
+       トラックバック関連の表示方法はテンプレートを編集することによってカスタマイズできますが、色や余白などのデザインはCSS側で指定する必要があります。テンプレート内に入力したクラス名のプロパティはcssで指定してください。デフォルトのテンプレートを飾るCSSプロパティの例をあげておきます。
+</p>
+
+<pre>div.tb {
+       border: 1px solid #000; background: #FFF;
+}
+div.tb div.head {
+       padding: 4px;
+       background: #000; color: #FFF;
+       font-weight: bold; text-transform: lowercase; letter-spacing: 0.6em;
+}
+div.tb div.empty {
+       padding: 4px;
+       font-size: 95%;
+}
+div.tb div.item {
+       padding: 4px;
+}
+div.tb div.item div.name {
+       margin-bottom: 8px;
+       font-size: 120%; font-weight: bold;
+}
+div.tb div.item div.body {
+       font-size: 95%;
+}
+div.tb div.item div.body a {
+       font-weight: bold;
+}
+div.tb div.item div.date {
+       margin-bottom: 8px;
+       color: #888;
+       font-size: 85%; text-align: right;
+}
+div.tb div.info {
+       padding: 4px;
+       color: #FFF; background: #888;
+       font-size: 85%; font-style: italic;
+}</pre>
+
+<h3>日本語版更新履歴</h3>
+
+<ul>
+       <li>Version 2.0.3jp13 : (2008/12/14)</li>
+       <li> [Fixed] Pingフォームへのリンクを開こうとするとInvalid or expired ticket.になる問題を修正</li>
+       <li> [Fixed] TrackBack送信時のエラーハンドリングを改良した</li>
+       <li> [Fixed] TrackBackの文字コードの検出が正しく行われない場合がある問題を修正</li>
+       
+       <li>Version 2.0.3jp12 : (2008/01/12)</li>
+       <li> [Fixed] エラーメッセージの文字コードが適切でなかった問題を修正</li>
+       <li> [Fixed] トラックバックの送信文字コードをUTF-8固定にした</li>
+       <li> [Fixed] トラックバック可/不可の判定がおかしかった問題を修正</li>
+       
+       <li>Version 2.0.3jp11 : (2007/09/30)</li>
+       <li> [Added] SuperAdmin以外でもTrackBackが管理できるようにした</li>
+       
+       <li>Version 2.0.3jp10 : (2007/06/30)</li>
+       <li> [Fixed] mysql_query()をsql_query()に変更</li>
+       <li> [Changed] 実体参照テーブルについてNucleus標準ものを使うようにした</li>
+       <li> [Changed] インストールできるバージョンを3.3以降した</li>
+       <li> [Changed] Ricoを2.0にアップデートしたのに伴い、管理画面の機能を増強</li>
+       <li> [Added] コメント部分でもテンプレート変数が使えるようにした</li>
+       <li> [Fixed] トラックバック送信部分の不具合を修正(FC2対策)</li>
+       <li> [Changed] UserAgentをオリジナル版にあわせて変更</li>
+       <li> [Changed] TrackBackのレスポンスの解析にXMLパーサーを使うようにした</li>
+       <li> [Fixed] 言及リンクチェックの不具合を修正</li>
+       <li> [Added] 保留にしているURLと同じURLのトラックバックを無視するようにした</li>
+       <!-- 10.1, 10.2, 10.3 -->
+       <li> [Fixed] 言及リンクチェックの用のURL生成ルーチンの不具合を修正</li>
+       <!-- 10.4 -->
+       <li> [Added] spamトラックバック一括消去時に確認が出るようにした</li>
+       
+       <li>Version 2.0.3jp9 : (2007/05/04)</li>
+       <li> [Added] doIf()を追加(Nucleus 3.3向け)</li>
+       <li> [Added] URLが無効なトラックバックを無視するようにした</li>
+       
+       <li>Version 2.0.3jp8 : (2007/03/18)</li>
+       <li> [Fixed] 管理画面でStoryのリンクの不具合を修正</li>
+       <li> [Changed] URL抽出ルーチンを改良</li>
+       <li> [Changed] 管理画面へのリンクを修正</li>
+       <li> [Changed] 管理画面のページングをAjax対応にした</li>
+       <li> [Changed] トラックバック通知アドレスをブログごとに設定できるようにした</li>
+       <li> [Fixed] phpのshort openタグを修正</li>
+       <li> [Fixed] 管理画面のトラックバック一覧で日付が正常に表示されない問題を修正</li>
+       
+       <li>Version 2.0.3jp7 : (2006/11/26)</li>
+       <li> [Changed] SpamChekについて微調整</li>
+       <li> [Added] Ticket処理を追加(CSRF対策)</li>
+       <li> [Fixed] URLに&amp;が入っているときの動作を変更</li>
+       <li> [Added] 管理画面にアイコンを追加</li>
+       
+       <li>Version 2.0.3jp6 : (2006/09/30)</li>
+       <li> [Fixed] セキュリティの向上</li>
+       
+       <li>Version 2.0.3jp5 : (2006/09/16)</li>
+       <li> [Fixed] getPermaLinksFromText()内のURL抽出ルーチンの不具合を修正</li>
+       <li> [Fixed] Auto-Discovery時にSQLエラーが出る不具合を修正</li>
+       <li> [Fixed] 管理画面でパースエラーが出る場合がある不具合を修正</li>
+       <li> [Changed] SQLのクォートを"から'に変更</li>
+       <li> [Fixed] mb_emulator環境にてエラーが出る問題を修正</li>
+       <li> [Fixed] curlが有効な環境でエラーが出る問題を修正</li>
+       
+       <li>Version 2.0.3jp4 : (2006/07/15)</li>
+       <li> [Added] AutoDiscoveryURL出力時にSpamCheckを行うようにした</li>
+       <li> [Added] メッセージ、デフォルト値を日本語化</li>
+       <li> [Added] 大手ASPの言及トラックバックチェックの際にURLのリダイレクトを解除するようにした</li>
+</ul>
\ No newline at end of file
diff --git a/NP_TrackBack/trunk/trackback/japanese-utf8.templates/all.html b/NP_TrackBack/trunk/trackback/japanese-utf8.templates/all.html
new file mode 100644 (file)
index 0000000..56c75b1
--- /dev/null
@@ -0,0 +1,106 @@
+<?php global $manager; ?>
+<h2>
+       All trackbacks
+       <?php if ($count > $amount): ?>
+               (Page <?php echo ceil($start / $amount) + 1;?> of <?php echo ceil($count / $amount);?>)
+       <?php endif; ?>
+</h2>
+
+<?php if(count($items)): ?>
+<?php if ($count > $amount): ?>
+<table class="navigation">
+       <tr>
+               <td style='padding: 0;'>
+                       <?php if ($start > 0): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="&lt;&lt; Previous" />       
+                                       <input type="hidden" name="action" value="all" />
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+               <td style='padding: 0; text-align: right;'>     
+                       <?php if ($start + $amount < $count): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="Next &gt; &gt;" />
+                                       <input type="hidden" name="action" value="all" />
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+       </tr>
+</table>
+<?php endif; ?>
+
+<table>
+       <thead>
+               <tr>
+                       <th>Date</th>
+                       <th>Story</th>
+                       <th>Title, Blog and Excerpt</th>
+                       <th colspan="2">Actions</th>
+               </tr>
+       </thead>
+       <tbody>
+               <?php while (list(,$item) = each ($items)): ?>
+               <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>
+                       <td>
+                               <?php echo str_replace(' ', '&nbsp;', date("Y-m-d @ H:i",$item['timestamp']));?>
+                       </td>
+                       <td>
+                               <a href="<?php echo $item['story_url']; ?>"><?php echo $item['story'];?></a>
+                       </td>
+                       <td>
+                               <a href="<?php echo $item['url'];?>"><img alt="Visit" border="0" src="<?php echo $plugindirurl?>silk/house_go.png" /></a>
+                               <strong><?php echo $item['title'];?></strong> 
+                               <em>(<?php echo $item['blog_name'];?>)</em><br />
+                               <?php echo $item['excerpt'];?>
+                       </td>
+                       <td>
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=block&tb='.$item['id'].'&next=all&start='.$start),ENT_QUOTES);?>"><img alt="Block" border="0" src="<?php echo $plugindirurl?>silk/delete.png" /></a>
+                       </td>
+                       <td>
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=delete&tb='.$item['id'].'&next=all&start='.$start),ENT_QUOTES);?>"><img alt="Delete" border="0" src="<?php echo $plugindirurl?>silk/cross.png" /></a>
+                       </td>
+               </tr>
+               <?php endwhile; ?>
+       </tbody>
+</table>
+
+<?php if ($count > $amount): ?>
+<table class="navigation">
+       <tr>
+               <td style='padding: 0;'>
+                       <?php if ($start > 0): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="&lt;&lt; Previous" />       
+                                       <input type="hidden" name="action" value="all" />
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+               <td style='padding: 0; text-align: right;'>     
+                       <?php if ($start + $amount < $count): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="Next &gt; &gt;" />
+                                       <input type="hidden" name="action" value="all" />
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+       </tr>
+</table>
+<?php endif; ?>
+<?php endif; ?>
\ No newline at end of file
diff --git a/NP_TrackBack/trunk/trackback/japanese-utf8.templates/all_ajax.html b/NP_TrackBack/trunk/trackback/japanese-utf8.templates/all_ajax.html
new file mode 100644 (file)
index 0000000..38788e4
--- /dev/null
@@ -0,0 +1,130 @@
+<?php global $manager; ?>
+<h2>
+       All trackbacks
+</h2>
+
+<div id="message" style="color: red;"></div>
+
+<div style="width: 95%">
+<span id="tb_grid_bookmark"></span>
+
+<table id="tb_grid" style="border:0; margin:0;">
+       <colgroup>
+               <col style="width:25px;" />
+               <col style="width:40px;" />
+               <col style="width:70px;" />
+               <col style="width:150px;" />
+               <col style="width:200px;"/>
+               <col style="width:25px;" />
+       </colgroup>
+       <thead>
+               <tr>
+                       <th>&#160;</th>
+                       <th>id</th>
+                       <th>Date</th>
+                       <th>Story</th>
+                       <th>Title, Blog and Excerpt</th>
+                       <th>&#160;</th>
+               </tr>
+       </thead>
+</table>
+
+上記で選択したトラックバックを一括して処理します
+<a href="javascript:doDelete()" onclick=""><img alt="Delete" border="0" src="<?php echo $plugindirurl?>silk/cross.png" /></a>
+<a href="javascript:doBlock()" onclick=""><img alt="Block" border="0" src="<?php echo $plugindirurl?>silk/delete.png" /></a>
+</div>
+
+<!--
+<textarea id='tb_grid_debugmsgs' rows='5' cols='80' style='font-size:smaller;'></textarea>
+-->
+
+<script type="text/javascript">
+//<![CDATA[
+       Rico.loadModule('LiveGridAjax');
+       Rico.loadModule('LiveGridMenu');
+       Rico.include('translations/livegrid_ja.js');
+       Rico.include('ricoAjaxEngine.js');
+       
+       Rico.onLoad( function() {
+               var params = [
+                       'action=ajax',
+                       'type=all',
+                       'ticket=<?php echo $ticket ;?>'
+               ]; 
+               
+               var cb = new Rico.TableColumn.checkbox('1','0');
+               var colspec = [
+                       {canHide:false, type:'control', control:cb, ClassName:'aligncenter'},
+                       {type:'raw'},
+                       {type:'raw'},
+                       ,
+                       ,
+                       ,
+               ];
+               
+               var opts = {
+                       saveColumnInfo   : {width:true, filter:false, sort:false}, 
+                       menuEvent       : 'none',
+                       frozenColumns   : 2,
+                       canSortDefault  : false,
+                       canHideDefault  : true,
+                       allowColResize  : true,
+                       canFilterDefault: false,
+                       highlightElem   : 'none',
+                       columnSpecs     : colspec
+               };
+               
+               buffer = new Rico.Buffer.AjaxSQL('<?php echo $CONF['PluginURL'].'trackback/';?>grid.php',
+                               {TimeOut:10, requestParameters:params, sortParmFmt: 'displayName'}
+               );
+               orderGrid=new Rico.LiveGrid ('tb_grid', buffer, opts);
+               orderGrid.menu=new Rico.GridMenu({});
+               
+               // ajaxEngine
+               ajaxEngine = new Rico.AjaxEngine;
+               ajaxEngine.registerRequest('updateData', '<?php echo $CONF['PluginURL'].'trackback/';?>grid.php' );
+               ajaxEngine.registerAjaxElement('message');
+       });
+
+       function checkUpdateIds(){
+               var updateIds = [];
+               Rico.writeDebugMsg('check updated rows');
+               for(var i = 0; i < buffer.size; i++){
+                       row = buffer.rows[i];
+                       if( row[0].content && row[0].content == '1' ){
+                               updateIds.push(row[1].content);
+                               Rico.writeDebugMsg('id: '+row[1].content+' updated');
+                       }
+               }
+               return updateIds;
+       }
+       
+       function doBlock(){
+               var ids = checkUpdateIds();
+               if( !(ids.length && ids.length > 0) ) return ;
+               var params = [
+                       'action=doblock',
+                       'ticket=<?php echo $ticket ;?>',
+                       'ids='+ids.join(',')
+               ]; 
+               ajaxEngine.sendRequest('updateData', {parameters: ajaxEngine._createQueryString(params, 0)});
+               orderGrid.resetContents('tb_grid');
+               buffer.fetch(-1);
+       }
+       
+       function doDelete(){
+               var ids = checkUpdateIds();
+               if( !(ids.length && ids.length > 0) ) return ;
+               if( !confirm('本当に削除しますか?') ) return ;
+               
+               var params = [
+                       'action=dodelete',
+                       'ticket=<?php echo $ticket ;?>',
+                       'ids='+ids.join(',')
+               ];
+               ajaxEngine.sendRequest('updateData', {parameters: ajaxEngine._createQueryString(params, 0)});
+               orderGrid.resetContents('tb_grid');
+               buffer.fetch(-1);
+       }
+//]]>
+</script>
diff --git a/NP_TrackBack/trunk/trackback/japanese-utf8.templates/blocked.html b/NP_TrackBack/trunk/trackback/japanese-utf8.templates/blocked.html
new file mode 100644 (file)
index 0000000..b6d553b
--- /dev/null
@@ -0,0 +1,119 @@
+<?php global $manager; ?>
+<h2>
+       ブロックされたトラックバック
+       <?php if ($count > $amount): ?>
+               (Page <?php echo ceil($start / $amount) + 1;?> of <?php echo ceil($count / $amount);?>)
+       <?php endif; ?>
+</h2>
+
+<ul>
+       <li><a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=blocked_clear&next=blocked'),ENT_QUOTES); ?>" onClick="return confirm('ブロックされたトラックバックをクリアしてもよろしいですか?');">ブロックされたトラックバックのクリア</a></li>
+       <li><a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=blocked_spamclear&next=blocked'),ENT_QUOTES); ?>" onClick="return confirm('spam判定されたトラックバックをクリアしてもよろしいですか?');">spam判定されたトラックバックのクリア</a></li> 
+</ul>
+
+<?php if(count($items)): ?>
+<?php if ($count > $amount): ?>
+<table class="navigation">
+       <tr>
+               <td style='padding: 0;'>
+                       <?php if ($start > 0): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="&lt;&lt; Previous" />       
+                                       <input type="hidden" name="action" value="blocked" />
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+               <td style='padding: 0; text-align: right;'>     
+                       <?php if ($start + $amount < $count): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="Next &gt; &gt;" />
+                                       <input type="hidden" name="action" value="blocked" />
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+       </tr>
+</table>
+<?php endif; ?>
+
+<table>
+       <thead>
+               <tr>
+                       <th>Date</th>
+                       <th>Story</th>
+                       <th>Title, Blog and Excerpt</th>
+                       <th colspan="2">Actions</th>
+               </tr>
+       </thead>
+       <tbody>
+               <?php while (list(,$item) = each ($items)): ?>
+               <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>
+                       <td>
+                               <?php echo str_replace(' ', '&nbsp;', date("Y-m-d @ H:i",$item['timestamp']));?>
+                       </td>
+                       <td>
+                               <a href="<?php echo $item['story_url']; ?>"><?php echo $item['story'];?></a>
+                       </td>
+                       <td>
+                               <a href="<?php echo $item['url'];?>"><img alt="Visit" border="0" src="<?php echo $plugindirurl;?>silk/house_go.png" /></a>
+                               <strong><?php echo $item['title'];?></strong> 
+                               <em>(<?php echo $item['blog_name'];?>)</em>
+                               <?php echo $item['spam'] ? 
+                                       '<img alt="spam" border="0" src="' . $plugindirurl . 'silk/delete.png" />' : 
+                                       '';?>
+                               <?php echo $item['link'] ? 
+                                       '' : 
+                                       '<img alt="NOT Linked" border="0" src="' . $plugindirurl . 'silk/link_break.png" />';?>
+                               <br />
+                               <?php echo $item['excerpt'];?>
+                       </td>
+                       <td>
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=unblock&tb='.$item['id'].'&next=blocked&start='.$start),ENT_QUOTES);?>"><img alt="Unblock" border="0" src="<?php echo $plugindirurl;?>silk/accept.png" /></a>
+                       </td>
+                       <td>
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=delete&tb='.$item['id'].'&next=blocked&start='.$start),ENT_QUOTES);?>"><img alt="Delete" border="0" src="<?php echo $plugindirurl;?>silk/cross.png" /></a>
+                       </td>
+               </tr>
+               <?php endwhile; ?>
+       </tbody>
+</table>
+
+<?php if ($count > $amount): ?>
+<table class="navigation">
+       <tr>
+               <td style='padding: 0;'>
+                       <?php if ($start > 0): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="&lt;&lt; Previous" />       
+                                       <input type="hidden" name="action" value="blocked" />
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+               <td style='padding: 0; text-align: right;'>     
+                       <?php if ($start + $amount < $count): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="Next &gt; &gt;" />
+                                       <input type="hidden" name="action" value="blocked" />
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+       </tr>
+</table>
+<?php endif; ?>
+<?php endif; ?>
+
diff --git a/NP_TrackBack/trunk/trackback/japanese-utf8.templates/blocked_ajax.html b/NP_TrackBack/trunk/trackback/japanese-utf8.templates/blocked_ajax.html
new file mode 100644 (file)
index 0000000..7474160
--- /dev/null
@@ -0,0 +1,134 @@
+<?php global $manager; ?>
+<h2>
+       ブロックされたトラックバック
+</h2>
+
+<ul>
+       <li><a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=blocked_clear&next=blocked'),ENT_QUOTES); ?>" onClick="return confirm('ブロックされたトラックバックをクリアしてもよろしいですか?');">ブロックされたトラックバックのクリア</a></li>
+       <li><a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=blocked_spamclear&next=blocked'),ENT_QUOTES); ?>" onClick="return confirm('spam判定されたトラックバックをクリアしてもよろしいですか?');">spam判定されたトラックバックのクリア</a></li> 
+</ul>
+
+<div id="message" style="color: red;"></div>
+
+<div style="width: 95%">
+<span id="tb_grid_bookmark"></span>
+
+<table id="tb_grid" style="border:0; margin:0;">
+       <colgroup>
+               <col style="width:25px;" />
+               <col style="width:40px;" />
+               <col style="width:70px;" />
+               <col style="width:150px;" />
+               <col style="width:200px;"/>
+               <col style="width:25px;" />
+       </colgroup>
+       <thead>
+               <tr>
+                       <th>&#160;</th>
+                       <th>id</th>
+                       <th>Date</th>
+                       <th>Story</th>
+                       <th>Title, Blog and Excerpt</th>
+                       <th>&#160;</th>
+               </tr>
+       </thead>
+</table>
+上記で選択したトラックバックを一括して処理します
+<a href="javascript:doUnBlock()" onclick=""><img alt="Unblock" border="0" src="<?php echo $plugindirurl;?>silk/accept.png" /></a>
+<a href="javascript:doDelete()" onclick=""><img alt="Delete" border="0" src="<?php echo $plugindirurl?>silk/cross.png" /></a>
+</div>
+
+<!--
+<textarea id='tb_grid_debugmsgs' rows='5' cols='80' style='font-size:smaller;'></textarea>
+-->
+
+<script type="text/javascript">
+//<![CDATA[
+       Rico.loadModule('LiveGridAjax');
+       Rico.loadModule('LiveGridMenu');
+       Rico.include('translations/livegrid_ja.js');
+       Rico.include('ricoAjaxEngine.js');
+       
+       Rico.onLoad( function() {
+               var params = [
+                       'action=ajax',
+                       'type=blocked',
+                       'ticket=<?php echo $ticket ;?>'
+               ]; 
+               
+               var cb = new Rico.TableColumn.checkbox('1','0');
+               var colspec = [
+                       {canHide:false, type:'control', control:cb, ClassName:'aligncenter'},
+                       {type:'raw'},
+                       {type:'raw'},
+                       ,
+                       ,
+                       ,
+               ];
+               
+               var opts = {
+                       saveColumnInfo   : {width:true, filter:false, sort:false}, 
+                       menuEvent       : 'none',
+                       frozenColumns   : 2,
+                       canSortDefault  : false,
+                       canHideDefault  : true,
+                       allowColResize  : true,
+                       canFilterDefault: false,
+                       highlightElem   : 'none',
+                       columnSpecs     : colspec
+               };
+               
+               buffer = new Rico.Buffer.AjaxSQL('<?php echo $CONF['PluginURL'].'trackback/';?>grid.php',
+                               {TimeOut:10, requestParameters:params, sortParmFmt: 'displayName'}
+               );
+               orderGrid=new Rico.LiveGrid ('tb_grid', buffer, opts);
+               orderGrid.menu=new Rico.GridMenu({});
+               
+               // ajaxEngine
+               ajaxEngine = new Rico.AjaxEngine;
+               ajaxEngine.registerRequest('updateData', '<?php echo $CONF['PluginURL'].'trackback/';?>grid.php' );
+               ajaxEngine.registerAjaxElement('message');
+       });
+
+       function checkUpdateIds(){
+               var updateIds = [];
+               Rico.writeDebugMsg('check updated rows');
+               for(var i = 0; i < buffer.size; i++){
+                       row = buffer.rows[i];
+                       if( row[0].content && row[0].content == '1' ){
+                               updateIds.push(row[1].content);
+                               Rico.writeDebugMsg('id: '+row[1].content+' updated');
+                       }
+               }
+               return updateIds;
+       }
+       
+       function doUnBlock(){
+               var ids = checkUpdateIds();
+               if( !(ids.length && ids.length > 0) ) return ;
+               var params = [
+                       'action=dounblock',
+                       'ticket=<?php echo $ticket ;?>',
+                       'ids='+ids.join(',')
+               ]; 
+               ajaxEngine.sendRequest('updateData', {parameters: ajaxEngine._createQueryString(params, 0)});
+               orderGrid.resetContents('tb_grid');
+               buffer.fetch(-1);
+       }
+       
+       function doDelete(){
+               var ids = checkUpdateIds();
+               if( !(ids.length && ids.length > 0) ) return ;
+               if( !confirm('本当に削除しますか?') ) return ;
+               
+               var params = [
+                       'action=dodelete',
+                       'ticket=<?php echo $ticket ;?>',
+                       'ids='+ids.join(',')
+               ];
+               ajaxEngine.sendRequest('updateData', {parameters: ajaxEngine._createQueryString(params, 0)});
+               orderGrid.resetContents('tb_grid');
+               buffer.fetch(-1);
+       }
+//]]>
+</script>
diff --git a/NP_TrackBack/trunk/trackback/japanese-utf8.templates/form.html b/NP_TrackBack/trunk/trackback/japanese-utf8.templates/form.html
new file mode 100644 (file)
index 0000000..add813a
--- /dev/null
@@ -0,0 +1,56 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+       <head>
+               <title>TrackBackの手動送信</title>
+               <link rel="stylesheet" type="text/css" href="<?php echo $CONF['AdminURL']?>styles/bookmarklet.css" />
+       </head>
+       
+       <body>
+               <h1>TrackBackの手動送信</h1>
+<?php if ($success): ?>
+               <p>
+                       <strong>トラックバックの送信が完了しました</strong>
+               </p>
+<?php endif; ?>
+<?php if ($error): ?>
+               <p>
+                       <strong><?php echo $status; ?></strong>
+               </p>
+<?php endif; ?>
+<?php if ($form): ?>           
+               <form method="post" action="<?php echo $CONF['ActionURL'] ?>">
+               
+               <div>
+                       <input type="hidden" name="tb_id" value="<?php echo $itemid;?>" />
+                       <input type="hidden" name="action" value="plugin" />
+                       <input type="hidden" name="name" value="TrackBack" />
+                       <input type="hidden" name="type" value="ping" />
+                       
+                       <table>
+                               <tr>
+                                       <td>あなたの記事のurl</td>
+                                       <td><input type="text" value="" name="url" size="60" /></td>
+                               </tr>
+                               <tr>
+                                       <td>記事のタイトル</td>
+                                       <td><input type="text" value="" name="title" size="60" /></td>
+                               </tr>
+                               <tr>
+                                       <td>記事の要約文</td>
+                                       <td><textarea name="excerpt" cols="40" rows="5"></textarea></td>
+                               </tr>
+                               <tr>
+                                       <td>あなたのblogの名前</td>
+                                       <td><input type="text" value="" name="blog_name" size="60" /></td>
+                               </tr>
+                               <tr>
+                                       <td>トラックバック送信</td>
+                                       <td><input type="submit" value="トラックバックを送信する" /></td>
+                               </tr>
+                       </table>
+               </div>
+               
+               </form>
+<?php endif; ?>
+       </body>
+</html>
\ No newline at end of file
diff --git a/NP_TrackBack/trunk/trackback/japanese-utf8.templates/index.html b/NP_TrackBack/trunk/trackback/japanese-utf8.templates/index.html
new file mode 100644 (file)
index 0000000..15a2ce4
--- /dev/null
@@ -0,0 +1,34 @@
+<?php global $manager; ?>
+<h2>Overview of all items</h2>
+
+<?php if(count($blogs)): ?>
+
+<table>
+<?php while (list(,$blog) = each ($blogs)): ?>
+<?php if(count($blog['items'])): ?>
+       <thead>
+               <tr>
+                       <th>Blog: <?php echo htmlspecialchars($blog['bname']);?></th>
+                       <th>Total</th>
+                       <th>Action</th>
+               </tr>
+       </thead>
+       <tbody>
+               <?php while (list(,$item) = each ($blog['items'])): ?>
+               <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>
+                       <td>
+                               <?php echo $item['ititle'];?>
+                       </td>
+                       <td>
+                               <?php echo htmlspecialchars($item['total']);?>
+                       </td>
+                       <td>
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=list&id='.$item['inumber']),ENT_QUOTES);?>">Trackbacks</a>
+                       </td>
+               </tr>
+               <?php endwhile; ?>
+       </tbody>
+<?php endif; ?>
+<?php endwhile; ?>
+</table>
+<?php endif; ?>
\ No newline at end of file
diff --git a/NP_TrackBack/trunk/trackback/japanese-utf8.templates/list.html b/NP_TrackBack/trunk/trackback/japanese-utf8.templates/list.html
new file mode 100644 (file)
index 0000000..0fcf3c3
--- /dev/null
@@ -0,0 +1,107 @@
+<?php global $manager; ?>
+<h2>
+       All trackbacks for &quot;<?php echo $story['title'];?>&quot;
+       <?php if ($count > $amount): ?>
+               (Page <?php echo ceil($start / $amount) + 1;?> of <?php echo ceil($count / $amount);?>)
+       <?php endif; ?>
+</h2>
+
+<?php if(count($items)): ?>
+<?php if ($count > $amount): ?>
+<table class="navigation">
+       <tr>
+               <td style='padding: 0;'>
+                       <?php if ($start > 0): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="&lt;&lt; Previous" />       
+                                       <input type="hidden" name="action" value="list" />
+                                       <input type="hidden" name="id" value="<?php echo $story['id'];?>" />
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+               <td style='padding: 0; text-align: right;'>     
+                       <?php if ($start + $amount < $count): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="Next &gt; &gt;" />
+                                       <input type="hidden" name="action" value="list" />
+                                       <input type="hidden" name="id" value="<?php echo $story['id'];?>" />
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+       </tr>
+</table>
+<?php endif; ?>
+
+<table>
+       <thead>
+               <tr>
+                       <th>Date</th>
+                       <th>Title, Blog and Excerpt</th>
+                       <th colspan="2">Actions</th>
+               </tr>
+       </thead>
+       <tbody>
+               <?php while (list(,$item) = each ($items)): ?>
+               <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>
+                       <td>
+                               <?php echo str_replace(' ', '&nbsp;', date("Y-m-d @ H:i",$item['timestamp']));?>
+                       </td>
+                       <td>
+                               <a href="<?php echo $item['url'];?>"><img alt="Visit" border="0" src="<?php echo $plugindirurl?>silk/house_go.png" /></a>
+                               <strong><?php echo $item['title'];?></strong> 
+                               <em>(<?php echo $item['blog_name'];?>)</em><br />
+                               <?php echo $item['excerpt'];?>
+                       </td>
+                       <td>
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=block&tb='.$item['id'].'&next=list&id='.$story['id'].'&start='.$start),ENT_QUOTES);?>"><img alt="Block" border="0" src="<?php echo $plugindirurl?>silk/delete.png" /></a>
+                       </td>
+                       <td>
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=delete&tb='.$item['id'].'&next=list&id='.$story['id'].'&start='.$start),ENT_QUOTES);?>"><img alt="Delete" border="0" src="<?php echo $plugindirurl?>silk/cross.png" /></a>
+                       </td>
+               </tr>
+               <?php endwhile; ?>
+       </tbody>
+</table>
+
+<?php if ($count > $amount): ?>
+<table class="navigation">
+       <tr>
+               <td style='padding: 0;'>
+                       <?php if ($start > 0): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="&lt;&lt; Previous" />       
+                                       <input type="hidden" name="action" value="list" />
+                                       <input type="hidden" name="id" value="<?php echo $story['id'];?>" />
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+               <td style='padding: 0; text-align: right;'>     
+                       <?php if ($start + $amount < $count): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="Next &gt; &gt;" />
+                                       <input type="hidden" name="action" value="list" />
+                                       <input type="hidden" name="id" value="<?php echo $story['id'];?>" />
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+       </tr>
+</table>
+<?php endif; ?>
+<?php endif; ?>
+
diff --git a/NP_TrackBack/trunk/trackback/japanese-utf8.templates/menu.html b/NP_TrackBack/trunk/trackback/japanese-utf8.templates/menu.html
new file mode 100644 (file)
index 0000000..ac37024
--- /dev/null
@@ -0,0 +1,32 @@
+<?php global $manager?>
+<h2>Trackback</h2>
+
+<script type="text/javascript" src="<?php echo $CONF['PluginURL']?>trackback/js/prototype.js"></script>
+<script type="text/javascript" src="<?php echo $CONF['PluginURL']?>trackback/js/rico/rico.js"></script>
+
+<ul>
+       <li>
+               <img border="0" src="<?php echo $plugindirurl?>silk/application_view_list.png" />
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=index'),ENT_QUOTES);?>">Overview of all items</a>
+       </li>
+       <li>
+               <img border="0" src="<?php echo $plugindirurl?>silk/tick.png" />
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=all'),ENT_QUOTES);?>">トラックバックの全データ</a>
+       </li>
+       <li>
+               <img border="0" src="<?php echo $plugindirurl?>silk/exclamation.png" />
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=blocked'),ENT_QUOTES);?>">ブロックされたトラックバック</a>
+       </li> 
+       <li>
+               <img border="0" src="<?php echo $plugindirurl?>silk/transmit_go.png" />
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=ping'),ENT_QUOTES);?>">手動ping</a>
+       </li>
+       <li>
+               <img border="0" src="<?php echo $plugindirurl?>silk/help.png" />
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=help'),ENT_QUOTES);?>">ヘルプ</a>
+       </li>
+    <li>
+               <img border="0" src="<?php echo $plugindirurl?>silk/plugin_edit.png" />
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['AdminURL'].'index.php?action=pluginoptions&plugid='.$plugid),ENT_QUOTES);?>">プラグインオプション設定</a>
+       </li>
+</ul>
diff --git a/NP_TrackBack/trunk/trackback/japanese-utf8.templates/ping.html b/NP_TrackBack/trunk/trackback/japanese-utf8.templates/ping.html
new file mode 100644 (file)
index 0000000..84a882c
--- /dev/null
@@ -0,0 +1,50 @@
+<?php global $manager; ?>
+<h2>手動pingフォーム</h2>
+
+<form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+
+    <input type="hidden" name="action" value="sendping" />
+    <input type="hidden" name="next" value="ping" />
+    <?php $manager->addTicketHidden(); ?>
+       
+    <table>
+        <tr>
+            <th colspan='2'>手動ping</th>
+        </tr>
+        <tr>
+            <td>あなたのurl</td>
+            <td>
+                <input type="text" name="url" size="60" value="<?php echo htmlspecialchars($item['url']);?>" />
+            </td>
+        </tr>
+        <tr>
+            <td>記事のタイトル</td>
+            <td>
+                <input type="text" name="title" size="60" value="<?php echo htmlspecialchars($item['title']);?>" />
+            </td>
+        </tr>
+        <tr>
+            <td>記事の要約文</td>
+            <td>
+                    <textarea name="excerpt" cols="40" rows="5"><?php echo $item['excerpt'];?></textarea>
+            </td>
+        </tr>
+        <tr>
+            <td>Blog名</td>
+            <td>
+                <input type="text" name="blog_name" size="60" value="<?php echo htmlspecialchars($item['blogname']);?>" />
+            </td>
+        </tr>
+        <tr>
+            <td>ping先url</td>
+            <td>
+                <input type="text" value="" name="ping_url" size="60" />
+            </td>
+        </tr>
+        <tr>
+            <td>送信</td>
+            <td><input type="submit" value="送信" /></td>
+        </tr>
+    </table>
+
+</form>
\ No newline at end of file
diff --git a/NP_TrackBack/trunk/trackback/japanese-utf8.templates/response_all.xml b/NP_TrackBack/trunk/trackback/japanese-utf8.templates/response_all.xml
new file mode 100644 (file)
index 0000000..4ae2445
--- /dev/null
@@ -0,0 +1,35 @@
+<?php echo '<'.'?xml version="1.0" encoding="UTF-8"?'.'>';?>\r
+<?php global $manager; ?>\r
+\r
+<ajax-response>\r
+       <response type="object" id="tb_grid_updater">\r
+               <rowcount><?php echo $count; ?></rowcount>\r
+               <rows update_ui="true" offset="<?php echo $start; ?>" >\r
+                       <?php while (list(,$item) = each ($items)): ?>\r
+                       <tr>\r
+                               <td>0</td>\r
+                               <td><?php echo $item['id'];?></td>\r
+                               <td>\r
+                                       <?php echo date("Y-m-d H:i:s",$item['timestamp']);?>\r
+                               </td>\r
+                               <td>\r
+                                       <![CDATA[\r
+                                       <a href="<?php echo $item['story_url']; ?>"><?php echo $item['story'];?></a>\r
+                                       ]]>\r
+                               </td>\r
+                               <td>\r
+                                       <![CDATA[\r
+                                       <a href="<?php echo $item['url'];?>">\r
+                                               <img alt="Visit" border="0" src="<?php echo $plugindirurl?>silk/house_go.png" />\r
+                                       </a>\r
+                                       <strong><?php echo $item['title'];?></strong>\r
+                                       <?php echo $item['excerpt'];?>\r
+                                       <em>(<?php echo $item['blog_name'];?>)</em>\r
+                                       ]]>\r
+                               </td>\r
+                               <td></td>\r
+                       </tr>\r
+                       <?php endwhile; ?>\r
+               </rows> \r
+       </response>\r
+</ajax-response>\r
diff --git a/NP_TrackBack/trunk/trackback/japanese-utf8.templates/response_blocked.xml b/NP_TrackBack/trunk/trackback/japanese-utf8.templates/response_blocked.xml
new file mode 100644 (file)
index 0000000..29faac9
--- /dev/null
@@ -0,0 +1,41 @@
+<?php echo '<'.'?xml version="1.0" encoding="UTF-8"?'.'>';?>\r
+<?php global $manager; ?>\r
+\r
+<ajax-response>\r
+       <response type="object" id='tb_grid_updater'>\r
+               <rowcount><?php echo $count; ?></rowcount>\r
+               <rows update_ui='true' >\r
+                       <?php while (list(,$item) = each ($items)): ?>\r
+                       <tr>\r
+                               <td>0</td>\r
+                               <td><?php echo $item['id'];?></td>\r
+                               <td>\r
+                                       <?php echo date("Y-m-d H:i:s",$item['timestamp']);?>\r
+                               </td>\r
+                               <td>\r
+                                       <![CDATA[\r
+                                       <a href="<?php echo $item['story_url']; ?>"><?php echo $item['story'];?></a>\r
+                                       ]]>\r
+                               </td>\r
+                               <td>\r
+                                       <![CDATA[\r
+                                       <a href="<?php echo $item['url'];?>">\r
+                                               <img alt="Visit" border="0" src="<?php echo $plugindirurl?>silk/house_go.png" />\r
+                                       </a>\r
+                                       <strong><?php echo $item['title'];?></strong>\r
+                                       <?php echo $item['spam'] ? \r
+                                               '<img alt="spam" border="0" src="' . $plugindirurl . 'silk/delete.png" />' : \r
+                                               '';?>\r
+                                       <?php echo $item['link'] ? \r
+                                               '' : \r
+                                               '<img alt="NOT Linked" border="0" src="' . $plugindirurl . 'silk/link_break.png" />';?>\r
+                                       <?php echo $item['excerpt'];?>\r
+                                       <em>(<?php echo $item['blog_name'];?>)</em>\r
+                                       ]]>\r
+                               </td>\r
+                               <td></td>\r
+                       </tr>\r
+                       <?php endwhile; ?>\r
+               </rows> \r
+       </response>\r
+</ajax-response>\r
diff --git a/NP_TrackBack/trunk/trackback/japanese-utf8.templates/response_doblock.xml b/NP_TrackBack/trunk/trackback/japanese-utf8.templates/response_doblock.xml
new file mode 100644 (file)
index 0000000..a53f37c
--- /dev/null
@@ -0,0 +1,8 @@
+<?echo '<'.'?xml version="1.0" encoding="UTF-8"?'.'>';?>
+<?php global $manager; ?>
+
+<ajax-response>
+   <response type="element" id="message">
+               <?php echo $message; ?>
+   </response>
+</ajax-response>
\ No newline at end of file
diff --git a/NP_TrackBack/trunk/trackback/japanese-utf8.templates/response_dodelete.xml b/NP_TrackBack/trunk/trackback/japanese-utf8.templates/response_dodelete.xml
new file mode 100644 (file)
index 0000000..a53f37c
--- /dev/null
@@ -0,0 +1,8 @@
+<?echo '<'.'?xml version="1.0" encoding="UTF-8"?'.'>';?>
+<?php global $manager; ?>
+
+<ajax-response>
+   <response type="element" id="message">
+               <?php echo $message; ?>
+   </response>
+</ajax-response>
\ No newline at end of file
diff --git a/NP_TrackBack/trunk/trackback/japanese-utf8.templates/response_dounblock.xml b/NP_TrackBack/trunk/trackback/japanese-utf8.templates/response_dounblock.xml
new file mode 100644 (file)
index 0000000..a53f37c
--- /dev/null
@@ -0,0 +1,8 @@
+<?echo '<'.'?xml version="1.0" encoding="UTF-8"?'.'>';?>
+<?php global $manager; ?>
+
+<ajax-response>
+   <response type="element" id="message">
+               <?php echo $message; ?>
+   </response>
+</ajax-response>
\ No newline at end of file
diff --git a/NP_TrackBack/trunk/trackback/japanese-utf8.templates/updatetable.html b/NP_TrackBack/trunk/trackback/japanese-utf8.templates/updatetable.html
new file mode 100644 (file)
index 0000000..a426a8a
--- /dev/null
@@ -0,0 +1,13 @@
+<?php global $manager; ?>
+<blockquote style="color: red;border:1px solid red;padding:1em;"><b>アップデートが必要です:</b><br />
+このバージョンで運用するためにはDB内のテーブルのアップデートが必要です。<br />
+今までのデータが削除されることはありません。
+下のアップデートボタンを押してください。
+
+                       <form method="post"><div>
+                               <input type="hidden" name="action" value="tableUpgrade" />
+                               <input type="submit" tabindex="10" value="upgrade table" />
+                               <?php $manager->addTicketHidden(); ?>
+                       </div></form>
+</blockquote>
+       
diff --git a/NP_TrackBack/trunk/trackback/japanese-utf8.templates/updatetablefinished.html b/NP_TrackBack/trunk/trackback/japanese-utf8.templates/updatetablefinished.html
new file mode 100644 (file)
index 0000000..6b1ea94
--- /dev/null
@@ -0,0 +1,5 @@
+<?php global $manager; ?>
+<blockquote style="color: red;border:1px solid red;padding:1em;">
+テーブルのアップデートは完了しました。
+</blockquote>
+       
diff --git a/NP_TrackBack/trunk/trackback/js/prototype.js b/NP_TrackBack/trunk/trackback/js/prototype.js
new file mode 100644 (file)
index 0000000..5d2100f
--- /dev/null
@@ -0,0 +1,3271 @@
+/*  Prototype JavaScript framework, version 1.5.1
+ *  (c) 2005-2007 Sam Stephenson
+ *
+ *  Prototype is freely distributable under the terms of an MIT-style license.
+ *  For details, see the Prototype web site: http://www.prototypejs.org/
+ *
+/*--------------------------------------------------------------------------*/
+
+var Prototype = {
+  Version: '1.5.1',
+
+  Browser: {
+    IE:     !!(window.attachEvent && !window.opera),
+    Opera:  !!window.opera,
+    WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
+    Gecko:  navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1
+  },
+
+  BrowserFeatures: {
+    XPath: !!document.evaluate,
+    ElementExtensions: !!window.HTMLElement,
+    SpecificElementExtensions:
+      (document.createElement('div').__proto__ !==
+       document.createElement('form').__proto__)
+  },
+
+  ScriptFragment: '<script[^>]*>([\u0001-\uFFFF]*?)</script>',
+  JSONFilter: /^\/\*-secure-\s*(.*)\s*\*\/\s*$/,
+
+  emptyFunction: function() { },
+  K: function(x) { return x }
+}
+
+var Class = {
+  create: function() {
+    return function() {
+      this.initialize.apply(this, arguments);
+    }
+  }
+}
+
+var Abstract = new Object();
+
+Object.extend = function(destination, source) {
+  for (var property in source) {
+    destination[property] = source[property];
+  }
+  return destination;
+}
+
+Object.extend(Object, {
+  inspect: function(object) {
+    try {
+      if (object === undefined) return 'undefined';
+      if (object === null) return 'null';
+      return object.inspect ? object.inspect() : object.toString();
+    } catch (e) {
+      if (e instanceof RangeError) return '...';
+      throw e;
+    }
+  },
+
+  toJSON: function(object) {
+    var type = typeof object;
+    switch(type) {
+      case 'undefined':
+      case 'function':
+      case 'unknown': return;
+      case 'boolean': return object.toString();
+    }
+    if (object === null) return 'null';
+    if (object.toJSON) return object.toJSON();
+    if (object.ownerDocument === document) return;
+    var results = [];
+    for (var property in object) {
+      var value = Object.toJSON(object[property]);
+      if (value !== undefined)
+        results.push(property.toJSON() + ': ' + value);
+    }
+    return '{' + results.join(', ') + '}';
+  },
+
+  keys: function(object) {
+    var keys = [];
+    for (var property in object)
+      keys.push(property);
+    return keys;
+  },
+
+  values: function(object) {
+    var values = [];
+    for (var property in object)
+      values.push(object[property]);
+    return values;
+  },
+
+  clone: function(object) {
+    return Object.extend({}, object);
+  }
+});
+
+Function.prototype.bind = function() {
+  var __method = this, args = $A(arguments), object = args.shift();
+  return function() {
+    return __method.apply(object, args.concat($A(arguments)));
+  }
+}
+
+Function.prototype.bindAsEventListener = function(object) {
+  var __method = this, args = $A(arguments), object = args.shift();
+  return function(event) {
+    return __method.apply(object, [event || window.event].concat(args));
+  }
+}
+
+Object.extend(Number.prototype, {
+  toColorPart: function() {
+    return this.toPaddedString(2, 16);
+  },
+
+  succ: function() {
+    return this + 1;
+  },
+
+  times: function(iterator) {
+    $R(0, this, true).each(iterator);
+    return this;
+  },
+
+  toPaddedString: function(length, radix) {
+    var string = this.toString(radix || 10);
+    return '0'.times(length - string.length) + string;
+  },
+
+  toJSON: function() {
+    return isFinite(this) ? this.toString() : 'null';
+  }
+});
+
+Date.prototype.toJSON = function() {
+  return '"' + this.getFullYear() + '-' +
+    (this.getMonth() + 1).toPaddedString(2) + '-' +
+    this.getDate().toPaddedString(2) + 'T' +
+    this.getHours().toPaddedString(2) + ':' +
+    this.getMinutes().toPaddedString(2) + ':' +
+    this.getSeconds().toPaddedString(2) + '"';
+};
+
+var Try = {
+  these: function() {
+    var returnValue;
+
+    for (var i = 0, length = arguments.length; i < length; i++) {
+      var lambda = arguments[i];
+      try {
+        returnValue = lambda();
+        break;
+      } catch (e) {}
+    }
+
+    return returnValue;
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var PeriodicalExecuter = Class.create();
+PeriodicalExecuter.prototype = {
+  initialize: function(callback, frequency) {
+    this.callback = callback;
+    this.frequency = frequency;
+    this.currentlyExecuting = false;
+
+    this.registerCallback();
+  },
+
+  registerCallback: function() {
+    this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+  },
+
+  stop: function() {
+    if (!this.timer) return;
+    clearInterval(this.timer);
+    this.timer = null;
+  },
+
+  onTimerEvent: function() {
+    if (!this.currentlyExecuting) {
+      try {
+        this.currentlyExecuting = true;
+        this.callback(this);
+      } finally {
+        this.currentlyExecuting = false;
+      }
+    }
+  }
+}
+Object.extend(String, {
+  interpret: function(value) {
+    return value == null ? '' : String(value);
+  },
+  specialChar: {
+    '\b': '\\b',
+    '\t': '\\t',
+    '\n': '\\n',
+    '\f': '\\f',
+    '\r': '\\r',
+    '\\': '\\\\'
+  }
+});
+
+Object.extend(String.prototype, {
+  gsub: function(pattern, replacement) {
+    var result = '', source = this, match;
+    replacement = arguments.callee.prepareReplacement(replacement);
+
+    while (source.length > 0) {
+      if (match = source.match(pattern)) {
+        result += source.slice(0, match.index);
+        result += String.interpret(replacement(match));
+        source  = source.slice(match.index + match[0].length);
+      } else {
+        result += source, source = '';
+      }
+    }
+    return result;
+  },
+
+  sub: function(pattern, replacement, count) {
+    replacement = this.gsub.prepareReplacement(replacement);
+    count = count === undefined ? 1 : count;
+
+    return this.gsub(pattern, function(match) {
+      if (--count < 0) return match[0];
+      return replacement(match);
+    });
+  },
+
+  scan: function(pattern, iterator) {
+    this.gsub(pattern, iterator);
+    return this;
+  },
+
+  truncate: function(length, truncation) {
+    length = length || 30;
+    truncation = truncation === undefined ? '...' : truncation;
+    return this.length > length ?
+      this.slice(0, length - truncation.length) + truncation : this;
+  },
+
+  strip: function() {
+    return this.replace(/^\s+/, '').replace(/\s+$/, '');
+  },
+
+  stripTags: function() {
+    return this.replace(/<\/?[^>]+>/gi, '');
+  },
+
+  stripScripts: function() {
+    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
+  },
+
+  extractScripts: function() {
+    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
+    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+    return (this.match(matchAll) || []).map(function(scriptTag) {
+      return (scriptTag.match(matchOne) || ['', ''])[1];
+    });
+  },
+
+  evalScripts: function() {
+    return this.extractScripts().map(function(script) { return eval(script) });
+  },
+
+  escapeHTML: function() {
+    var self = arguments.callee;
+    self.text.data = this;
+    return self.div.innerHTML;
+  },
+
+  unescapeHTML: function() {
+    var div = document.createElement('div');
+    div.innerHTML = this.stripTags();
+    return div.childNodes[0] ? (div.childNodes.length > 1 ?
+      $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
+      div.childNodes[0].nodeValue) : '';
+  },
+
+  toQueryParams: function(separator) {
+    var match = this.strip().match(/([^?#]*)(#.*)?$/);
+    if (!match) return {};
+
+    return match[1].split(separator || '&').inject({}, function(hash, pair) {
+      if ((pair = pair.split('='))[0]) {
+        var key = decodeURIComponent(pair.shift());
+        var value = pair.length > 1 ? pair.join('=') : pair[0];
+        if (value != undefined) value = decodeURIComponent(value);
+
+        if (key in hash) {
+          if (hash[key].constructor != Array) hash[key] = [hash[key]];
+          hash[key].push(value);
+        }
+        else hash[key] = value;
+      }
+      return hash;
+    });
+  },
+
+  toArray: function() {
+    return this.split('');
+  },
+
+  succ: function() {
+    return this.slice(0, this.length - 1) +
+      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
+  },
+
+  times: function(count) {
+    var result = '';
+    for (var i = 0; i < count; i++) result += this;
+    return result;
+  },
+
+  camelize: function() {
+    var parts = this.split('-'), len = parts.length;
+    if (len == 1) return parts[0];
+
+    var camelized = this.charAt(0) == '-'
+      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
+      : parts[0];
+
+    for (var i = 1; i < len; i++)
+      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
+
+    return camelized;
+  },
+
+  capitalize: function() {
+    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
+  },
+
+  underscore: function() {
+    return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
+  },
+
+  dasherize: function() {
+    return this.gsub(/_/,'-');
+  },
+
+  inspect: function(useDoubleQuotes) {
+    var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
+      var character = String.specialChar[match[0]];
+      return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
+    });
+    if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
+    return "'" + escapedString.replace(/'/g, '\\\'') + "'";
+  },
+
+  toJSON: function() {
+    return this.inspect(true);
+  },
+
+  unfilterJSON: function(filter) {
+    return this.sub(filter || Prototype.JSONFilter, '#{1}');
+  },
+
+  evalJSON: function(sanitize) {
+    var json = this.unfilterJSON();
+    try {
+      if (!sanitize || (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(json)))
+        return eval('(' + json + ')');
+    } catch (e) { }
+    throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
+  },
+
+  include: function(pattern) {
+    return this.indexOf(pattern) > -1;
+  },
+
+  startsWith: function(pattern) {
+    return this.indexOf(pattern) === 0;
+  },
+
+  endsWith: function(pattern) {
+    var d = this.length - pattern.length;
+    return d >= 0 && this.lastIndexOf(pattern) === d;
+  },
+
+  empty: function() {
+    return this == '';
+  },
+
+  blank: function() {
+    return /^\s*$/.test(this);
+  }
+});
+
+if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
+  escapeHTML: function() {
+    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
+  },
+  unescapeHTML: function() {
+    return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
+  }
+});
+
+String.prototype.gsub.prepareReplacement = function(replacement) {
+  if (typeof replacement == 'function') return replacement;
+  var template = new Template(replacement);
+  return function(match) { return template.evaluate(match) };
+}
+
+String.prototype.parseQuery = String.prototype.toQueryParams;
+
+Object.extend(String.prototype.escapeHTML, {
+  div:  document.createElement('div'),
+  text: document.createTextNode('')
+});
+
+with (String.prototype.escapeHTML) div.appendChild(text);
+
+var Template = Class.create();
+Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
+Template.prototype = {
+  initialize: function(template, pattern) {
+    this.template = template.toString();
+    this.pattern  = pattern || Template.Pattern;
+  },
+
+  evaluate: function(object) {
+    return this.template.gsub(this.pattern, function(match) {
+      var before = match[1];
+      if (before == '\\') return match[2];
+      return before + String.interpret(object[match[3]]);
+    });
+  }
+}
+
+var $break = {}, $continue = new Error('"throw $continue" is deprecated, use "return" instead');
+
+var Enumerable = {
+  each: function(iterator) {
+    var index = 0;
+    try {
+      this._each(function(value) {
+        iterator(value, index++);
+      });
+    } catch (e) {
+      if (e != $break) throw e;
+    }
+    return this;
+  },
+
+  eachSlice: function(number, iterator) {
+    var index = -number, slices = [], array = this.toArray();
+    while ((index += number) < array.length)
+      slices.push(array.slice(index, index+number));
+    return slices.map(iterator);
+  },
+
+  all: function(iterator) {
+    var result = true;
+    this.each(function(value, index) {
+      result = result && !!(iterator || Prototype.K)(value, index);
+      if (!result) throw $break;
+    });
+    return result;
+  },
+
+  any: function(iterator) {
+    var result = false;
+    this.each(function(value, index) {
+      if (result = !!(iterator || Prototype.K)(value, index))
+        throw $break;
+    });
+    return result;
+  },
+
+  collect: function(iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      results.push((iterator || Prototype.K)(value, index));
+    });
+    return results;
+  },
+
+  detect: function(iterator) {
+    var result;
+    this.each(function(value, index) {
+      if (iterator(value, index)) {
+        result = value;
+        throw $break;
+      }
+    });
+    return result;
+  },
+
+  findAll: function(iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      if (iterator(value, index))
+        results.push(value);
+    });
+    return results;
+  },
+
+  grep: function(pattern, iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      var stringValue = value.toString();
+      if (stringValue.match(pattern))
+        results.push((iterator || Prototype.K)(value, index));
+    })
+    return results;
+  },
+
+  include: function(object) {
+    var found = false;
+    this.each(function(value) {
+      if (value == object) {
+        found = true;
+        throw $break;
+      }
+    });
+    return found;
+  },
+
+  inGroupsOf: function(number, fillWith) {
+    fillWith = fillWith === undefined ? null : fillWith;
+    return this.eachSlice(number, function(slice) {
+      while(slice.length < number) slice.push(fillWith);
+      return slice;
+    });
+  },
+
+  inject: function(memo, iterator) {
+    this.each(function(value, index) {
+      memo = iterator(memo, value, index);
+    });
+    return memo;
+  },
+
+  invoke: function(method) {
+    var args = $A(arguments).slice(1);
+    return this.map(function(value) {
+      return value[method].apply(value, args);
+    });
+  },
+
+  max: function(iterator) {
+    var result;
+    this.each(function(value, index) {
+      value = (iterator || Prototype.K)(value, index);
+      if (result == undefined || value >= result)
+        result = value;
+    });
+    return result;
+  },
+
+  min: function(iterator) {
+    var result;
+    this.each(function(value, index) {
+      value = (iterator || Prototype.K)(value, index);
+      if (result == undefined || value < result)
+        result = value;
+    });
+    return result;
+  },
+
+  partition: function(iterator) {
+    var trues = [], falses = [];
+    this.each(function(value, index) {
+      ((iterator || Prototype.K)(value, index) ?
+        trues : falses).push(value);
+    });
+    return [trues, falses];
+  },
+
+  pluck: function(property) {
+    var results = [];
+    this.each(function(value, index) {
+      results.push(value[property]);
+    });
+    return results;
+  },
+
+  reject: function(iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      if (!iterator(value, index))
+        results.push(value);
+    });
+    return results;
+  },
+
+  sortBy: function(iterator) {
+    return this.map(function(value, index) {
+      return {value: value, criteria: iterator(value, index)};
+    }).sort(function(left, right) {
+      var a = left.criteria, b = right.criteria;
+      return a < b ? -1 : a > b ? 1 : 0;
+    }).pluck('value');
+  },
+
+  toArray: function() {
+    return this.map();
+  },
+
+  zip: function() {
+    var iterator = Prototype.K, args = $A(arguments);
+    if (typeof args.last() == 'function')
+      iterator = args.pop();
+
+    var collections = [this].concat(args).map($A);
+    return this.map(function(value, index) {
+      return iterator(collections.pluck(index));
+    });
+  },
+
+  size: function() {
+    return this.toArray().length;
+  },
+
+  inspect: function() {
+    return '#<Enumerable:' + this.toArray().inspect() + '>';
+  }
+}
+
+Object.extend(Enumerable, {
+  map:     Enumerable.collect,
+  find:    Enumerable.detect,
+  select:  Enumerable.findAll,
+  member:  Enumerable.include,
+  entries: Enumerable.toArray
+});
+var $A = Array.from = function(iterable) {
+  if (!iterable) return [];
+  if (iterable.toArray) {
+    return iterable.toArray();
+  } else {
+    var results = [];
+    for (var i = 0, length = iterable.length; i < length; i++)
+      results.push(iterable[i]);
+    return results;
+  }
+}
+
+if (Prototype.Browser.WebKit) {
+  $A = Array.from = function(iterable) {
+    if (!iterable) return [];
+    if (!(typeof iterable == 'function' && iterable == '[object NodeList]') &&
+      iterable.toArray) {
+      return iterable.toArray();
+    } else {
+      var results = [];
+      for (var i = 0, length = iterable.length; i < length; i++)
+        results.push(iterable[i]);
+      return results;
+    }
+  }
+}
+
+Object.extend(Array.prototype, Enumerable);
+
+if (!Array.prototype._reverse)
+  Array.prototype._reverse = Array.prototype.reverse;
+
+Object.extend(Array.prototype, {
+  _each: function(iterator) {
+    for (var i = 0, length = this.length; i < length; i++)
+      iterator(this[i]);
+  },
+
+  clear: function() {
+    this.length = 0;
+    return this;
+  },
+
+  first: function() {
+    return this[0];
+  },
+
+  last: function() {
+    return this[this.length - 1];
+  },
+
+  compact: function() {
+    return this.select(function(value) {
+      return value != null;
+    });
+  },
+
+  flatten: function() {
+    return this.inject([], function(array, value) {
+      return array.concat(value && value.constructor == Array ?
+        value.flatten() : [value]);
+    });
+  },
+
+  without: function() {
+    var values = $A(arguments);
+    return this.select(function(value) {
+      return !values.include(value);
+    });
+  },
+
+  indexOf: function(object) {
+    for (var i = 0, length = this.length; i < length; i++)
+      if (this[i] == object) return i;
+    return -1;
+  },
+
+  reverse: function(inline) {
+    return (inline !== false ? this : this.toArray())._reverse();
+  },
+
+  reduce: function() {
+    return this.length > 1 ? this : this[0];
+  },
+
+  uniq: function(sorted) {
+    return this.inject([], function(array, value, index) {
+      if (0 == index || (sorted ? array.last() != value : !array.include(value)))
+        array.push(value);
+      return array;
+    });
+  },
+
+  clone: function() {
+    return [].concat(this);
+  },
+
+  size: function() {
+    return this.length;
+  },
+
+  inspect: function() {
+    return '[' + this.map(Object.inspect).join(', ') + ']';
+  },
+
+  toJSON: function() {
+    var results = [];
+    this.each(function(object) {
+      var value = Object.toJSON(object);
+      if (value !== undefined) results.push(value);
+    });
+    return '[' + results.join(', ') + ']';
+  }
+});
+
+Array.prototype.toArray = Array.prototype.clone;
+
+function $w(string) {
+  string = string.strip();
+  return string ? string.split(/\s+/) : [];
+}
+
+if (Prototype.Browser.Opera){
+  Array.prototype.concat = function() {
+    var array = [];
+    for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
+    for (var i = 0, length = arguments.length; i < length; i++) {
+      if (arguments[i].constructor == Array) {
+        for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
+          array.push(arguments[i][j]);
+      } else {
+        array.push(arguments[i]);
+      }
+    }
+    return array;
+  }
+}
+var Hash = function(object) {
+  if (object instanceof Hash) this.merge(object);
+  else Object.extend(this, object || {});
+};
+
+Object.extend(Hash, {
+  toQueryString: function(obj) {
+    var parts = [];
+    parts.add = arguments.callee.addPair;
+
+    this.prototype._each.call(obj, function(pair) {
+      if (!pair.key) return;
+      var value = pair.value;
+
+      if (value && typeof value == 'object') {
+        if (value.constructor == Array) value.each(function(value) {
+          parts.add(pair.key, value);
+        });
+        return;
+      }
+      parts.add(pair.key, value);
+    });
+
+    return parts.join('&');
+  },
+
+  toJSON: function(object) {
+    var results = [];
+    this.prototype._each.call(object, function(pair) {
+      var value = Object.toJSON(pair.value);
+      if (value !== undefined) results.push(pair.key.toJSON() + ': ' + value);
+    });
+    return '{' + results.join(', ') + '}';
+  }
+});
+
+Hash.toQueryString.addPair = function(key, value, prefix) {
+  key = encodeURIComponent(key);
+  if (value === undefined) this.push(key);
+  else this.push(key + '=' + (value == null ? '' : encodeURIComponent(value)));
+}
+
+Object.extend(Hash.prototype, Enumerable);
+Object.extend(Hash.prototype, {
+  _each: function(iterator) {
+    for (var key in this) {
+      var value = this[key];
+      if (value && value == Hash.prototype[key]) continue;
+
+      var pair = [key, value];
+      pair.key = key;
+      pair.value = value;
+      iterator(pair);
+    }
+  },
+
+  keys: function() {
+    return this.pluck('key');
+  },
+
+  values: function() {
+    return this.pluck('value');
+  },
+
+  merge: function(hash) {
+    return $H(hash).inject(this, function(mergedHash, pair) {
+      mergedHash[pair.key] = pair.value;
+      return mergedHash;
+    });
+  },
+
+  remove: function() {
+    var result;
+    for(var i = 0, length = arguments.length; i < length; i++) {
+      var value = this[arguments[i]];
+      if (value !== undefined){
+        if (result === undefined) result = value;
+        else {
+          if (result.constructor != Array) result = [result];
+          result.push(value)
+        }
+      }
+      delete this[arguments[i]];
+    }
+    return result;
+  },
+
+  toQueryString: function() {
+    return Hash.toQueryString(this);
+  },
+
+  inspect: function() {
+    return '#<Hash:{' + this.map(function(pair) {
+      return pair.map(Object.inspect).join(': ');
+    }).join(', ') + '}>';
+  },
+
+  toJSON: function() {
+    return Hash.toJSON(this);
+  }
+});
+
+function $H(object) {
+  if (object instanceof Hash) return object;
+  return new Hash(object);
+};
+
+// Safari iterates over shadowed properties
+if (function() {
+  var i = 0, Test = function(value) { this.key = value };
+  Test.prototype.key = 'foo';
+  for (var property in new Test('bar')) i++;
+  return i > 1;
+}()) Hash.prototype._each = function(iterator) {
+  var cache = [];
+  for (var key in this) {
+    var value = this[key];
+    if ((value && value == Hash.prototype[key]) || cache.include(key)) continue;
+    cache.push(key);
+    var pair = [key, value];
+    pair.key = key;
+    pair.value = value;
+    iterator(pair);
+  }
+};
+ObjectRange = Class.create();
+Object.extend(ObjectRange.prototype, Enumerable);
+Object.extend(ObjectRange.prototype, {
+  initialize: function(start, end, exclusive) {
+    this.start = start;
+    this.end = end;
+    this.exclusive = exclusive;
+  },
+
+  _each: function(iterator) {
+    var value = this.start;
+    while (this.include(value)) {
+      iterator(value);
+      value = value.succ();
+    }
+  },
+
+  include: function(value) {
+    if (value < this.start)
+      return false;
+    if (this.exclusive)
+      return value < this.end;
+    return value <= this.end;
+  }
+});
+
+var $R = function(start, end, exclusive) {
+  return new ObjectRange(start, end, exclusive);
+}
+
+var Ajax = {
+  getTransport: function() {
+    return Try.these(
+      function() {return new XMLHttpRequest()},
+      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
+    ) || false;
+  },
+
+  activeRequestCount: 0
+}
+
+Ajax.Responders = {
+  responders: [],
+
+  _each: function(iterator) {
+    this.responders._each(iterator);
+  },
+
+  register: function(responder) {
+    if (!this.include(responder))
+      this.responders.push(responder);
+  },
+
+  unregister: function(responder) {
+    this.responders = this.responders.without(responder);
+  },
+
+  dispatch: function(callback, request, transport, json) {
+    this.each(function(responder) {
+      if (typeof responder[callback] == 'function') {
+        try {
+          responder[callback].apply(responder, [request, transport, json]);
+        } catch (e) {}
+      }
+    });
+  }
+};
+
+Object.extend(Ajax.Responders, Enumerable);
+
+Ajax.Responders.register({
+  onCreate: function() {
+    Ajax.activeRequestCount++;
+  },
+  onComplete: function() {
+    Ajax.activeRequestCount--;
+  }
+});
+
+Ajax.Base = function() {};
+Ajax.Base.prototype = {
+  setOptions: function(options) {
+    this.options = {
+      method:       'post',
+      asynchronous: true,
+      contentType:  'application/x-www-form-urlencoded',
+      encoding:     'UTF-8',
+      parameters:   ''
+    }
+    Object.extend(this.options, options || {});
+
+    this.options.method = this.options.method.toLowerCase();
+    if (typeof this.options.parameters == 'string')
+      this.options.parameters = this.options.parameters.toQueryParams();
+  }
+}
+
+Ajax.Request = Class.create();
+Ajax.Request.Events =
+  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
+  _complete: false,
+
+  initialize: function(url, options) {
+    this.transport = Ajax.getTransport();
+    this.setOptions(options);
+    this.request(url);
+  },
+
+  request: function(url) {
+    this.url = url;
+    this.method = this.options.method;
+    var params = Object.clone(this.options.parameters);
+
+    if (!['get', 'post'].include(this.method)) {
+      // simulate other verbs over post
+      params['_method'] = this.method;
+      this.method = 'post';
+    }
+
+    this.parameters = params;
+
+    if (params = Hash.toQueryString(params)) {
+      // when GET, append parameters to URL
+      if (this.method == 'get')
+        this.url += (this.url.include('?') ? '&' : '?') + params;
+      else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
+        params += '&_=';
+    }
+
+    try {
+      if (this.options.onCreate) this.options.onCreate(this.transport);
+      Ajax.Responders.dispatch('onCreate', this, this.transport);
+
+      this.transport.open(this.method.toUpperCase(), this.url,
+        this.options.asynchronous);
+
+      if (this.options.asynchronous)
+        setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
+
+      this.transport.onreadystatechange = this.onStateChange.bind(this);
+      this.setRequestHeaders();
+
+      this.body = this.method == 'post' ? (this.options.postBody || params) : null;
+      this.transport.send(this.body);
+
+      /* Force Firefox to handle ready state 4 for synchronous requests */
+      if (!this.options.asynchronous && this.transport.overrideMimeType)
+        this.onStateChange();
+
+    }
+    catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
+  onStateChange: function() {
+    var readyState = this.transport.readyState;
+    if (readyState > 1 && !((readyState == 4) && this._complete))
+      this.respondToReadyState(this.transport.readyState);
+  },
+
+  setRequestHeaders: function() {
+    var headers = {
+      'X-Requested-With': 'XMLHttpRequest',
+      'X-Prototype-Version': Prototype.Version,
+      'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
+    };
+
+    if (this.method == 'post') {
+      headers['Content-type'] = this.options.contentType +
+        (this.options.encoding ? '; charset=' + this.options.encoding : '');
+
+      /* Force "Connection: close" for older Mozilla browsers to work
+       * around a bug where XMLHttpRequest sends an incorrect
+       * Content-length header. See Mozilla Bugzilla #246651.
+       */
+      if (this.transport.overrideMimeType &&
+          (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
+            headers['Connection'] = 'close';
+    }
+
+    // user-defined headers
+    if (typeof this.options.requestHeaders == 'object') {
+      var extras = this.options.requestHeaders;
+
+      if (typeof extras.push == 'function')
+        for (var i = 0, length = extras.length; i < length; i += 2)
+          headers[extras[i]] = extras[i+1];
+      else
+        $H(extras).each(function(pair) { headers[pair.key] = pair.value });
+    }
+
+    for (var name in headers)
+      this.transport.setRequestHeader(name, headers[name]);
+  },
+
+  success: function() {
+    return !this.transport.status
+        || (this.transport.status >= 200 && this.transport.status < 300);
+  },
+
+  respondToReadyState: function(readyState) {
+    var state = Ajax.Request.Events[readyState];
+    var transport = this.transport, json = this.evalJSON();
+
+    if (state == 'Complete') {
+      try {
+        this._complete = true;
+        (this.options['on' + this.transport.status]
+         || this.options['on' + (this.success() ? 'Success' : 'Failure')]
+         || Prototype.emptyFunction)(transport, json);
+      } catch (e) {
+        this.dispatchException(e);
+      }
+
+      var contentType = this.getHeader('Content-type');
+      if (contentType && contentType.strip().
+        match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
+          this.evalResponse();
+    }
+
+    try {
+      (this.options['on' + state] || Prototype.emptyFunction)(transport, json);
+      Ajax.Responders.dispatch('on' + state, this, transport, json);
+    } catch (e) {
+      this.dispatchException(e);
+    }
+
+    if (state == 'Complete') {
+      // avoid memory leak in MSIE: clean up
+      this.transport.onreadystatechange = Prototype.emptyFunction;
+    }
+  },
+
+  getHeader: function(name) {
+    try {
+      return this.transport.getResponseHeader(name);
+    } catch (e) { return null }
+  },
+
+  evalJSON: function() {
+    try {
+      var json = this.getHeader('X-JSON');
+      return json ? json.evalJSON() : null;
+    } catch (e) { return null }
+  },
+
+  evalResponse: function() {
+    try {
+      return eval((this.transport.responseText || '').unfilterJSON());
+    } catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
+  dispatchException: function(exception) {
+    (this.options.onException || Prototype.emptyFunction)(this, exception);
+    Ajax.Responders.dispatch('onException', this, exception);
+  }
+});
+
+Ajax.Updater = Class.create();
+
+Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
+  initialize: function(container, url, options) {
+    this.container = {
+      success: (container.success || container),
+      failure: (container.failure || (container.success ? null : container))
+    }
+
+    this.transport = Ajax.getTransport();
+    this.setOptions(options);
+
+    var onComplete = this.options.onComplete || Prototype.emptyFunction;
+    this.options.onComplete = (function(transport, param) {
+      this.updateContent();
+      onComplete(transport, param);
+    }).bind(this);
+
+    this.request(url);
+  },
+
+  updateContent: function() {
+    var receiver = this.container[this.success() ? 'success' : 'failure'];
+    var response = this.transport.responseText;
+
+    if (!this.options.evalScripts) response = response.stripScripts();
+
+    if (receiver = $(receiver)) {
+      if (this.options.insertion)
+        new this.options.insertion(receiver, response);
+      else
+        receiver.update(response);
+    }
+
+    if (this.success()) {
+      if (this.onComplete)
+        setTimeout(this.onComplete.bind(this), 10);
+    }
+  }
+});
+
+Ajax.PeriodicalUpdater = Class.create();
+Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
+  initialize: function(container, url, options) {
+    this.setOptions(options);
+    this.onComplete = this.options.onComplete;
+
+    this.frequency = (this.options.frequency || 2);
+    this.decay = (this.options.decay || 1);
+
+    this.updater = {};
+    this.container = container;
+    this.url = url;
+
+    this.start();
+  },
+
+  start: function() {
+    this.options.onComplete = this.updateComplete.bind(this);
+    this.onTimerEvent();
+  },
+
+  stop: function() {
+    this.updater.options.onComplete = undefined;
+    clearTimeout(this.timer);
+    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
+  },
+
+  updateComplete: function(request) {
+    if (this.options.decay) {
+      this.decay = (request.responseText == this.lastText ?
+        this.decay * this.options.decay : 1);
+
+      this.lastText = request.responseText;
+    }
+    this.timer = setTimeout(this.onTimerEvent.bind(this),
+      this.decay * this.frequency * 1000);
+  },
+
+  onTimerEvent: function() {
+    this.updater = new Ajax.Updater(this.container, this.url, this.options);
+  }
+});
+function $(element) {
+  if (arguments.length > 1) {
+    for (var i = 0, elements = [], length = arguments.length; i < length; i++)
+      elements.push($(arguments[i]));
+    return elements;
+  }
+  if (typeof element == 'string')
+    element = document.getElementById(element);
+  return Element.extend(element);
+}
+
+if (Prototype.BrowserFeatures.XPath) {
+  document._getElementsByXPath = function(expression, parentElement) {
+    var results = [];
+    var query = document.evaluate(expression, $(parentElement) || document,
+      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+    for (var i = 0, length = query.snapshotLength; i < length; i++)
+      results.push(query.snapshotItem(i));
+    return results;
+  };
+
+  document.getElementsByClassName = function(className, parentElement) {
+    var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
+    return document._getElementsByXPath(q, parentElement);
+  }
+
+} else document.getElementsByClassName = function(className, parentElement) {
+  var children = ($(parentElement) || document.body).getElementsByTagName('*');
+  var elements = [], child;
+  for (var i = 0, length = children.length; i < length; i++) {
+    child = children[i];
+    if (Element.hasClassName(child, className))
+      elements.push(Element.extend(child));
+  }
+  return elements;
+};
+
+/*--------------------------------------------------------------------------*/
+
+if (!window.Element) var Element = {};
+
+Element.extend = function(element) {
+  var F = Prototype.BrowserFeatures;
+  if (!element || !element.tagName || element.nodeType == 3 ||
+   element._extended || F.SpecificElementExtensions || element == window)
+    return element;
+
+  var methods = {}, tagName = element.tagName, cache = Element.extend.cache,
+   T = Element.Methods.ByTag;
+
+  // extend methods for all tags (Safari doesn't need this)
+  if (!F.ElementExtensions) {
+    Object.extend(methods, Element.Methods),
+    Object.extend(methods, Element.Methods.Simulated);
+  }
+
+  // extend methods for specific tags
+  if (T[tagName]) Object.extend(methods, T[tagName]);
+
+  for (var property in methods) {
+    var value = methods[property];
+    if (typeof value == 'function' && !(property in element))
+      element[property] = cache.findOrStore(value);
+  }
+
+  element._extended = Prototype.emptyFunction;
+  return element;
+};
+
+Element.extend.cache = {
+  findOrStore: function(value) {
+    return this[value] = this[value] || function() {
+      return value.apply(null, [this].concat($A(arguments)));
+    }
+  }
+};
+
+Element.Methods = {
+  visible: function(element) {
+    return $(element).style.display != 'none';
+  },
+
+  toggle: function(element) {
+    element = $(element);
+    Element[Element.visible(element) ? 'hide' : 'show'](element);
+    return element;
+  },
+
+  hide: function(element) {
+    $(element).style.display = 'none';
+    return element;
+  },
+
+  show: function(element) {
+    $(element).style.display = '';
+    return element;
+  },
+
+  remove: function(element) {
+    element = $(element);
+    element.parentNode.removeChild(element);
+    return element;
+  },
+
+  update: function(element, html) {
+    html = typeof html == 'undefined' ? '' : html.toString();
+    $(element).innerHTML = html.stripScripts();
+    setTimeout(function() {html.evalScripts()}, 10);
+    return element;
+  },
+
+  replace: function(element, html) {
+    element = $(element);
+    html = typeof html == 'undefined' ? '' : html.toString();
+    if (element.outerHTML) {
+      element.outerHTML = html.stripScripts();
+    } else {
+      var range = element.ownerDocument.createRange();
+      range.selectNodeContents(element);
+      element.parentNode.replaceChild(
+        range.createContextualFragment(html.stripScripts()), element);
+    }
+    setTimeout(function() {html.evalScripts()}, 10);
+    return element;
+  },
+
+  inspect: function(element) {
+    element = $(element);
+    var result = '<' + element.tagName.toLowerCase();
+    $H({'id': 'id', 'className': 'class'}).each(function(pair) {
+      var property = pair.first(), attribute = pair.last();
+      var value = (element[property] || '').toString();
+      if (value) result += ' ' + attribute + '=' + value.inspect(true);
+    });
+    return result + '>';
+  },
+
+  recursivelyCollect: function(element, property) {
+    element = $(element);
+    var elements = [];
+    while (element = element[property])
+      if (element.nodeType == 1)
+        elements.push(Element.extend(element));
+    return elements;
+  },
+
+  ancestors: function(element) {
+    return $(element).recursivelyCollect('parentNode');
+  },
+
+  descendants: function(element) {
+    return $A($(element).getElementsByTagName('*')).each(Element.extend);
+  },
+
+  firstDescendant: function(element) {
+    element = $(element).firstChild;
+    while (element && element.nodeType != 1) element = element.nextSibling;
+    return $(element);
+  },
+
+  immediateDescendants: function(element) {
+    if (!(element = $(element).firstChild)) return [];
+    while (element && element.nodeType != 1) element = element.nextSibling;
+    if (element) return [element].concat($(element).nextSiblings());
+    return [];
+  },
+
+  previousSiblings: function(element) {
+    return $(element).recursivelyCollect('previousSibling');
+  },
+
+  nextSiblings: function(element) {
+    return $(element).recursivelyCollect('nextSibling');
+  },
+
+  siblings: function(element) {
+    element = $(element);
+    return element.previousSiblings().reverse().concat(element.nextSiblings());
+  },
+
+  match: function(element, selector) {
+    if (typeof selector == 'string')
+      selector = new Selector(selector);
+    return selector.match($(element));
+  },
+
+  up: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return $(element.parentNode);
+    var ancestors = element.ancestors();
+    return expression ? Selector.findElement(ancestors, expression, index) :
+      ancestors[index || 0];
+  },
+
+  down: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return element.firstDescendant();
+    var descendants = element.descendants();
+    return expression ? Selector.findElement(descendants, expression, index) :
+      descendants[index || 0];
+  },
+
+  previous: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
+    var previousSiblings = element.previousSiblings();
+    return expression ? Selector.findElement(previousSiblings, expression, index) :
+      previousSiblings[index || 0];
+  },
+
+  next: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
+    var nextSiblings = element.nextSiblings();
+    return expression ? Selector.findElement(nextSiblings, expression, index) :
+      nextSiblings[index || 0];
+  },
+
+  getElementsBySelector: function() {
+    var args = $A(arguments), element = $(args.shift());
+    return Selector.findChildElements(element, args);
+  },
+
+  getElementsByClassName: function(element, className) {
+    return document.getElementsByClassName(className, element);
+  },
+
+  readAttribute: function(element, name) {
+    element = $(element);
+    if (Prototype.Browser.IE) {
+      if (!element.attributes) return null;
+      var t = Element._attributeTranslations;
+      if (t.values[name]) return t.values[name](element, name);
+      if (t.names[name])  name = t.names[name];
+      var attribute = element.attributes[name];
+      return attribute ? attribute.nodeValue : null;
+    }
+    return element.getAttribute(name);
+  },
+
+  getHeight: function(element) {
+    return $(element).getDimensions().height;
+  },
+
+  getWidth: function(element) {
+    return $(element).getDimensions().width;
+  },
+
+  classNames: function(element) {
+    return new Element.ClassNames(element);
+  },
+
+  hasClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    var elementClassName = element.className;
+    if (elementClassName.length == 0) return false;
+    if (elementClassName == className ||
+        elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
+      return true;
+    return false;
+  },
+
+  addClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    Element.classNames(element).add(className);
+    return element;
+  },
+
+  removeClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    Element.classNames(element).remove(className);
+    return element;
+  },
+
+  toggleClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
+    return element;
+  },
+
+  observe: function() {
+    Event.observe.apply(Event, arguments);
+    return $A(arguments).first();
+  },
+
+  stopObserving: function() {
+    Event.stopObserving.apply(Event, arguments);
+    return $A(arguments).first();
+  },
+
+  // removes whitespace-only text node children
+  cleanWhitespace: function(element) {
+    element = $(element);
+    var node = element.firstChild;
+    while (node) {
+      var nextNode = node.nextSibling;
+      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
+        element.removeChild(node);
+      node = nextNode;
+    }
+    return element;
+  },
+
+  empty: function(element) {
+    return $(element).innerHTML.blank();
+  },
+
+  descendantOf: function(element, ancestor) {
+    element = $(element), ancestor = $(ancestor);
+    while (element = element.parentNode)
+      if (element == ancestor) return true;
+    return false;
+  },
+
+  scrollTo: function(element) {
+    element = $(element);
+    var pos = Position.cumulativeOffset(element);
+    window.scrollTo(pos[0], pos[1]);
+    return element;
+  },
+
+  getStyle: function(element, style) {
+    element = $(element);
+    style = style == 'float' ? 'cssFloat' : style.camelize();
+    var value = element.style[style];
+    if (!value) {
+      var css = document.defaultView.getComputedStyle(element, null);
+      value = css ? css[style] : null;
+    }
+    if (style == 'opacity') return value ? parseFloat(value) : 1.0;
+    return value == 'auto' ? null : value;
+  },
+
+  getOpacity: function(element) {
+    return $(element).getStyle('opacity');
+  },
+
+  setStyle: function(element, styles, camelized) {
+    element = $(element);
+    var elementStyle = element.style;
+
+    for (var property in styles)
+      if (property == 'opacity') element.setOpacity(styles[property])
+      else
+        elementStyle[(property == 'float' || property == 'cssFloat') ?
+          (elementStyle.styleFloat === undefined ? 'cssFloat' : 'styleFloat') :
+          (camelized ? property : property.camelize())] = styles[property];
+
+    return element;
+  },
+
+  setOpacity: function(element, value) {
+    element = $(element);
+    element.style.opacity = (value == 1 || value === '') ? '' :
+      (value < 0.00001) ? 0 : value;
+    return element;
+  },
+
+  getDimensions: function(element) {
+    element = $(element);
+    var display = $(element).getStyle('display');
+    if (display != 'none' && display != null) // Safari bug
+      return {width: element.offsetWidth, height: element.offsetHeight};
+
+    // All *Width and *Height properties give 0 on elements with display none,
+    // so enable the element temporarily
+    var els = element.style;
+    var originalVisibility = els.visibility;
+    var originalPosition = els.position;
+    var originalDisplay = els.display;
+    els.visibility = 'hidden';
+    els.position = 'absolute';
+    els.display = 'block';
+    var originalWidth = element.clientWidth;
+    var originalHeight = element.clientHeight;
+    els.display = originalDisplay;
+    els.position = originalPosition;
+    els.visibility = originalVisibility;
+    return {width: originalWidth, height: originalHeight};
+  },
+
+  makePositioned: function(element) {
+    element = $(element);
+    var pos = Element.getStyle(element, 'position');
+    if (pos == 'static' || !pos) {
+      element._madePositioned = true;
+      element.style.position = 'relative';
+      // Opera returns the offset relative to the positioning context, when an
+      // element is position relative but top and left have not been defined
+      if (window.opera) {
+        element.style.top = 0;
+        element.style.left = 0;
+      }
+    }
+    return element;
+  },
+
+  undoPositioned: function(element) {
+    element = $(element);
+    if (element._madePositioned) {
+      element._madePositioned = undefined;
+      element.style.position =
+        element.style.top =
+        element.style.left =
+        element.style.bottom =
+        element.style.right = '';
+    }
+    return element;
+  },
+
+  makeClipping: function(element) {
+    element = $(element);
+    if (element._overflow) return element;
+    element._overflow = element.style.overflow || 'auto';
+    if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
+      element.style.overflow = 'hidden';
+    return element;
+  },
+
+  undoClipping: function(element) {
+    element = $(element);
+    if (!element._overflow) return element;
+    element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
+    element._overflow = null;
+    return element;
+  }
+};
+
+Object.extend(Element.Methods, {
+  childOf: Element.Methods.descendantOf,
+  childElements: Element.Methods.immediateDescendants
+});
+
+if (Prototype.Browser.Opera) {
+  Element.Methods._getStyle = Element.Methods.getStyle;
+  Element.Methods.getStyle = function(element, style) {
+    switch(style) {
+      case 'left':
+      case 'top':
+      case 'right':
+      case 'bottom':
+        if (Element._getStyle(element, 'position') == 'static') return null;
+      default: return Element._getStyle(element, style);
+    }
+  };
+}
+else if (Prototype.Browser.IE) {
+  Element.Methods.getStyle = function(element, style) {
+    element = $(element);
+    style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
+    var value = element.style[style];
+    if (!value && element.currentStyle) value = element.currentStyle[style];
+
+    if (style == 'opacity') {
+      if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
+        if (value[1]) return parseFloat(value[1]) / 100;
+      return 1.0;
+    }
+
+    if (value == 'auto') {
+      if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
+        return element['offset'+style.capitalize()] + 'px';
+      return null;
+    }
+    return value;
+  };
+
+  Element.Methods.setOpacity = function(element, value) {
+    element = $(element);
+    var filter = element.getStyle('filter'), style = element.style;
+    if (value == 1 || value === '') {
+      style.filter = filter.replace(/alpha\([^\)]*\)/gi,'');
+      return element;
+    } else if (value < 0.00001) value = 0;
+    style.filter = filter.replace(/alpha\([^\)]*\)/gi, '') +
+      'alpha(opacity=' + (value * 100) + ')';
+    return element;
+  };
+
+  // IE is missing .innerHTML support for TABLE-related elements
+  Element.Methods.update = function(element, html) {
+    element = $(element);
+    html = typeof html == 'undefined' ? '' : html.toString();
+    var tagName = element.tagName.toUpperCase();
+    if (['THEAD','TBODY','TR','TD'].include(tagName)) {
+      var div = document.createElement('div');
+      switch (tagName) {
+        case 'THEAD':
+        case 'TBODY':
+          div.innerHTML = '<table><tbody>' +  html.stripScripts() + '</tbody></table>';
+          depth = 2;
+          break;
+        case 'TR':
+          div.innerHTML = '<table><tbody><tr>' +  html.stripScripts() + '</tr></tbody></table>';
+          depth = 3;
+          break;
+        case 'TD':
+          div.innerHTML = '<table><tbody><tr><td>' +  html.stripScripts() + '</td></tr></tbody></table>';
+          depth = 4;
+      }
+      $A(element.childNodes).each(function(node) { element.removeChild(node) });
+      depth.times(function() { div = div.firstChild });
+      $A(div.childNodes).each(function(node) { element.appendChild(node) });
+    } else {
+      element.innerHTML = html.stripScripts();
+    }
+    setTimeout(function() { html.evalScripts() }, 10);
+    return element;
+  }
+}
+else if (Prototype.Browser.Gecko) {
+  Element.Methods.setOpacity = function(element, value) {
+    element = $(element);
+    element.style.opacity = (value == 1) ? 0.999999 :
+      (value === '') ? '' : (value < 0.00001) ? 0 : value;
+    return element;
+  };
+}
+
+Element._attributeTranslations = {
+  names: {
+    colspan:   "colSpan",
+    rowspan:   "rowSpan",
+    valign:    "vAlign",
+    datetime:  "dateTime",
+    accesskey: "accessKey",
+    tabindex:  "tabIndex",
+    enctype:   "encType",
+    maxlength: "maxLength",
+    readonly:  "readOnly",
+    longdesc:  "longDesc"
+  },
+  values: {
+    _getAttr: function(element, attribute) {
+      return element.getAttribute(attribute, 2);
+    },
+    _flag: function(element, attribute) {
+      return $(element).hasAttribute(attribute) ? attribute : null;
+    },
+    style: function(element) {
+      return element.style.cssText.toLowerCase();
+    },
+    title: function(element) {
+      var node = element.getAttributeNode('title');
+      return node.specified ? node.nodeValue : null;
+    }
+  }
+};
+
+(function() {
+  Object.extend(this, {
+    href: this._getAttr,
+    src:  this._getAttr,
+    type: this._getAttr,
+    disabled: this._flag,
+    checked:  this._flag,
+    readonly: this._flag,
+    multiple: this._flag
+  });
+}).call(Element._attributeTranslations.values);
+
+Element.Methods.Simulated = {
+  hasAttribute: function(element, attribute) {
+    var t = Element._attributeTranslations, node;
+    attribute = t.names[attribute] || attribute;
+    node = $(element).getAttributeNode(attribute);
+    return node && node.specified;
+  }
+};
+
+Element.Methods.ByTag = {};
+
+Object.extend(Element, Element.Methods);
+
+if (!Prototype.BrowserFeatures.ElementExtensions &&
+ document.createElement('div').__proto__) {
+  window.HTMLElement = {};
+  window.HTMLElement.prototype = document.createElement('div').__proto__;
+  Prototype.BrowserFeatures.ElementExtensions = true;
+}
+
+Element.hasAttribute = function(element, attribute) {
+  if (element.hasAttribute) return element.hasAttribute(attribute);
+  return Element.Methods.Simulated.hasAttribute(element, attribute);
+};
+
+Element.addMethods = function(methods) {
+  var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
+
+  if (!methods) {
+    Object.extend(Form, Form.Methods);
+    Object.extend(Form.Element, Form.Element.Methods);
+    Object.extend(Element.Methods.ByTag, {
+      "FORM":     Object.clone(Form.Methods),
+      "INPUT":    Object.clone(Form.Element.Methods),
+      "SELECT":   Object.clone(Form.Element.Methods),
+      "TEXTAREA": Object.clone(Form.Element.Methods)
+    });
+  }
+
+  if (arguments.length == 2) {
+    var tagName = methods;
+    methods = arguments[1];
+  }
+
+  if (!tagName) Object.extend(Element.Methods, methods || {});
+  else {
+    if (tagName.constructor == Array) tagName.each(extend);
+    else extend(tagName);
+  }
+
+  function extend(tagName) {
+    tagName = tagName.toUpperCase();
+    if (!Element.Methods.ByTag[tagName])
+      Element.Methods.ByTag[tagName] = {};
+    Object.extend(Element.Methods.ByTag[tagName], methods);
+  }
+
+  function copy(methods, destination, onlyIfAbsent) {
+    onlyIfAbsent = onlyIfAbsent || false;
+    var cache = Element.extend.cache;
+    for (var property in methods) {
+      var value = methods[property];
+      if (!onlyIfAbsent || !(property in destination))
+        destination[property] = cache.findOrStore(value);
+    }
+  }
+
+  function findDOMClass(tagName) {
+    var klass;
+    var trans = {
+      "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
+      "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
+      "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
+      "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
+      "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
+      "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
+      "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
+      "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
+      "FrameSet", "IFRAME": "IFrame"
+    };
+    if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
+    if (window[klass]) return window[klass];
+    klass = 'HTML' + tagName + 'Element';
+    if (window[klass]) return window[klass];
+    klass = 'HTML' + tagName.capitalize() + 'Element';
+    if (window[klass]) return window[klass];
+
+    window[klass] = {};
+    window[klass].prototype = document.createElement(tagName).__proto__;
+    return window[klass];
+  }
+
+  if (F.ElementExtensions) {
+    copy(Element.Methods, HTMLElement.prototype);
+    copy(Element.Methods.Simulated, HTMLElement.prototype, true);
+  }
+
+  if (F.SpecificElementExtensions) {
+    for (var tag in Element.Methods.ByTag) {
+      var klass = findDOMClass(tag);
+      if (typeof klass == "undefined") continue;
+      copy(T[tag], klass.prototype);
+    }
+  }
+
+  Object.extend(Element, Element.Methods);
+  delete Element.ByTag;
+};
+
+var Toggle = { display: Element.toggle };
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.Insertion = function(adjacency) {
+  this.adjacency = adjacency;
+}
+
+Abstract.Insertion.prototype = {
+  initialize: function(element, content) {
+    this.element = $(element);
+    this.content = content.stripScripts();
+
+    if (this.adjacency && this.element.insertAdjacentHTML) {
+      try {
+        this.element.insertAdjacentHTML(this.adjacency, this.content);
+      } catch (e) {
+        var tagName = this.element.tagName.toUpperCase();
+        if (['TBODY', 'TR'].include(tagName)) {
+          this.insertContent(this.contentFromAnonymousTable());
+        } else {
+          throw e;
+        }
+      }
+    } else {
+      this.range = this.element.ownerDocument.createRange();
+      if (this.initializeRange) this.initializeRange();
+      this.insertContent([this.range.createContextualFragment(this.content)]);
+    }
+
+    setTimeout(function() {content.evalScripts()}, 10);
+  },
+
+  contentFromAnonymousTable: function() {
+    var div = document.createElement('div');
+    div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
+    return $A(div.childNodes[0].childNodes[0].childNodes);
+  }
+}
+
+var Insertion = new Object();
+
+Insertion.Before = Class.create();
+Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
+  initializeRange: function() {
+    this.range.setStartBefore(this.element);
+  },
+
+  insertContent: function(fragments) {
+    fragments.each((function(fragment) {
+      this.element.parentNode.insertBefore(fragment, this.element);
+    }).bind(this));
+  }
+});
+
+Insertion.Top = Class.create();
+Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
+  initializeRange: function() {
+    this.range.selectNodeContents(this.element);
+    this.range.collapse(true);
+  },
+
+  insertContent: function(fragments) {
+    fragments.reverse(false).each((function(fragment) {
+      this.element.insertBefore(fragment, this.element.firstChild);
+    }).bind(this));
+  }
+});
+
+Insertion.Bottom = Class.create();
+Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
+  initializeRange: function() {
+    this.range.selectNodeContents(this.element);
+    this.range.collapse(this.element);
+  },
+
+  insertContent: function(fragments) {
+    fragments.each((function(fragment) {
+      this.element.appendChild(fragment);
+    }).bind(this));
+  }
+});
+
+Insertion.After = Class.create();
+Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
+  initializeRange: function() {
+    this.range.setStartAfter(this.element);
+  },
+
+  insertContent: function(fragments) {
+    fragments.each((function(fragment) {
+      this.element.parentNode.insertBefore(fragment,
+        this.element.nextSibling);
+    }).bind(this));
+  }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Element.ClassNames = Class.create();
+Element.ClassNames.prototype = {
+  initialize: function(element) {
+    this.element = $(element);
+  },
+
+  _each: function(iterator) {
+    this.element.className.split(/\s+/).select(function(name) {
+      return name.length > 0;
+    })._each(iterator);
+  },
+
+  set: function(className) {
+    this.element.className = className;
+  },
+
+  add: function(classNameToAdd) {
+    if (this.include(classNameToAdd)) return;
+    this.set($A(this).concat(classNameToAdd).join(' '));
+  },
+
+  remove: function(classNameToRemove) {
+    if (!this.include(classNameToRemove)) return;
+    this.set($A(this).without(classNameToRemove).join(' '));
+  },
+
+  toString: function() {
+    return $A(this).join(' ');
+  }
+};
+
+Object.extend(Element.ClassNames.prototype, Enumerable);
+/* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
+ * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
+ * license.  Please see http://www.yui-ext.com/ for more information. */
+
+var Selector = Class.create();
+
+Selector.prototype = {
+  initialize: function(expression) {
+    this.expression = expression.strip();
+    this.compileMatcher();
+  },
+
+  compileMatcher: function() {
+    // Selectors with namespaced attributes can't use the XPath version
+    if (Prototype.BrowserFeatures.XPath && !(/\[[\w-]*?:/).test(this.expression))
+      return this.compileXPathMatcher();
+
+    var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
+        c = Selector.criteria, le, p, m;
+
+    if (Selector._cache[e]) {
+      this.matcher = Selector._cache[e]; return;
+    }
+    this.matcher = ["this.matcher = function(root) {",
+                    "var r = root, h = Selector.handlers, c = false, n;"];
+
+    while (e && le != e && (/\S/).test(e)) {
+      le = e;
+      for (var i in ps) {
+        p = ps[i];
+        if (m = e.match(p)) {
+          this.matcher.push(typeof c[i] == 'function' ? c[i](m) :
+             new Template(c[i]).evaluate(m));
+          e = e.replace(m[0], '');
+          break;
+        }
+      }
+    }
+
+    this.matcher.push("return h.unique(n);\n}");
+    eval(this.matcher.join('\n'));
+    Selector._cache[this.expression] = this.matcher;
+  },
+
+  compileXPathMatcher: function() {
+    var e = this.expression, ps = Selector.patterns,
+        x = Selector.xpath, le,  m;
+
+    if (Selector._cache[e]) {
+      this.xpath = Selector._cache[e]; return;
+    }
+
+    this.matcher = ['.//*'];
+    while (e && le != e && (/\S/).test(e)) {
+      le = e;
+      for (var i in ps) {
+        if (m = e.match(ps[i])) {
+          this.matcher.push(typeof x[i] == 'function' ? x[i](m) :
+            new Template(x[i]).evaluate(m));
+          e = e.replace(m[0], '');
+          break;
+        }
+      }
+    }
+
+    this.xpath = this.matcher.join('');
+    Selector._cache[this.expression] = this.xpath;
+  },
+
+  findElements: function(root) {
+    root = root || document;
+    if (this.xpath) return document._getElementsByXPath(this.xpath, root);
+    return this.matcher(root);
+  },
+
+  match: function(element) {
+    return this.findElements(document).include(element);
+  },
+
+  toString: function() {
+    return this.expression;
+  },
+
+  inspect: function() {
+    return "#<Selector:" + this.expression.inspect() + ">";
+  }
+};
+
+Object.extend(Selector, {
+  _cache: {},
+
+  xpath: {
+    descendant:   "//*",
+    child:        "/*",
+    adjacent:     "/following-sibling::*[1]",
+    laterSibling: '/following-sibling::*',
+    tagName:      function(m) {
+      if (m[1] == '*') return '';
+      return "[local-name()='" + m[1].toLowerCase() +
+             "' or local-name()='" + m[1].toUpperCase() + "']";
+    },
+    className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]",
+    id:           "[@id='#{1}']",
+    attrPresence: "[@#{1}]",
+    attr: function(m) {
+      m[3] = m[5] || m[6];
+      return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
+    },
+    pseudo: function(m) {
+      var h = Selector.xpath.pseudos[m[1]];
+      if (!h) return '';
+      if (typeof h === 'function') return h(m);
+      return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
+    },
+    operators: {
+      '=':  "[@#{1}='#{3}']",
+      '!=': "[@#{1}!='#{3}']",
+      '^=': "[starts-with(@#{1}, '#{3}')]",
+      '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
+      '*=': "[contains(@#{1}, '#{3}')]",
+      '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
+      '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
+    },
+    pseudos: {
+      'first-child': '[not(preceding-sibling::*)]',
+      'last-child':  '[not(following-sibling::*)]',
+      'only-child':  '[not(preceding-sibling::* or following-sibling::*)]',
+      'empty':       "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
+      'checked':     "[@checked]",
+      'disabled':    "[@disabled]",
+      'enabled':     "[not(@disabled)]",
+      'not': function(m) {
+        var e = m[6], p = Selector.patterns,
+            x = Selector.xpath, le, m, v;
+
+        var exclusion = [];
+        while (e && le != e && (/\S/).test(e)) {
+          le = e;
+          for (var i in p) {
+            if (m = e.match(p[i])) {
+              v = typeof x[i] == 'function' ? x[i](m) : new Template(x[i]).evaluate(m);
+              exclusion.push("(" + v.substring(1, v.length - 1) + ")");
+              e = e.replace(m[0], '');
+              break;
+            }
+          }
+        }
+        return "[not(" + exclusion.join(" and ") + ")]";
+      },
+      'nth-child':      function(m) {
+        return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
+      },
+      'nth-last-child': function(m) {
+        return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
+      },
+      'nth-of-type':    function(m) {
+        return Selector.xpath.pseudos.nth("position() ", m);
+      },
+      'nth-last-of-type': function(m) {
+        return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
+      },
+      'first-of-type':  function(m) {
+        m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
+      },
+      'last-of-type':   function(m) {
+        m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
+      },
+      'only-of-type':   function(m) {
+        var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
+      },
+      nth: function(fragment, m) {
+        var mm, formula = m[6], predicate;
+        if (formula == 'even') formula = '2n+0';
+        if (formula == 'odd')  formula = '2n+1';
+        if (mm = formula.match(/^(\d+)$/)) // digit only
+          return '[' + fragment + "= " + mm[1] + ']';
+        if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+          if (mm[1] == "-") mm[1] = -1;
+          var a = mm[1] ? Number(mm[1]) : 1;
+          var b = mm[2] ? Number(mm[2]) : 0;
+          predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
+          "((#{fragment} - #{b}) div #{a} >= 0)]";
+          return new Template(predicate).evaluate({
+            fragment: fragment, a: a, b: b });
+        }
+      }
+    }
+  },
+
+  criteria: {
+    tagName:      'n = h.tagName(n, r, "#{1}", c);   c = false;',
+    className:    'n = h.className(n, r, "#{1}", c); c = false;',
+    id:           'n = h.id(n, r, "#{1}", c);        c = false;',
+    attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;',
+    attr: function(m) {
+      m[3] = (m[5] || m[6]);
+      return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m);
+    },
+    pseudo:       function(m) {
+      if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
+      return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
+    },
+    descendant:   'c = "descendant";',
+    child:        'c = "child";',
+    adjacent:     'c = "adjacent";',
+    laterSibling: 'c = "laterSibling";'
+  },
+
+  patterns: {
+    // combinators must be listed first
+    // (and descendant needs to be last combinator)
+    laterSibling: /^\s*~\s*/,
+    child:        /^\s*>\s*/,
+    adjacent:     /^\s*\+\s*/,
+    descendant:   /^\s/,
+
+    // selectors follow
+    tagName:      /^\s*(\*|[\w\-]+)(\b|$)?/,
+    id:           /^#([\w\-\*]+)(\b|$)/,
+    className:    /^\.([\w\-\*]+)(\b|$)/,
+    pseudo:       /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|\s|(?=:))/,
+    attrPresence: /^\[([\w]+)\]/,
+    attr:         /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\]]*?)\4|([^'"][^\]]*?)))?\]/
+  },
+
+  handlers: {
+    // UTILITY FUNCTIONS
+    // joins two collections
+    concat: function(a, b) {
+      for (var i = 0, node; node = b[i]; i++)
+        a.push(node);
+      return a;
+    },
+
+    // marks an array of nodes for counting
+    mark: function(nodes) {
+      for (var i = 0, node; node = nodes[i]; i++)
+        node._counted = true;
+      return nodes;
+    },
+
+    unmark: function(nodes) {
+      for (var i = 0, node; node = nodes[i]; i++)
+        node._counted = undefined;
+      return nodes;
+    },
+
+    // mark each child node with its position (for nth calls)
+    // "ofType" flag indicates whether we're indexing for nth-of-type
+    // rather than nth-child
+    index: function(parentNode, reverse, ofType) {
+      parentNode._counted = true;
+      if (reverse) {
+        for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
+          node = nodes[i];
+          if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
+        }
+      } else {
+        for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
+          if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
+      }
+    },
+
+    // filters out duplicates and extends all nodes
+    unique: function(nodes) {
+      if (nodes.length == 0) return nodes;
+      var results = [], n;
+      for (var i = 0, l = nodes.length; i < l; i++)
+        if (!(n = nodes[i])._counted) {
+          n._counted = true;
+          results.push(Element.extend(n));
+        }
+      return Selector.handlers.unmark(results);
+    },
+
+    // COMBINATOR FUNCTIONS
+    descendant: function(nodes) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        h.concat(results, node.getElementsByTagName('*'));
+      return results;
+    },
+
+    child: function(nodes) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        for (var j = 0, children = [], child; child = node.childNodes[j]; j++)
+          if (child.nodeType == 1 && child.tagName != '!') results.push(child);
+      }
+      return results;
+    },
+
+    adjacent: function(nodes) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        var next = this.nextElementSibling(node);
+        if (next) results.push(next);
+      }
+      return results;
+    },
+
+    laterSibling: function(nodes) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        h.concat(results, Element.nextSiblings(node));
+      return results;
+    },
+
+    nextElementSibling: function(node) {
+      while (node = node.nextSibling)
+             if (node.nodeType == 1) return node;
+      return null;
+    },
+
+    previousElementSibling: function(node) {
+      while (node = node.previousSibling)
+        if (node.nodeType == 1) return node;
+      return null;
+    },
+
+    // TOKEN FUNCTIONS
+    tagName: function(nodes, root, tagName, combinator) {
+      tagName = tagName.toUpperCase();
+      var results = [], h = Selector.handlers;
+      if (nodes) {
+        if (combinator) {
+          // fastlane for ordinary descendant combinators
+          if (combinator == "descendant") {
+            for (var i = 0, node; node = nodes[i]; i++)
+              h.concat(results, node.getElementsByTagName(tagName));
+            return results;
+          } else nodes = this[combinator](nodes);
+          if (tagName == "*") return nodes;
+        }
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node.tagName.toUpperCase() == tagName) results.push(node);
+        return results;
+      } else return root.getElementsByTagName(tagName);
+    },
+
+    id: function(nodes, root, id, combinator) {
+      var targetNode = $(id), h = Selector.handlers;
+      if (!nodes && root == document) return targetNode ? [targetNode] : [];
+      if (nodes) {
+        if (combinator) {
+          if (combinator == 'child') {
+            for (var i = 0, node; node = nodes[i]; i++)
+              if (targetNode.parentNode == node) return [targetNode];
+          } else if (combinator == 'descendant') {
+            for (var i = 0, node; node = nodes[i]; i++)
+              if (Element.descendantOf(targetNode, node)) return [targetNode];
+          } else if (combinator == 'adjacent') {
+            for (var i = 0, node; node = nodes[i]; i++)
+              if (Selector.handlers.previousElementSibling(targetNode) == node)
+                return [targetNode];
+          } else nodes = h[combinator](nodes);
+        }
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node == targetNode) return [targetNode];
+        return [];
+      }
+      return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
+    },
+
+    className: function(nodes, root, className, combinator) {
+      if (nodes && combinator) nodes = this[combinator](nodes);
+      return Selector.handlers.byClassName(nodes, root, className);
+    },
+
+    byClassName: function(nodes, root, className) {
+      if (!nodes) nodes = Selector.handlers.descendant([root]);
+      var needle = ' ' + className + ' ';
+      for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
+        nodeClassName = node.className;
+        if (nodeClassName.length == 0) continue;
+        if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
+          results.push(node);
+      }
+      return results;
+    },
+
+    attrPresence: function(nodes, root, attr) {
+      var results = [];
+      for (var i = 0, node; node = nodes[i]; i++)
+        if (Element.hasAttribute(node, attr)) results.push(node);
+      return results;
+    },
+
+    attr: function(nodes, root, attr, value, operator) {
+      if (!nodes) nodes = root.getElementsByTagName("*");
+      var handler = Selector.operators[operator], results = [];
+      for (var i = 0, node; node = nodes[i]; i++) {
+        var nodeValue = Element.readAttribute(node, attr);
+        if (nodeValue === null) continue;
+        if (handler(nodeValue, value)) results.push(node);
+      }
+      return results;
+    },
+
+    pseudo: function(nodes, name, value, root, combinator) {
+      if (nodes && combinator) nodes = this[combinator](nodes);
+      if (!nodes) nodes = root.getElementsByTagName("*");
+      return Selector.pseudos[name](nodes, value, root);
+    }
+  },
+
+  pseudos: {
+    'first-child': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        if (Selector.handlers.previousElementSibling(node)) continue;
+          results.push(node);
+      }
+      return results;
+    },
+    'last-child': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        if (Selector.handlers.nextElementSibling(node)) continue;
+          results.push(node);
+      }
+      return results;
+    },
+    'only-child': function(nodes, value, root) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
+          results.push(node);
+      return results;
+    },
+    'nth-child':        function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root);
+    },
+    'nth-last-child':   function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root, true);
+    },
+    'nth-of-type':      function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root, false, true);
+    },
+    'nth-last-of-type': function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root, true, true);
+    },
+    'first-of-type':    function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, "1", root, false, true);
+    },
+    'last-of-type':     function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, "1", root, true, true);
+    },
+    'only-of-type':     function(nodes, formula, root) {
+      var p = Selector.pseudos;
+      return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
+    },
+
+    // handles the an+b logic
+    getIndices: function(a, b, total) {
+      if (a == 0) return b > 0 ? [b] : [];
+      return $R(1, total).inject([], function(memo, i) {
+        if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
+        return memo;
+      });
+    },
+
+    // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
+    nth: function(nodes, formula, root, reverse, ofType) {
+      if (nodes.length == 0) return [];
+      if (formula == 'even') formula = '2n+0';
+      if (formula == 'odd')  formula = '2n+1';
+      var h = Selector.handlers, results = [], indexed = [], m;
+      h.mark(nodes);
+      for (var i = 0, node; node = nodes[i]; i++) {
+        if (!node.parentNode._counted) {
+          h.index(node.parentNode, reverse, ofType);
+          indexed.push(node.parentNode);
+        }
+      }
+      if (formula.match(/^\d+$/)) { // just a number
+        formula = Number(formula);
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node.nodeIndex == formula) results.push(node);
+      } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+        if (m[1] == "-") m[1] = -1;
+        var a = m[1] ? Number(m[1]) : 1;
+        var b = m[2] ? Number(m[2]) : 0;
+        var indices = Selector.pseudos.getIndices(a, b, nodes.length);
+        for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
+          for (var j = 0; j < l; j++)
+            if (node.nodeIndex == indices[j]) results.push(node);
+        }
+      }
+      h.unmark(nodes);
+      h.unmark(indexed);
+      return results;
+    },
+
+    'empty': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        // IE treats comments as element nodes
+        if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
+        results.push(node);
+      }
+      return results;
+    },
+
+    'not': function(nodes, selector, root) {
+      var h = Selector.handlers, selectorType, m;
+      var exclusions = new Selector(selector).findElements(root);
+      h.mark(exclusions);
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (!node._counted) results.push(node);
+      h.unmark(exclusions);
+      return results;
+    },
+
+    'enabled': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (!node.disabled) results.push(node);
+      return results;
+    },
+
+    'disabled': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (node.disabled) results.push(node);
+      return results;
+    },
+
+    'checked': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (node.checked) results.push(node);
+      return results;
+    }
+  },
+
+  operators: {
+    '=':  function(nv, v) { return nv == v; },
+    '!=': function(nv, v) { return nv != v; },
+    '^=': function(nv, v) { return nv.startsWith(v); },
+    '$=': function(nv, v) { return nv.endsWith(v); },
+    '*=': function(nv, v) { return nv.include(v); },
+    '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
+    '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
+  },
+
+  matchElements: function(elements, expression) {
+    var matches = new Selector(expression).findElements(), h = Selector.handlers;
+    h.mark(matches);
+    for (var i = 0, results = [], element; element = elements[i]; i++)
+      if (element._counted) results.push(element);
+    h.unmark(matches);
+    return results;
+  },
+
+  findElement: function(elements, expression, index) {
+    if (typeof expression == 'number') {
+      index = expression; expression = false;
+    }
+    return Selector.matchElements(elements, expression || '*')[index || 0];
+  },
+
+  findChildElements: function(element, expressions) {
+    var exprs = expressions.join(','), expressions = [];
+    exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
+      expressions.push(m[1].strip());
+    });
+    var results = [], h = Selector.handlers;
+    for (var i = 0, l = expressions.length, selector; i < l; i++) {
+      selector = new Selector(expressions[i].strip());
+      h.concat(results, selector.findElements(element));
+    }
+    return (l > 1) ? h.unique(results) : results;
+  }
+});
+
+function $$() {
+  return Selector.findChildElements(document, $A(arguments));
+}
+var Form = {
+  reset: function(form) {
+    $(form).reset();
+    return form;
+  },
+
+  serializeElements: function(elements, getHash) {
+    var data = elements.inject({}, function(result, element) {
+      if (!element.disabled && element.name) {
+        var key = element.name, value = $(element).getValue();
+        if (value != null) {
+               if (key in result) {
+            if (result[key].constructor != Array) result[key] = [result[key]];
+            result[key].push(value);
+          }
+          else result[key] = value;
+        }
+      }
+      return result;
+    });
+
+    return getHash ? data : Hash.toQueryString(data);
+  }
+};
+
+Form.Methods = {
+  serialize: function(form, getHash) {
+    return Form.serializeElements(Form.getElements(form), getHash);
+  },
+
+  getElements: function(form) {
+    return $A($(form).getElementsByTagName('*')).inject([],
+      function(elements, child) {
+        if (Form.Element.Serializers[child.tagName.toLowerCase()])
+          elements.push(Element.extend(child));
+        return elements;
+      }
+    );
+  },
+
+  getInputs: function(form, typeName, name) {
+    form = $(form);
+    var inputs = form.getElementsByTagName('input');
+
+    if (!typeName && !name) return $A(inputs).map(Element.extend);
+
+    for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
+      var input = inputs[i];
+      if ((typeName && input.type != typeName) || (name && input.name != name))
+        continue;
+      matchingInputs.push(Element.extend(input));
+    }
+
+    return matchingInputs;
+  },
+
+  disable: function(form) {
+    form = $(form);
+    Form.getElements(form).invoke('disable');
+    return form;
+  },
+
+  enable: function(form) {
+    form = $(form);
+    Form.getElements(form).invoke('enable');
+    return form;
+  },
+
+  findFirstElement: function(form) {
+    return $(form).getElements().find(function(element) {
+      return element.type != 'hidden' && !element.disabled &&
+        ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
+    });
+  },
+
+  focusFirstElement: function(form) {
+    form = $(form);
+    form.findFirstElement().activate();
+    return form;
+  },
+
+  request: function(form, options) {
+    form = $(form), options = Object.clone(options || {});
+
+    var params = options.parameters;
+    options.parameters = form.serialize(true);
+
+    if (params) {
+      if (typeof params == 'string') params = params.toQueryParams();
+      Object.extend(options.parameters, params);
+    }
+
+    if (form.hasAttribute('method') && !options.method)
+      options.method = form.method;
+
+    return new Ajax.Request(form.readAttribute('action'), options);
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element = {
+  focus: function(element) {
+    $(element).focus();
+    return element;
+  },
+
+  select: function(element) {
+    $(element).select();
+    return element;
+  }
+}
+
+Form.Element.Methods = {
+  serialize: function(element) {
+    element = $(element);
+    if (!element.disabled && element.name) {
+      var value = element.getValue();
+      if (value != undefined) {
+        var pair = {};
+        pair[element.name] = value;
+        return Hash.toQueryString(pair);
+      }
+    }
+    return '';
+  },
+
+  getValue: function(element) {
+    element = $(element);
+    var method = element.tagName.toLowerCase();
+    return Form.Element.Serializers[method](element);
+  },
+
+  clear: function(element) {
+    $(element).value = '';
+    return element;
+  },
+
+  present: function(element) {
+    return $(element).value != '';
+  },
+
+  activate: function(element) {
+    element = $(element);
+    try {
+      element.focus();
+      if (element.select && (element.tagName.toLowerCase() != 'input' ||
+        !['button', 'reset', 'submit'].include(element.type)))
+        element.select();
+    } catch (e) {}
+    return element;
+  },
+
+  disable: function(element) {
+    element = $(element);
+    element.blur();
+    element.disabled = true;
+    return element;
+  },
+
+  enable: function(element) {
+    element = $(element);
+    element.disabled = false;
+    return element;
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Field = Form.Element;
+var $F = Form.Element.Methods.getValue;
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element.Serializers = {
+  input: function(element) {
+    switch (element.type.toLowerCase()) {
+      case 'checkbox':
+      case 'radio':
+        return Form.Element.Serializers.inputSelector(element);
+      default:
+        return Form.Element.Serializers.textarea(element);
+    }
+  },
+
+  inputSelector: function(element) {
+    return element.checked ? element.value : null;
+  },
+
+  textarea: function(element) {
+    return element.value;
+  },
+
+  select: function(element) {
+    return this[element.type == 'select-one' ?
+      'selectOne' : 'selectMany'](element);
+  },
+
+  selectOne: function(element) {
+    var index = element.selectedIndex;
+    return index >= 0 ? this.optionValue(element.options[index]) : null;
+  },
+
+  selectMany: function(element) {
+    var values, length = element.length;
+    if (!length) return null;
+
+    for (var i = 0, values = []; i < length; i++) {
+      var opt = element.options[i];
+      if (opt.selected) values.push(this.optionValue(opt));
+    }
+    return values;
+  },
+
+  optionValue: function(opt) {
+    // extend element because hasAttribute may not be native
+    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.TimedObserver = function() {}
+Abstract.TimedObserver.prototype = {
+  initialize: function(element, frequency, callback) {
+    this.frequency = frequency;
+    this.element   = $(element);
+    this.callback  = callback;
+
+    this.lastValue = this.getValue();
+    this.registerCallback();
+  },
+
+  registerCallback: function() {
+    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+  },
+
+  onTimerEvent: function() {
+    var value = this.getValue();
+    var changed = ('string' == typeof this.lastValue && 'string' == typeof value
+      ? this.lastValue != value : String(this.lastValue) != String(value));
+    if (changed) {
+      this.callback(this.element, value);
+      this.lastValue = value;
+    }
+  }
+}
+
+Form.Element.Observer = Class.create();
+Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+  getValue: function() {
+    return Form.Element.getValue(this.element);
+  }
+});
+
+Form.Observer = Class.create();
+Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+  getValue: function() {
+    return Form.serialize(this.element);
+  }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.EventObserver = function() {}
+Abstract.EventObserver.prototype = {
+  initialize: function(element, callback) {
+    this.element  = $(element);
+    this.callback = callback;
+
+    this.lastValue = this.getValue();
+    if (this.element.tagName.toLowerCase() == 'form')
+      this.registerFormCallbacks();
+    else
+      this.registerCallback(this.element);
+  },
+
+  onElementEvent: function() {
+    var value = this.getValue();
+    if (this.lastValue != value) {
+      this.callback(this.element, value);
+      this.lastValue = value;
+    }
+  },
+
+  registerFormCallbacks: function() {
+    Form.getElements(this.element).each(this.registerCallback.bind(this));
+  },
+
+  registerCallback: function(element) {
+    if (element.type) {
+      switch (element.type.toLowerCase()) {
+        case 'checkbox':
+        case 'radio':
+          Event.observe(element, 'click', this.onElementEvent.bind(this));
+          break;
+        default:
+          Event.observe(element, 'change', this.onElementEvent.bind(this));
+          break;
+      }
+    }
+  }
+}
+
+Form.Element.EventObserver = Class.create();
+Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+  getValue: function() {
+    return Form.Element.getValue(this.element);
+  }
+});
+
+Form.EventObserver = Class.create();
+Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+  getValue: function() {
+    return Form.serialize(this.element);
+  }
+});
+if (!window.Event) {
+  var Event = new Object();
+}
+
+Object.extend(Event, {
+  KEY_BACKSPACE: 8,
+  KEY_TAB:       9,
+  KEY_RETURN:   13,
+  KEY_ESC:      27,
+  KEY_LEFT:     37,
+  KEY_UP:       38,
+  KEY_RIGHT:    39,
+  KEY_DOWN:     40,
+  KEY_DELETE:   46,
+  KEY_HOME:     36,
+  KEY_END:      35,
+  KEY_PAGEUP:   33,
+  KEY_PAGEDOWN: 34,
+
+  element: function(event) {
+    return $(event.target || event.srcElement);
+  },
+
+  isLeftClick: function(event) {
+    return (((event.which) && (event.which == 1)) ||
+            ((event.button) && (event.button == 1)));
+  },
+
+  pointerX: function(event) {
+    return event.pageX || (event.clientX +
+      (document.documentElement.scrollLeft || document.body.scrollLeft));
+  },
+
+  pointerY: function(event) {
+    return event.pageY || (event.clientY +
+      (document.documentElement.scrollTop || document.body.scrollTop));
+  },
+
+  stop: function(event) {
+    if (event.preventDefault) {
+      event.preventDefault();
+      event.stopPropagation();
+    } else {
+      event.returnValue = false;
+      event.cancelBubble = true;
+    }
+  },
+
+  // find the first node with the given tagName, starting from the
+  // node the event was triggered on; traverses the DOM upwards
+  findElement: function(event, tagName) {
+    var element = Event.element(event);
+    while (element.parentNode && (!element.tagName ||
+        (element.tagName.toUpperCase() != tagName.toUpperCase())))
+      element = element.parentNode;
+    return element;
+  },
+
+  observers: false,
+
+  _observeAndCache: function(element, name, observer, useCapture) {
+    if (!this.observers) this.observers = [];
+    if (element.addEventListener) {
+      this.observers.push([element, name, observer, useCapture]);
+      element.addEventListener(name, observer, useCapture);
+    } else if (element.attachEvent) {
+      this.observers.push([element, name, observer, useCapture]);
+      element.attachEvent('on' + name, observer);
+    }
+  },
+
+  unloadCache: function() {
+    if (!Event.observers) return;
+    for (var i = 0, length = Event.observers.length; i < length; i++) {
+      Event.stopObserving.apply(this, Event.observers[i]);
+      Event.observers[i][0] = null;
+    }
+    Event.observers = false;
+  },
+
+  observe: function(element, name, observer, useCapture) {
+    element = $(element);
+    useCapture = useCapture || false;
+
+    if (name == 'keypress' &&
+      (Prototype.Browser.WebKit || element.attachEvent))
+      name = 'keydown';
+
+    Event._observeAndCache(element, name, observer, useCapture);
+  },
+
+  stopObserving: function(element, name, observer, useCapture) {
+    element = $(element);
+    useCapture = useCapture || false;
+
+    if (name == 'keypress' &&
+        (Prototype.Browser.WebKit || element.attachEvent))
+      name = 'keydown';
+
+    if (element.removeEventListener) {
+      element.removeEventListener(name, observer, useCapture);
+    } else if (element.detachEvent) {
+      try {
+        element.detachEvent('on' + name, observer);
+      } catch (e) {}
+    }
+  }
+});
+
+/* prevent memory leaks in IE */
+if (Prototype.Browser.IE)
+  Event.observe(window, 'unload', Event.unloadCache, false);
+var Position = {
+  // set to true if needed, warning: firefox performance problems
+  // NOT neeeded for page scrolling, only if draggable contained in
+  // scrollable elements
+  includeScrollOffsets: false,
+
+  // must be called before calling withinIncludingScrolloffset, every time the
+  // page is scrolled
+  prepare: function() {
+    this.deltaX =  window.pageXOffset
+                || document.documentElement.scrollLeft
+                || document.body.scrollLeft
+                || 0;
+    this.deltaY =  window.pageYOffset
+                || document.documentElement.scrollTop
+                || document.body.scrollTop
+                || 0;
+  },
+
+  realOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.scrollTop  || 0;
+      valueL += element.scrollLeft || 0;
+      element = element.parentNode;
+    } while (element);
+    return [valueL, valueT];
+  },
+
+  cumulativeOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+    } while (element);
+    return [valueL, valueT];
+  },
+
+  positionedOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+      if (element) {
+        if(element.tagName=='BODY') break;
+        var p = Element.getStyle(element, 'position');
+        if (p == 'relative' || p == 'absolute') break;
+      }
+    } while (element);
+    return [valueL, valueT];
+  },
+
+  offsetParent: function(element) {
+    if (element.offsetParent) return element.offsetParent;
+    if (element == document.body) return element;
+
+    while ((element = element.parentNode) && element != document.body)
+      if (Element.getStyle(element, 'position') != 'static')
+        return element;
+
+    return document.body;
+  },
+
+  // caches x/y coordinate pair to use with overlap
+  within: function(element, x, y) {
+    if (this.includeScrollOffsets)
+      return this.withinIncludingScrolloffsets(element, x, y);
+    this.xcomp = x;
+    this.ycomp = y;
+    this.offset = this.cumulativeOffset(element);
+
+    return (y >= this.offset[1] &&
+            y <  this.offset[1] + element.offsetHeight &&
+            x >= this.offset[0] &&
+            x <  this.offset[0] + element.offsetWidth);
+  },
+
+  withinIncludingScrolloffsets: function(element, x, y) {
+    var offsetcache = this.realOffset(element);
+
+    this.xcomp = x + offsetcache[0] - this.deltaX;
+    this.ycomp = y + offsetcache[1] - this.deltaY;
+    this.offset = this.cumulativeOffset(element);
+
+    return (this.ycomp >= this.offset[1] &&
+            this.ycomp <  this.offset[1] + element.offsetHeight &&
+            this.xcomp >= this.offset[0] &&
+            this.xcomp <  this.offset[0] + element.offsetWidth);
+  },
+
+  // within must be called directly before
+  overlap: function(mode, element) {
+    if (!mode) return 0;
+    if (mode == 'vertical')
+      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
+        element.offsetHeight;
+    if (mode == 'horizontal')
+      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
+        element.offsetWidth;
+  },
+
+  page: function(forElement) {
+    var valueT = 0, valueL = 0;
+
+    var element = forElement;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+
+      // Safari fix
+      if (element.offsetParent == document.body)
+        if (Element.getStyle(element,'position')=='absolute') break;
+
+    } while (element = element.offsetParent);
+
+    element = forElement;
+    do {
+      if (!window.opera || element.tagName=='BODY') {
+        valueT -= element.scrollTop  || 0;
+        valueL -= element.scrollLeft || 0;
+      }
+    } while (element = element.parentNode);
+
+    return [valueL, valueT];
+  },
+
+  clone: function(source, target) {
+    var options = Object.extend({
+      setLeft:    true,
+      setTop:     true,
+      setWidth:   true,
+      setHeight:  true,
+      offsetTop:  0,
+      offsetLeft: 0
+    }, arguments[2] || {})
+
+    // find page position of source
+    source = $(source);
+    var p = Position.page(source);
+
+    // find coordinate system to use
+    target = $(target);
+    var delta = [0, 0];
+    var parent = null;
+    // delta [0,0] will do fine with position: fixed elements,
+    // position:absolute needs offsetParent deltas
+    if (Element.getStyle(target,'position') == 'absolute') {
+      parent = Position.offsetParent(target);
+      delta = Position.page(parent);
+    }
+
+    // correct by body offsets (fixes Safari)
+    if (parent == document.body) {
+      delta[0] -= document.body.offsetLeft;
+      delta[1] -= document.body.offsetTop;
+    }
+
+    // set position
+    if(options.setLeft)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
+    if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
+    if(options.setWidth)  target.style.width = source.offsetWidth + 'px';
+    if(options.setHeight) target.style.height = source.offsetHeight + 'px';
+  },
+
+  absolutize: function(element) {
+    element = $(element);
+    if (element.style.position == 'absolute') return;
+    Position.prepare();
+
+    var offsets = Position.positionedOffset(element);
+    var top     = offsets[1];
+    var left    = offsets[0];
+    var width   = element.clientWidth;
+    var height  = element.clientHeight;
+
+    element._originalLeft   = left - parseFloat(element.style.left  || 0);
+    element._originalTop    = top  - parseFloat(element.style.top || 0);
+    element._originalWidth  = element.style.width;
+    element._originalHeight = element.style.height;
+
+    element.style.position = 'absolute';
+    element.style.top    = top + 'px';
+    element.style.left   = left + 'px';
+    element.style.width  = width + 'px';
+    element.style.height = height + 'px';
+  },
+
+  relativize: function(element) {
+    element = $(element);
+    if (element.style.position == 'relative') return;
+    Position.prepare();
+
+    element.style.position = 'relative';
+    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
+    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
+
+    element.style.top    = top + 'px';
+    element.style.left   = left + 'px';
+    element.style.height = element._originalHeight;
+    element.style.width  = element._originalWidth;
+  }
+}
+
+// Safari returns margins on body which is incorrect if the child is absolutely
+// positioned.  For performance reasons, redefine Position.cumulativeOffset for
+// KHTML/WebKit only.
+if (Prototype.Browser.WebKit) {
+  Position.cumulativeOffset = function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      if (element.offsetParent == document.body)
+        if (Element.getStyle(element, 'position') == 'absolute') break;
+
+      element = element.offsetParent;
+    } while (element);
+
+    return [valueL, valueT];
+  }
+}
+
+Element.addMethods();
\ No newline at end of file
diff --git a/NP_TrackBack/trunk/trackback/js/rico/css/coffee-with-milk.css b/NP_TrackBack/trunk/trackback/js/rico/css/coffee-with-milk.css
new file mode 100644 (file)
index 0000000..86a1998
--- /dev/null
@@ -0,0 +1,73 @@
+/*\r
+Based on "Coffee with milk" table design by Roger Johansson, 456 Berea Street\r
+www.456bereastreet.com\r
+================================================*/\r
+\r
+.ricoLG_table {\r
+       border-top:1px solid #523A0B !important;\r
+       border-right:none;\r
+       font:normal 76%/150% "Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif;\r
+       color:#000;\r
+}\r
+tr.ricoLG_hdg .ricoLG_cell, tr.ricoLG_hdg th, tr.ricoLG_hdg td {  /* td/th required for IE */\r
+       background:#EBE5D9 !important;\r
+       line-height:normal;\r
+       text-align:left;\r
+}\r
+\r
+tr.ricoLG_hdg th, tr.ricoLG_hdg td {\r
+       border-bottom:1px solid #523A0B;\r
+       background:#EBE5D9;\r
+       }\r
+\r
+tr.ricoLG_hdg th, tr.ricoLG_hdg td {\r
+  border-left: 1px solid #E0D8CD !important;\r
+}\r
+\r
+.ricoLG_bottom th, .ricoLG_bottom td {\r
+  border-left: 1px solid #FFF;\r
+}\r
+\r
+tr.ricoLG_hdg div.ricoLG_cell {\r
+       background:#EBE5D9;\r
+       font-weight:bold;\r
+       padding:0.5em 0 0.5em 0.5em;\r
+}\r
+div.ricoLG_outerDiv table a {\r
+       color:#523A0B;\r
+       text-decoration:none;\r
+       border-bottom:1px dotted;\r
+       }\r
+div.ricoLG_outerDiv tbody a:visited {\r
+       color:#444;\r
+       font-weight:normal;\r
+       }\r
+div.ricoLG_outerDiv table a:hover {\r
+       border-bottom-style:solid;\r
+       }\r
+\r
+.ricoLG_bottom div.ricoLG_oddRow {\r
+       background-color:#F7F4EE;\r
+       border-top: 1px solid #EBE5D9;\r
+       border-bottom: 1px solid #EBE5D9;\r
+}\r
+.ricoLG_bottom div.ricoLG_evenRow {\r
+       border-top: 1px solid #FFF;\r
+       border-bottom: 1px solid #FFF;\r
+}\r
+.ricoLG_selection {\r
+       background-color:#ffffee !important;\r
+       border-color:#523A0B !important;\r
+}\r
+.ricoLG_table {\r
+  border-style:none;\r
+}\r
+\r
+caption {\r
+       font-family:Georgia,Times,serif;\r
+       font-weight:normal;\r
+       font-size:1.4em;\r
+       text-align:left;\r
+       margin:0;\r
+       padding:0.5em 0.25em;\r
+       }
\ No newline at end of file
diff --git a/NP_TrackBack/trunk/trackback/js/rico/css/grayedout.css b/NP_TrackBack/trunk/trackback/js/rico/css/grayedout.css
new file mode 100644 (file)
index 0000000..d5a0dd4
--- /dev/null
@@ -0,0 +1,68 @@
+/* -------------------------------------------------------\r
+Based on Grayed Out table design\r
+Author: Terence Ordona\r
+URL: http://www.imaputz.com/\r
+ ------------------------------------------------------- */\r
+div.ricoLG_outerDiv *, div.ricoLG_cell {\r
+  font-size: 11px;\r
+  font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;\r
+}\r
+\r
+.ricoLG_table {\r
+       border-top: 1px solid #CCC;\r
+       border-right: 1px solid #CCC;\r
+}\r
+\r
+tr.ricoLG_hdg th, tr.ricoLG_hdg td {\r
+       background-color: #FFF !important;\r
+  background: url(../images/grayedout.gif) #FFF repeat-x scroll center left;\r
+  border-bottom: 1px solid #CCC;\r
+}\r
+\r
+.ricoLG_table th, .ricoLG_table td {\r
+       border-left: 1px solid #CCC;\r
+}\r
+\r
+.ricoLG_bottom th, .ricoLG_bottom td {\r
+       border-bottom: 1px solid #CCC;\r
+}\r
+\r
+.ricoLG_bottom div.ricoLG_cell {\r
+  border-bottom: none;\r
+  padding: 5px;\r
+}\r
+\r
+tr.ricoLG_hdg .ricoLG_cell {\r
+  font-weight: normal;\r
+}\r
+\r
+div.ricoLG_outerDiv a:visited, div.ricoLG_outerDiv a:link {\r
+       color: #009;\r
+       text-decoration: none;\r
+}\r
+\r
+div.ricoLG_outerDiv a:hover {\r
+       color: #009;\r
+       text-decoration: underline;\r
+}\r
+\r
+.ricoLG_oddRow {\r
+       background-color: #EEE;\r
+}\r
+\r
+div.ricoLG_selection {\r
+       background-color: #999;\r
+       color: #FFF;\r
+}\r
+\r
+div.ricoLG_highlightDiv {\r
+       border-color: #999;\r
+}\r
+       \r
+caption {\r
+       text-align: left;\r
+       font-size: 100%;\r
+       padding: .75em;\r
+       color: #000;\r
+}\r
+\r
diff --git a/NP_TrackBack/trunk/trackback/js/rico/css/greenHdg.css b/NP_TrackBack/trunk/trackback/js/rico/css/greenHdg.css
new file mode 100644 (file)
index 0000000..f46dfb1
--- /dev/null
@@ -0,0 +1,8 @@
+/* display grid headings with a green background */\r
+\r
+tr.ricoLG_hdg .ricoLG_cell, tr.ricoLG_hdg th, tr.ricoLG_hdg td {  /* td/th required for IE */\r
+  background-color : #cedebd !important;\r
+  color            : #000000;\r
+  font-weight      : bold;\r
+}\r
+div.ricoLG_selection { background-color: #cedebd; }\r
diff --git a/NP_TrackBack/trunk/trackback/js/rico/css/iegradient.css b/NP_TrackBack/trunk/trackback/js/rico/css/iegradient.css
new file mode 100644 (file)
index 0000000..6d4b80e
--- /dev/null
@@ -0,0 +1,3 @@
+tr.ricoLG_hdg .ricoLG_cell, tr.ricoLG_hdg th, tr.ricoLG_hdg td {  /* td/th required for IE */\r
+  Filter: progid:DXImageTransform.Microsoft.Gradient(gradientType=0,startColorStr=white,endColorStr=Gainsboro); \r
+}\r
diff --git a/NP_TrackBack/trunk/trackback/js/rico/css/ricoCalendar.css b/NP_TrackBack/trunk/trackback/js/rico/css/ricoCalendar.css
new file mode 100644 (file)
index 0000000..18c3ad4
--- /dev/null
@@ -0,0 +1,112 @@
+/* ricoCalendar */\r
+\r
+div.ricoCalContainer, div.ricoTreeContainer {\r
+  position:absolute;\r
+  z-index:9999;\r
+  font-size:8pt;\r
+  left:0px;\r
+  top:0px;\r
+}\r
+\r
+table.ricoCalTab {\r
+  border:1px solid #666666;\r
+}\r
+\r
+table.ricoCalTab thead a {\r
+  border:1px solid #D4D0C8;\r
+  text-decoration: none;\r
+  color:black;\r
+}\r
+\r
+table.ricoCalTab thead img {\r
+  border:none;\r
+  padding-left: 0.3em;\r
+  padding-right: 0.3em;\r
+}\r
+\r
+table.ricoCalTab thead a:hover {\r
+  border:1px solid #666666;\r
+  cursor:pointer;\r
+}\r
+\r
+table.ricoCalTab thead td {\r
+  background-color: #D4D0C8;\r
+  font-weight: bold;\r
+  text-align:center;\r
+  padding: 2px;\r
+}\r
+\r
+table.ricoCalTab tfoot td {\r
+  color:#FFF;\r
+  text-align:center;\r
+  background-color: #666666;\r
+  padding: 2px;\r
+}\r
+\r
+table.ricoCalTab tfoot span {\r
+  text-decoration: underline;\r
+  cursor:pointer;\r
+}\r
+\r
+table.ricoCalTab tbody {\r
+  background-color: white;\r
+}\r
+\r
+tr.ricoCalDayNames td {\r
+  font-weight: bold;\r
+  padding: 0px 2px 0px 2px;\r
+  text-align:right;\r
+}\r
+\r
+td.ricoCal0, td.ricoCal1, td.ricoCal2, td.ricoCal3, td.ricoCal4, td.ricoCal5, td.ricoCal6, td.ricoCalToday, td.ricoCalEmpty {\r
+  text-decoration:none;\r
+  text-align:right;\r
+  width:3em;\r
+}\r
+\r
+/* Monday-Friday */\r
+td.ricoCal1, td.ricoCal2, td.ricoCal3, td.ricoCal4, td.ricoCal5 {\r
+  cursor:pointer;\r
+  color:black;\r
+}\r
+\r
+/* Sunday, Saturday */\r
+td.ricoCal0, td.ricoCal6 {\r
+  cursor:pointer;\r
+  color:#999;\r
+}\r
+\r
+td.ricoCalToday {\r
+  cursor:pointer;\r
+  color:red;\r
+  font-weight:bold;\r
+}\r
+\r
+td.ricoCalWeekNum {\r
+  background-color: #D4D0C8;\r
+  color:black;\r
+  text-align:center;\r
+}\r
+\r
+.ricoCalMenu {\r
+  position:absolute;\r
+  background-color: #FEE;\r
+  border-bottom:1px solid #666666;\r
+  border-right:1px solid #666666;\r
+}\r
+\r
+.ricoCalMenu td {\r
+  border-top:1px solid #666666;\r
+  border-left:1px solid #666666;\r
+}\r
+\r
+.ricoCalMenu a {\r
+  display:block;\r
+  text-decoration:none;\r
+  color:black;\r
+  cursor:pointer;\r
+}\r
+\r
+.ricoCalMenu a:hover {\r
+  background-color: #FCC;\r
+}\r
diff --git a/NP_TrackBack/trunk/trackback/js/rico/css/ricoGrid.css b/NP_TrackBack/trunk/trackback/js/rico/css/ricoGrid.css
new file mode 100644 (file)
index 0000000..6760ce6
--- /dev/null
@@ -0,0 +1,125 @@
+div.ricoLG_outerDiv {\r
+  position:relative;\r
+  /*border:thin solid blue;  /* for debugging */\r
+}\r
+\r
+div.ricoLG_innerDiv, div.ricoLG_frozenTabsDiv {\r
+  overflow:hidden;\r
+  margin:0px;\r
+  padding:0px;\r
+  position:absolute;\r
+  top:0px;\r
+}\r
+\r
+div.ricoLG_scrollDiv {\r
+  overflow:scroll;\r
+  position:relative;\r
+}\r
+\r
+div.ricoLG_scrollTabsDiv {\r
+  position:absolute;\r
+  top:0px;\r
+}\r
+\r
+div.ricoLG_resizeDiv {\r
+  position:absolute;\r
+  top:0px;\r
+  width:1px;\r
+  z-index:2;\r
+  background-color:blue;\r
+}\r
+\r
+div.ricoLG_highlightDiv {\r
+  position:absolute;\r
+  border: 2px solid black;\r
+}\r
+\r
+.ricoLG_table {\r
+  margin: 0px;\r
+  padding: 0px;\r
+  border-right: 1px solid silver;\r
+  border-top: 1px solid silver;\r
+}\r
+\r
+.ricoLG_table th, .ricoLG_table td {\r
+  border-left: 1px solid silver;\r
+}\r
+\r
+table.ricoLG_bottom {\r
+  border-top-style: none;\r
+}\r
+\r
+.ricoLG_evenRow   { }\r
+.ricoLG_oddRow    { background-color: #EEE; }\r
+.ricoLG_selection { background-color: #cedebd; }\r
+\r
+div.ricoLG_col {\r
+  overflow:hidden;\r
+  width:100px;\r
+}\r
+\r
+.ricoLG_top div.ricoLG_col {\r
+  position:relative;\r
+}\r
+\r
+.ricoLG_top div.ricoLG_Resize {\r
+  position:absolute;\r
+  width:5px;\r
+  height:100%;\r
+  top:0px;\r
+  cursor:e-resize;\r
+}\r
+\r
+.ricoLG_HdrIcon {\r
+  padding-left:2px;\r
+  padding-right:2px;\r
+}\r
+\r
+.ricoLG_bottom div.ricoLG_cell, .ricoLG_top th, .ricoLG_top td {\r
+  border-bottom: 1px solid silver;\r
+}\r
+\r
+div.ricoLG_cell {\r
+  overflow:hidden;\r
+  height:1.2em;\r
+  padding-left: 3px;\r
+  margin: 0px;\r
+  font-size: 10pt;\r
+       padding-top:3px;\r
+       padding-bottom:3px;\r
+}\r
+\r
+div.ricoLG_messageDiv {\r
+  position:absolute;\r
+  z-index:200;\r
+  border:1px solid green;\r
+  background-color:white;\r
+  font-weight:bold;\r
+  font-size:larger;\r
+  color:navy;\r
+  text-align:center;\r
+  padding:4px;\r
+}\r
+\r
+p.ricoBookmark {\r
+  margin-bottom: 3px;\r
+  font-size: 10pt;\r
+}\r
+\r
+div.alignleft {\r
+  text-align: left;\r
+}\r
+\r
+div.aligncenter {\r
+  text-align: center;\r
+}\r
+\r
+div.alignright {\r
+  text-align: right;\r
+}\r
+\r
+span.ricoSessionTimer {\r
+  background-color:black;\r
+  color:white;\r
+}\r
+\r
diff --git a/NP_TrackBack/trunk/trackback/js/rico/css/ricoLiveGridForms.css b/NP_TrackBack/trunk/trackback/js/rico/css/ricoLiveGridForms.css
new file mode 100644 (file)
index 0000000..e43e729
--- /dev/null
@@ -0,0 +1,81 @@
+/* ricoLiveGridForms */\r
+\r
+span.ricoSaveMsg {\r
+  background-color:yellow;\r
+}\r
+\r
+span.ricoSessionTimer {\r
+  background-color:black;\r
+  color:white;\r
+}\r
+\r
+div.ricoLG_editDiv, div.ricoLG_editResponseDiv {\r
+  color:#000; background:#E8ECF3;\r
+  overflow:auto;\r
+  padding:8px;\r
+  border: 1px solid navy;\r
+  position:absolute;\r
+  font-size: 10pt;\r
+  z-index:300;\r
+  top:0px;\r
+  left:0px;\r
+}\r
+\r
+form .ricoEditLabel, form .ricoEditLabelWithHelp {\r
+  font-weight: bold;\r
+  text-align: left;\r
+  padding-right: 1em;\r
+}\r
+\r
+form .ricoEditLabelWithHelp {\r
+  color: navy;\r
+}\r
+\r
+form {\r
+  margin:0px;\r
+}\r
+\r
+.tabHeader {\r
+  height: 1.8em;
+       color : #AAA;\r
+       background: #D8E0F2;
+       font-weight : bold;
+  float: left;
+  display: inline;
+  margin-left: 2px;
+  margin-right: 2px;\r
+  text-align: center;
+  white-space:nowrap;
+  overflow:hidden;
+}\r
+\r
+.tabHover {
+       color : #666;
+  cursor: pointer;
+}
+
+.tabSelected {
+       color : #444;
+       background: #CFD4E6;
+  cursor: auto;
+}
+
+.tabContentContainer {
+  clear:both;
+}\r
+\r
+div.ricoLG_editDiv .tabContent, div.ricoLG_editDiv .noTabContent {\r
+  color:#000; background:#CFD4E6;\r
+  overflow: hidden;\r
+  padding: 4px;\r
+  white-space:nowrap;\r
+}\r
+\r
+div.ricoLG_editDiv .noTabContent {\r
+  float:left;  /* required by IE7 */\r
+}\r
+\r
+span.ricoLookup {\r
+  display:none;\r
+}\r
+\r
diff --git a/NP_TrackBack/trunk/trackback/js/rico/css/ricoMenu.css b/NP_TrackBack/trunk/trackback/js/rico/css/ricoMenu.css
new file mode 100644 (file)
index 0000000..b694c79
--- /dev/null
@@ -0,0 +1,95 @@
+/* ricoMenu */\r
+\r
+div.ricoMenu, div.ricoMenuSafari {\r
+position: absolute;\r
+z-index: 100;\r
+border:1px solid #666;\r
+padding:2px;\r
+cursor:default;\r
+visibility: hidden;\r
+}\r
+\r
+div.ricoMenu, div.ricoMenu div.ricoMenuHeading, div.ricoMenu a {\r
+background-color:menu;\r
+color: menutext;\r
+text-decoration: none;\r
+font-family:tahoma,arial,helvetica,sans-serif;\r
+font-size: 8pt;\r
+display:block;\r
+}\r
+\r
+div.ricoMenuSafari, div.ricoMenuSafari div.ricoMenuHeading, div.ricoMenuSafari a {\r
+background-color:#EDEDED;\r
+text-decoration: none;\r
+font-family:tahoma,arial,helvetica,sans-serif;\r
+font-size: 8pt;\r
+display:block;\r
+}\r
+\r
+div.ricoMenu div.ricoMenuHeading{\r
+padding: 1px 0px;\r
+font-weight:bold;\r
+}\r
+\r
+div.ricoMenuSafari div.ricoMenuHeading{\r
+padding: 1px 0px;\r
+color: black;\r
+display: block;\r
+font-weight:bold;\r
+}\r
+\r
+div.ricoMenu .enabled {\r
+position: relative;\r
+}\r
+\r
+div.ricoMenuSafari .enabled {\r
+color: black;\r
+}\r
+\r
+div.ricoMenu .enabled, div.ricoMenu .enabled-hover, div.ricoMenuSafari .enabled, div.ricoMenuSafari .enabled-hover, div.ricoMenu .disabled, div.ricoMenuSafari .disabled {\r
+padding-left: 1em;\r
+padding-top:0.1em;\r
+padding-bottom:0.1em;\r
+z-index: 101;\r
+}\r
+\r
+div.ricoMenu .disabled, div.ricoMenuSafari .disabled {\r
+color: #999;\r
+}\r
+\r
+div.ricoMenu hr{\r
+height:1px;\r
+margin:1px;\r
+border:0;\r
+color: menu;\r
+background-color: menu;\r
+}\r
+\r
+div.ricoMenu .enabled-hover, div.ricoMenu .ricoSubMenuOpen {\r
+   background-color: Highlight;\r
+   color:            HighlightText;\r
+}\r
+\r
+div.ricoMenuSafari .enabled-hover, div.ricoMenuSafari .ricoSubMenuOpen {\r
+   background-color: #1657B8;\r
+   color:            white;\r
+}\r
+\r
+div.ricoMenu .ricoSubMenu, div.ricoMenu .ricoSubMenuOpen, div.ricoMenuSafari .ricoSubMenu, div.ricoMenuSafari .ricoSubMenuOpen {\r
+padding: 1px 0px;\r
+display: block;\r
+font-weight:bold;\r
+z-index: 101;\r
+position: relative;\r
+}\r
+\r
+div.ricoMenu div.ricoMenuBreak, div.ricoMenuSafari div.ricoMenuBreak {\r
+height:1px;\r
+margin:3px 0 3px 0;\r
+padding:0;\r
+background-color: #AAA;\r
+width:100%;\r
+line-height:5px;\r
+overflow:hidden;\r
+}\r
+\r
diff --git a/NP_TrackBack/trunk/trackback/js/rico/css/ricoTree.css b/NP_TrackBack/trunk/trackback/js/rico/css/ricoTree.css
new file mode 100644 (file)
index 0000000..33a457c
--- /dev/null
@@ -0,0 +1,42 @@
+/* ricoTree */\r
+\r
+div.ricoTreeContainer {\r
+  background-color:#cedebd;\r
+  padding:4px;\r
+  border:1px solid black;\r
+  top:0px;\r
+  left:0px;\r
+  position:absolute;\r
+  z-index:9999;\r
+}\r
+\r
+div.ricoTree {\r
+  border:thin inset;\r
+  overflow:auto;\r
+  background-color:#FFF;\r
+}\r
+\r
+div.ricoTree p, div.ricoTree a {\r
+  margin:0px;\r
+  padding-left:0.3em;\r
+  white-space:nowrap;\r
+}\r
+\r
+div.ricoTree a {\r
+  cursor:pointer;\r
+  text-decoration:none;\r
+}\r
+\r
+div.ricoTree a:hover {\r
+  background-color:#EEE;\r
+}\r
+\r
+div.ricoTree img {\r
+  margin:0px;\r
+  padding:0px;\r
+  display:block;\r
+}\r
+\r
+div.ricoTree * {\r
+  font-size:8pt;\r
+}\r
diff --git a/NP_TrackBack/trunk/trackback/js/rico/css/tanChisel.css b/NP_TrackBack/trunk/trackback/js/rico/css/tanChisel.css
new file mode 100644 (file)
index 0000000..706437e
--- /dev/null
@@ -0,0 +1,36 @@
+tr.ricoLG_hdg .ricoLG_cell, tr.ricoLG_hdg th, tr.ricoLG_hdg td {  /* td/th required for IE */\r
+  background-color:#e0e0c0 !important;\r
+       vertical-align:middle;\r
+}\r
+\r
+tr.ricoLG_hdg div.ricoLG_cell {\r
+       border-top: 1px solid #F0F0E8;\r
+}\r
+\r
+.ricoLG_bottom div.ricoLG_cell, .ricoLG_top th, .ricoLG_top td {\r
+       border-bottom: 1px solid #D8d0c0;;\r
+}\r
+\r
+.ricoLG_table th, .ricoLG_table td {\r
+       border-left: 1px solid #F0F0E8;\r
+       border-right: 1px solid #D8d0c0;\r
+}\r
+\r
+div.ricoMenu, div.ricoMenu div.ricoMenuHeading, div.ricoMenu .ricoSubMenu, div.ricoMenuSafari, div.ricoMenuSafari div.ricoMenuHeading, div.ricoMenuSafari .ricoSubMenu {\r
+  background-color:#f0f0e0;\r
+}\r
+\r
+div.ricoMenu, div.ricoMenu div.ricoMenuHeading, div.ricoMenu .ricoSubMenu, div.ricoMenu .ricoSubMenuOpen, div.ricoMenuSafari, div.ricoMenuSafari div.ricoMenuHeading, div.ricoMenuSafari .ricoSubMenu, div.ricoMenuSafari .ricoSubMenuOpen {\r
+       border-top: 1px solid #F0F0E8;\r
+       border-left: 1px solid #F0F0E8;\r
+       border-bottom: 1px solid #D8d0c0;;\r
+       border-right: 1px solid #D8d0c0;\r
+}\r
+\r
+.ricoLG_table {\r
+  border-style:none;\r
+}\r
+\r
+div.ricoLG_selection {\r
+  background-color:#e0e0c0;\r
+}\r
diff --git a/NP_TrackBack/trunk/trackback/js/rico/css/warmfall.css b/NP_TrackBack/trunk/trackback/js/rico/css/warmfall.css
new file mode 100644 (file)
index 0000000..1af5155
--- /dev/null
@@ -0,0 +1,63 @@
+/* -------------------------------------------------------\r
+Based on warm fall table design\r
+Author: Mya Leigh\r
+Theme: A Warm, Fall Table - Easy to Read\r
+URL: http://www.myaleigh.com \r
+ ------------------------------------------------------- */\r
+.ricoLG_table {\r
+       border-top: 1px solid #84785e;\r
+       border-right: 1px solid #84785e;\r
+}\r
+\r
+tr.ricoLG_hdg .ricoLG_cell, tr.ricoLG_hdg th, tr.ricoLG_hdg td {  /* td/th required for IE */\r
+       background-color: #a24116 !important;\r
+  color: #ffffff !important;  \r
+}\r
+\r
+.ricoLG_table th, .ricoLG_table td {\r
+       border-left: 1px solid #84785e;\r
+}\r
+\r
+.ricoLG_bottom div.ricoLG_cell, .ricoLG_top th, .ricoLG_top td {\r
+  border-bottom: 1px solid #84785e;\r
+}\r
+\r
+tr.ricoLG_hdg .ricoLG_cell {\r
+       background-color: #a24116;\r
+  border: 0;\r
+  color: #ffffff;  \r
+       padding: .75em;\r
+  font: "Verdana", Arial, Helvetica, sans-serif;\r
+  font-weight: bold;\r
+}\r
+\r
+div.ricoLG_outerDiv a:visited, div.ricoLG_outerDiv a:link, div.ricoLG_outerDiv a:active {\r
+       color: #101011;\r
+       text-decoration: none;\r
+}\r
+\r
+div.ricoLG_outerDiv a:hover {\r
+       text-decoration: underline;\r
+}\r
+\r
+div.ricoLG_outerDiv tbody a:visited {\r
+       color:#444;\r
+}\r
+\r
+.ricoLG_oddRow {\r
+       background-color: #fffce1;\r
+       color: #101011;\r
+}\r
+\r
+.ricoLG_selection {\r
+       background-color: #a24116;\r
+       color: #ffffff;\r
+}\r
+       \r
+caption {\r
+       text-align: left;\r
+       font-size: 100%;\r
+       padding: .75em;\r
+       color: #000;\r
+}\r
+\r
diff --git a/NP_TrackBack/trunk/trackback/js/rico/export-owc.html b/NP_TrackBack/trunk/trackback/js/rico/export-owc.html
new file mode 100644 (file)
index 0000000..8819eca
--- /dev/null
@@ -0,0 +1,34 @@
+<html>\r
+<head>\r
+<title>Export</title>\r
+<SCRIPT TYPE="text/javascript">\r
+function getdata() {\r
+  if (!window.opener || window.opener.closed) {\r
+    alert('Error! Parent window is closed');\r
+    return;\r
+  }\r
+  var divID=window.location.search;\r
+  if (divID.length<2) {\r
+    alert('Error! Invalid id');\r
+    return;\r
+  }\r
+  divID=divID.substring(1);\r
+  var oDiv=window.opener.document.getElementById(divID);\r
+  if (!oDiv) {\r
+    alert('Error! Can not find \"'+divID+'\"');\r
+    return;\r
+  }\r
+  var oSS=document.getElementById('ss')\r
+  if (!oSS) {\r
+    alert('Error! Can not find spreadsheet');\r
+    return;\r
+  }\r
+  oSS.HTMLData=oDiv.innerHTML;\r
+}\r
+window.onload=getdata;\r
+</SCRIPT>\r
+</head>\r
+<body>\r
+<object id="ss" classid="CLSID:0002E559-0000-0000-C000-000000000046" style="width:100%;height:100%"></object>\r
+</body>\r
+</html>\r
diff --git a/NP_TrackBack/trunk/trackback/js/rico/export-plain.html b/NP_TrackBack/trunk/trackback/js/rico/export-plain.html
new file mode 100644 (file)
index 0000000..b158c58
--- /dev/null
@@ -0,0 +1,28 @@
+<html>\r
+<head>\r
+<title>Export</title>\r
+<SCRIPT TYPE="text/javascript">\r
+function getdata() {\r
+  if (!window.opener || window.opener.closed) {\r
+    alert('Error! Parent window is closed');\r
+    return;\r
+  }\r
+  var divID=window.location.search;\r
+  if (divID.length<2) {\r
+    alert('Error! Invalid id');\r
+    return;\r
+  }\r
+  divID=divID.substring(1);\r
+  var oDiv=window.opener.document.getElementById(divID);\r
+  if (!oDiv) {\r
+    alert('Error! Can not find \"'+divID+'\"');\r
+    return;\r
+  }\r
+  document.body.innerHTML=oDiv.innerHTML;\r
+}\r
+window.onload=getdata;\r
+</SCRIPT>\r
+</head>\r
+<body>\r
+</body>\r
+</html>\r
diff --git a/NP_TrackBack/trunk/trackback/js/rico/images/aline.gif b/NP_TrackBack/trunk/trackback/js/rico/images/aline.gif
new file mode 100644 (file)
index 0000000..7f5cb43
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/js/rico/images/aline.gif differ
diff --git a/NP_TrackBack/trunk/trackback/js/rico/images/calarrow.png b/NP_TrackBack/trunk/trackback/js/rico/images/calarrow.png
new file mode 100644 (file)
index 0000000..acdcfed
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/js/rico/images/calarrow.png differ
diff --git a/NP_TrackBack/trunk/trackback/js/rico/images/calendaricon.gif b/NP_TrackBack/trunk/trackback/js/rico/images/calendaricon.gif
new file mode 100644 (file)
index 0000000..abdf6ac
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/js/rico/images/calendaricon.gif differ
diff --git a/NP_TrackBack/trunk/trackback/js/rico/images/close.gif b/NP_TrackBack/trunk/trackback/js/rico/images/close.gif
new file mode 100644 (file)
index 0000000..65c5f16
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/js/rico/images/close.gif differ
diff --git a/NP_TrackBack/trunk/trackback/js/rico/images/divider.gif b/NP_TrackBack/trunk/trackback/js/rico/images/divider.gif
new file mode 100644 (file)
index 0000000..d9863d4
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/js/rico/images/divider.gif differ
diff --git a/NP_TrackBack/trunk/trackback/js/rico/images/doc.gif b/NP_TrackBack/trunk/trackback/js/rico/images/doc.gif
new file mode 100644 (file)
index 0000000..231036d
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/js/rico/images/doc.gif differ
diff --git a/NP_TrackBack/trunk/trackback/js/rico/images/dotbutton.gif b/NP_TrackBack/trunk/trackback/js/rico/images/dotbutton.gif
new file mode 100644 (file)
index 0000000..20c85cc
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/js/rico/images/dotbutton.gif differ
diff --git a/NP_TrackBack/trunk/trackback/js/rico/images/drop.gif b/NP_TrackBack/trunk/trackback/js/rico/images/drop.gif
new file mode 100644 (file)
index 0000000..afce892
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/js/rico/images/drop.gif differ
diff --git a/NP_TrackBack/trunk/trackback/js/rico/images/filtercol.gif b/NP_TrackBack/trunk/trackback/js/rico/images/filtercol.gif
new file mode 100644 (file)
index 0000000..2e93828
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/js/rico/images/filtercol.gif differ
diff --git a/NP_TrackBack/trunk/trackback/js/rico/images/folderclosed.gif b/NP_TrackBack/trunk/trackback/js/rico/images/folderclosed.gif
new file mode 100644 (file)
index 0000000..7c61d6f
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/js/rico/images/folderclosed.gif differ
diff --git a/NP_TrackBack/trunk/trackback/js/rico/images/folderopen.gif b/NP_TrackBack/trunk/trackback/js/rico/images/folderopen.gif
new file mode 100644 (file)
index 0000000..c16e11b
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/js/rico/images/folderopen.gif differ
diff --git a/NP_TrackBack/trunk/trackback/js/rico/images/grayedout.gif b/NP_TrackBack/trunk/trackback/js/rico/images/grayedout.gif
new file mode 100644 (file)
index 0000000..ead27af
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/js/rico/images/grayedout.gif differ
diff --git a/NP_TrackBack/trunk/trackback/js/rico/images/left.gif b/NP_TrackBack/trunk/trackback/js/rico/images/left.gif
new file mode 100644 (file)
index 0000000..f652075
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/js/rico/images/left.gif differ
diff --git a/NP_TrackBack/trunk/trackback/js/rico/images/link.gif b/NP_TrackBack/trunk/trackback/js/rico/images/link.gif
new file mode 100644 (file)
index 0000000..a679f4d
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/js/rico/images/link.gif differ
diff --git a/NP_TrackBack/trunk/trackback/js/rico/images/node.gif b/NP_TrackBack/trunk/trackback/js/rico/images/node.gif
new file mode 100644 (file)
index 0000000..d2dfe4b
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/js/rico/images/node.gif differ
diff --git a/NP_TrackBack/trunk/trackback/js/rico/images/nodeblank.gif b/NP_TrackBack/trunk/trackback/js/rico/images/nodeblank.gif
new file mode 100644 (file)
index 0000000..4f1f916
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/js/rico/images/nodeblank.gif differ
diff --git a/NP_TrackBack/trunk/trackback/js/rico/images/nodelast.gif b/NP_TrackBack/trunk/trackback/js/rico/images/nodelast.gif
new file mode 100644 (file)
index 0000000..3fe0a7e
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/js/rico/images/nodelast.gif differ
diff --git a/NP_TrackBack/trunk/trackback/js/rico/images/nodeline.gif b/NP_TrackBack/trunk/trackback/js/rico/images/nodeline.gif
new file mode 100644 (file)
index 0000000..5767a1f
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/js/rico/images/nodeline.gif differ
diff --git a/NP_TrackBack/trunk/trackback/js/rico/images/nodem.gif b/NP_TrackBack/trunk/trackback/js/rico/images/nodem.gif
new file mode 100644 (file)
index 0000000..fcc2d37
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/js/rico/images/nodem.gif differ
diff --git a/NP_TrackBack/trunk/trackback/js/rico/images/nodemlast.gif b/NP_TrackBack/trunk/trackback/js/rico/images/nodemlast.gif
new file mode 100644 (file)
index 0000000..11ae43a
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/js/rico/images/nodemlast.gif differ
diff --git a/NP_TrackBack/trunk/trackback/js/rico/images/nodep.gif b/NP_TrackBack/trunk/trackback/js/rico/images/nodep.gif
new file mode 100644 (file)
index 0000000..5b68013
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/js/rico/images/nodep.gif differ
diff --git a/NP_TrackBack/trunk/trackback/js/rico/images/nodeplast.gif b/NP_TrackBack/trunk/trackback/js/rico/images/nodeplast.gif
new file mode 100644 (file)
index 0000000..b87f003
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/js/rico/images/nodeplast.gif differ
diff --git a/NP_TrackBack/trunk/trackback/js/rico/images/resize.gif b/NP_TrackBack/trunk/trackback/js/rico/images/resize.gif
new file mode 100644 (file)
index 0000000..8efd1b5
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/js/rico/images/resize.gif differ
diff --git a/NP_TrackBack/trunk/trackback/js/rico/images/ricologo.gif b/NP_TrackBack/trunk/trackback/js/rico/images/ricologo.gif
new file mode 100644 (file)
index 0000000..9b14203
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/js/rico/images/ricologo.gif differ
diff --git a/NP_TrackBack/trunk/trackback/js/rico/images/right.gif b/NP_TrackBack/trunk/trackback/js/rico/images/right.gif
new file mode 100644 (file)
index 0000000..5517f2b
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/js/rico/images/right.gif differ
diff --git a/NP_TrackBack/trunk/trackback/js/rico/images/shadow.png b/NP_TrackBack/trunk/trackback/js/rico/images/shadow.png
new file mode 100644 (file)
index 0000000..7862c9b
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/js/rico/images/shadow.png differ
diff --git a/NP_TrackBack/trunk/trackback/js/rico/images/shadow_ll.png b/NP_TrackBack/trunk/trackback/js/rico/images/shadow_ll.png
new file mode 100644 (file)
index 0000000..cc0665b
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/js/rico/images/shadow_ll.png differ
diff --git a/NP_TrackBack/trunk/trackback/js/rico/images/shadow_ur.png b/NP_TrackBack/trunk/trackback/js/rico/images/shadow_ur.png
new file mode 100644 (file)
index 0000000..cad0f2c
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/js/rico/images/shadow_ur.png differ
diff --git a/NP_TrackBack/trunk/trackback/js/rico/images/sort_asc.gif b/NP_TrackBack/trunk/trackback/js/rico/images/sort_asc.gif
new file mode 100644 (file)
index 0000000..6330232
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/js/rico/images/sort_asc.gif differ
diff --git a/NP_TrackBack/trunk/trackback/js/rico/images/sort_desc.gif b/NP_TrackBack/trunk/trackback/js/rico/images/sort_desc.gif
new file mode 100644 (file)
index 0000000..b3a681c
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/js/rico/images/sort_desc.gif differ
diff --git a/NP_TrackBack/trunk/trackback/js/rico/prototype.js b/NP_TrackBack/trunk/trackback/js/rico/prototype.js
new file mode 100644 (file)
index 0000000..5906575
--- /dev/null
@@ -0,0 +1,3269 @@
+/*  Prototype JavaScript framework, version 1.5.1_rc3
+ *  (c) 2005-2007 Sam Stephenson
+ *
+ *  Prototype is freely distributable under the terms of an MIT-style license.
+ *  For details, see the Prototype web site: http://www.prototypejs.org/
+ *
+/*--------------------------------------------------------------------------*/
+
+var Prototype = {
+  Version: '1.5.1_rc3',
+
+  Browser: {
+    IE:     !!(window.attachEvent && !window.opera),
+    Opera:  !!window.opera,
+    WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
+    Gecko:  navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1
+  },
+
+  BrowserFeatures: {
+    XPath: !!document.evaluate,
+    ElementExtensions: !!window.HTMLElement,
+    SpecificElementExtensions:
+      (document.createElement('div').__proto__ !==
+       document.createElement('form').__proto__)
+  },
+
+  ScriptFragment: '<script[^>]*>([\u0001-\uFFFF]*?)</script>',
+  JSONFilter: /^\/\*-secure-\s*(.*)\s*\*\/\s*$/,
+
+  emptyFunction: function() { },
+  K: function(x) { return x }
+}
+
+var Class = {
+  create: function() {
+    return function() {
+      this.initialize.apply(this, arguments);
+    }
+  }
+}
+
+var Abstract = new Object();
+
+Object.extend = function(destination, source) {
+  for (var property in source) {
+    destination[property] = source[property];
+  }
+  return destination;
+}
+
+Object.extend(Object, {
+  inspect: function(object) {
+    try {
+      if (object === undefined) return 'undefined';
+      if (object === null) return 'null';
+      return object.inspect ? object.inspect() : object.toString();
+    } catch (e) {
+      if (e instanceof RangeError) return '...';
+      throw e;
+    }
+  },
+
+  toJSON: function(object) {
+    var type = typeof object;
+    switch(type) {
+      case 'undefined':
+      case 'function':
+      case 'unknown': return;
+      case 'boolean': return object.toString();
+    }
+    if (object === null) return 'null';
+    if (object.toJSON) return object.toJSON();
+    if (object.ownerDocument === document) return;
+    var results = [];
+    for (var property in object) {
+      var value = Object.toJSON(object[property]);
+      if (value !== undefined)
+        results.push(property.toJSON() + ': ' + value);
+    }
+    return '{' + results.join(', ') + '}';
+  },
+
+  keys: function(object) {
+    var keys = [];
+    for (var property in object)
+      keys.push(property);
+    return keys;
+  },
+
+  values: function(object) {
+    var values = [];
+    for (var property in object)
+      values.push(object[property]);
+    return values;
+  },
+
+  clone: function(object) {
+    return Object.extend({}, object);
+  }
+});
+
+Function.prototype.bind = function() {
+  var __method = this, args = $A(arguments), object = args.shift();
+  return function() {
+    return __method.apply(object, args.concat($A(arguments)));
+  }
+}
+
+Function.prototype.bindAsEventListener = function(object) {
+  var __method = this, args = $A(arguments), object = args.shift();
+  return function(event) {
+    return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments)));
+  }
+}
+
+Object.extend(Number.prototype, {
+  toColorPart: function() {
+    return this.toPaddedString(2, 16);
+  },
+
+  succ: function() {
+    return this + 1;
+  },
+
+  times: function(iterator) {
+    $R(0, this, true).each(iterator);
+    return this;
+  },
+
+  toPaddedString: function(length, radix) {
+    var string = this.toString(radix || 10);
+    return '0'.times(length - string.length) + string;
+  },
+
+  toJSON: function() {
+    return isFinite(this) ? this.toString() : 'null';
+  }
+});
+
+Date.prototype.toJSON = function() {
+  return '"' + this.getFullYear() + '-' +
+    (this.getMonth() + 1).toPaddedString(2) + '-' +
+    this.getDate().toPaddedString(2) + 'T' +
+    this.getHours().toPaddedString(2) + ':' +
+    this.getMinutes().toPaddedString(2) + ':' +
+    this.getSeconds().toPaddedString(2) + '"';
+};
+
+var Try = {
+  these: function() {
+    var returnValue;
+
+    for (var i = 0, length = arguments.length; i < length; i++) {
+      var lambda = arguments[i];
+      try {
+        returnValue = lambda();
+        break;
+      } catch (e) {}
+    }
+
+    return returnValue;
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var PeriodicalExecuter = Class.create();
+PeriodicalExecuter.prototype = {
+  initialize: function(callback, frequency) {
+    this.callback = callback;
+    this.frequency = frequency;
+    this.currentlyExecuting = false;
+
+    this.registerCallback();
+  },
+
+  registerCallback: function() {
+    this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+  },
+
+  stop: function() {
+    if (!this.timer) return;
+    clearInterval(this.timer);
+    this.timer = null;
+  },
+
+  onTimerEvent: function() {
+    if (!this.currentlyExecuting) {
+      try {
+        this.currentlyExecuting = true;
+        this.callback(this);
+      } finally {
+        this.currentlyExecuting = false;
+      }
+    }
+  }
+}
+Object.extend(String, {
+  interpret: function(value) {
+    return value == null ? '' : String(value);
+  },
+  specialChar: {
+    '\b': '\\b',
+    '\t': '\\t',
+    '\n': '\\n',
+    '\f': '\\f',
+    '\r': '\\r',
+    '\\': '\\\\'
+  }
+});
+
+Object.extend(String.prototype, {
+  gsub: function(pattern, replacement) {
+    var result = '', source = this, match;
+    replacement = arguments.callee.prepareReplacement(replacement);
+
+    while (source.length > 0) {
+      if (match = source.match(pattern)) {
+        result += source.slice(0, match.index);
+        result += String.interpret(replacement(match));
+        source  = source.slice(match.index + match[0].length);
+      } else {
+        result += source, source = '';
+      }
+    }
+    return result;
+  },
+
+  sub: function(pattern, replacement, count) {
+    replacement = this.gsub.prepareReplacement(replacement);
+    count = count === undefined ? 1 : count;
+
+    return this.gsub(pattern, function(match) {
+      if (--count < 0) return match[0];
+      return replacement(match);
+    });
+  },
+
+  scan: function(pattern, iterator) {
+    this.gsub(pattern, iterator);
+    return this;
+  },
+
+  truncate: function(length, truncation) {
+    length = length || 30;
+    truncation = truncation === undefined ? '...' : truncation;
+    return this.length > length ?
+      this.slice(0, length - truncation.length) + truncation : this;
+  },
+
+  strip: function() {
+    return this.replace(/^\s+/, '').replace(/\s+$/, '');
+  },
+
+  stripTags: function() {
+    return this.replace(/<\/?[^>]+>/gi, '');
+  },
+
+  stripScripts: function() {
+    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
+  },
+
+  extractScripts: function() {
+    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
+    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+    return (this.match(matchAll) || []).map(function(scriptTag) {
+      return (scriptTag.match(matchOne) || ['', ''])[1];
+    });
+  },
+
+  evalScripts: function() {
+    return this.extractScripts().map(function(script) { return eval(script) });
+  },
+
+  escapeHTML: function() {
+    var self = arguments.callee;
+    self.text.data = this;
+    return self.div.innerHTML;
+  },
+
+  unescapeHTML: function() {
+    var div = document.createElement('div');
+    div.innerHTML = this.stripTags();
+    return div.childNodes[0] ? (div.childNodes.length > 1 ?
+      $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
+      div.childNodes[0].nodeValue) : '';
+  },
+
+  toQueryParams: function(separator) {
+    var match = this.strip().match(/([^?#]*)(#.*)?$/);
+    if (!match) return {};
+
+    return match[1].split(separator || '&').inject({}, function(hash, pair) {
+      if ((pair = pair.split('='))[0]) {
+        var key = decodeURIComponent(pair.shift());
+        var value = pair.length > 1 ? pair.join('=') : pair[0];
+        if (value != undefined) value = decodeURIComponent(value);
+
+        if (key in hash) {
+          if (hash[key].constructor != Array) hash[key] = [hash[key]];
+          hash[key].push(value);
+        }
+        else hash[key] = value;
+      }
+      return hash;
+    });
+  },
+
+  toArray: function() {
+    return this.split('');
+  },
+
+  succ: function() {
+    return this.slice(0, this.length - 1) +
+      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
+  },
+
+  times: function(count) {
+    var result = '';
+    for (var i = 0; i < count; i++) result += this;
+    return result;
+  },
+
+  camelize: function() {
+    var parts = this.split('-'), len = parts.length;
+    if (len == 1) return parts[0];
+
+    var camelized = this.charAt(0) == '-'
+      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
+      : parts[0];
+
+    for (var i = 1; i < len; i++)
+      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
+
+    return camelized;
+  },
+
+  capitalize: function() {
+    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
+  },
+
+  underscore: function() {
+    return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
+  },
+
+  dasherize: function() {
+    return this.gsub(/_/,'-');
+  },
+
+  inspect: function(useDoubleQuotes) {
+    var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
+      var character = String.specialChar[match[0]];
+      return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
+    });
+    if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
+    return "'" + escapedString.replace(/'/g, '\\\'') + "'";
+  },
+
+  toJSON: function() {
+    return this.inspect(true);
+  },
+
+  unfilterJSON: function(filter) {
+    return this.sub(filter || Prototype.JSONFilter, '#{1}');
+  },
+
+  evalJSON: function(sanitize) {
+    var json = this.unfilterJSON();
+    try {
+      if (!sanitize || (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(json)))
+        return eval('(' + json + ')');
+    } catch (e) { }
+    throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
+  },
+
+  include: function(pattern) {
+    return this.indexOf(pattern) > -1;
+  },
+
+  startsWith: function(pattern) {
+    return this.indexOf(pattern) === 0;
+  },
+
+  endsWith: function(pattern) {
+    var d = this.length - pattern.length;
+    return d >= 0 && this.lastIndexOf(pattern) === d;
+  },
+
+  empty: function() {
+    return this == '';
+  },
+
+  blank: function() {
+    return /^\s*$/.test(this);
+  }
+});
+
+if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
+  escapeHTML: function() {
+    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
+  },
+  unescapeHTML: function() {
+    return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
+  }
+});
+
+String.prototype.gsub.prepareReplacement = function(replacement) {
+  if (typeof replacement == 'function') return replacement;
+  var template = new Template(replacement);
+  return function(match) { return template.evaluate(match) };
+}
+
+String.prototype.parseQuery = String.prototype.toQueryParams;
+
+Object.extend(String.prototype.escapeHTML, {
+  div:  document.createElement('div'),
+  text: document.createTextNode('')
+});
+
+with (String.prototype.escapeHTML) div.appendChild(text);
+
+var Template = Class.create();
+Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
+Template.prototype = {
+  initialize: function(template, pattern) {
+    this.template = template.toString();
+    this.pattern  = pattern || Template.Pattern;
+  },
+
+  evaluate: function(object) {
+    return this.template.gsub(this.pattern, function(match) {
+      var before = match[1];
+      if (before == '\\') return match[2];
+      return before + String.interpret(object[match[3]]);
+    });
+  }
+}
+
+var $break    = new Object();
+var $continue = new Object();
+
+var Enumerable = {
+  each: function(iterator) {
+    var index = 0;
+    try {
+      this._each(function(value) {
+        iterator(value, index++);
+      });
+    } catch (e) {
+      if (e != $break) throw e;
+    }
+    return this;
+  },
+
+  eachSlice: function(number, iterator) {
+    var index = -number, slices = [], array = this.toArray();
+    while ((index += number) < array.length)
+      slices.push(array.slice(index, index+number));
+    return slices.map(iterator);
+  },
+
+  all: function(iterator) {
+    var result = true;
+    this.each(function(value, index) {
+      result = result && !!(iterator || Prototype.K)(value, index);
+      if (!result) throw $break;
+    });
+    return result;
+  },
+
+  any: function(iterator) {
+    var result = false;
+    this.each(function(value, index) {
+      if (result = !!(iterator || Prototype.K)(value, index))
+        throw $break;
+    });
+    return result;
+  },
+
+  collect: function(iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      results.push((iterator || Prototype.K)(value, index));
+    });
+    return results;
+  },
+
+  detect: function(iterator) {
+    var result;
+    this.each(function(value, index) {
+      if (iterator(value, index)) {
+        result = value;
+        throw $break;
+      }
+    });
+    return result;
+  },
+
+  findAll: function(iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      if (iterator(value, index))
+        results.push(value);
+    });
+    return results;
+  },
+
+  grep: function(pattern, iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      var stringValue = value.toString();
+      if (stringValue.match(pattern))
+        results.push((iterator || Prototype.K)(value, index));
+    })
+    return results;
+  },
+
+  include: function(object) {
+    var found = false;
+    this.each(function(value) {
+      if (value == object) {
+        found = true;
+        throw $break;
+      }
+    });
+    return found;
+  },
+
+  inGroupsOf: function(number, fillWith) {
+    fillWith = fillWith === undefined ? null : fillWith;
+    return this.eachSlice(number, function(slice) {
+      while(slice.length < number) slice.push(fillWith);
+      return slice;
+    });
+  },
+
+  inject: function(memo, iterator) {
+    this.each(function(value, index) {
+      memo = iterator(memo, value, index);
+    });
+    return memo;
+  },
+
+  invoke: function(method) {
+    var args = $A(arguments).slice(1);
+    return this.map(function(value) {
+      return value[method].apply(value, args);
+    });
+  },
+
+  max: function(iterator) {
+    var result;
+    this.each(function(value, index) {
+      value = (iterator || Prototype.K)(value, index);
+      if (result == undefined || value >= result)
+        result = value;
+    });
+    return result;
+  },
+
+  min: function(iterator) {
+    var result;
+    this.each(function(value, index) {
+      value = (iterator || Prototype.K)(value, index);
+      if (result == undefined || value < result)
+        result = value;
+    });
+    return result;
+  },
+
+  partition: function(iterator) {
+    var trues = [], falses = [];
+    this.each(function(value, index) {
+      ((iterator || Prototype.K)(value, index) ?
+        trues : falses).push(value);
+    });
+    return [trues, falses];
+  },
+
+  pluck: function(property) {
+    var results = [];
+    this.each(function(value, index) {
+      results.push(value[property]);
+    });
+    return results;
+  },
+
+  reject: function(iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      if (!iterator(value, index))
+        results.push(value);
+    });
+    return results;
+  },
+
+  sortBy: function(iterator) {
+    return this.map(function(value, index) {
+      return {value: value, criteria: iterator(value, index)};
+    }).sort(function(left, right) {
+      var a = left.criteria, b = right.criteria;
+      return a < b ? -1 : a > b ? 1 : 0;
+    }).pluck('value');
+  },
+
+  toArray: function() {
+    return this.map();
+  },
+
+  zip: function() {
+    var iterator = Prototype.K, args = $A(arguments);
+    if (typeof args.last() == 'function')
+      iterator = args.pop();
+
+    var collections = [this].concat(args).map($A);
+    return this.map(function(value, index) {
+      return iterator(collections.pluck(index));
+    });
+  },
+
+  size: function() {
+    return this.toArray().length;
+  },
+
+  inspect: function() {
+    return '#<Enumerable:' + this.toArray().inspect() + '>';
+  }
+}
+
+Object.extend(Enumerable, {
+  map:     Enumerable.collect,
+  find:    Enumerable.detect,
+  select:  Enumerable.findAll,
+  member:  Enumerable.include,
+  entries: Enumerable.toArray
+});
+var $A = Array.from = function(iterable) {
+  if (!iterable) return [];
+  if (iterable.toArray) {
+    return iterable.toArray();
+  } else {
+    var results = [];
+    for (var i = 0, length = iterable.length; i < length; i++)
+      results.push(iterable[i]);
+    return results;
+  }
+}
+
+if (Prototype.Browser.WebKit) {
+  $A = Array.from = function(iterable) {
+    if (!iterable) return [];
+    if (!(typeof iterable == 'function' && iterable == '[object NodeList]') &&
+      iterable.toArray) {
+      return iterable.toArray();
+    } else {
+      var results = [];
+      for (var i = 0, length = iterable.length; i < length; i++)
+        results.push(iterable[i]);
+      return results;
+    }
+  }
+}
+
+Object.extend(Array.prototype, Enumerable);
+
+if (!Array.prototype._reverse)
+  Array.prototype._reverse = Array.prototype.reverse;
+
+Object.extend(Array.prototype, {
+  _each: function(iterator) {
+    for (var i = 0, length = this.length; i < length; i++)
+      iterator(this[i]);
+  },
+
+  clear: function() {
+    this.length = 0;
+    return this;
+  },
+
+  first: function() {
+    return this[0];
+  },
+
+  last: function() {
+    return this[this.length - 1];
+  },
+
+  compact: function() {
+    return this.select(function(value) {
+      return value != null;
+    });
+  },
+
+  flatten: function() {
+    return this.inject([], function(array, value) {
+      return array.concat(value && value.constructor == Array ?
+        value.flatten() : [value]);
+    });
+  },
+
+  without: function() {
+    var values = $A(arguments);
+    return this.select(function(value) {
+      return !values.include(value);
+    });
+  },
+
+  indexOf: function(object) {
+    for (var i = 0, length = this.length; i < length; i++)
+      if (this[i] == object) return i;
+    return -1;
+  },
+
+  reverse: function(inline) {
+    return (inline !== false ? this : this.toArray())._reverse();
+  },
+
+  reduce: function() {
+    return this.length > 1 ? this : this[0];
+  },
+
+  uniq: function(sorted) {
+    return this.inject([], function(array, value, index) {
+      if (0 == index || (sorted ? array.last() != value : !array.include(value)))
+        array.push(value);
+      return array;
+    });
+  },
+
+  clone: function() {
+    return [].concat(this);
+  },
+
+  size: function() {
+    return this.length;
+  },
+
+  inspect: function() {
+    return '[' + this.map(Object.inspect).join(', ') + ']';
+  },
+
+  toJSON: function() {
+    var results = [];
+    this.each(function(object) {
+      var value = Object.toJSON(object);
+      if (value !== undefined) results.push(value);
+    });
+    return '[' + results.join(', ') + ']';
+  }
+});
+
+Array.prototype.toArray = Array.prototype.clone;
+
+function $w(string) {
+  string = string.strip();
+  return string ? string.split(/\s+/) : [];
+}
+
+if (Prototype.Browser.Opera){
+  Array.prototype.concat = function() {
+    var array = [];
+    for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
+    for (var i = 0, length = arguments.length; i < length; i++) {
+      if (arguments[i].constructor == Array) {
+        for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
+          array.push(arguments[i][j]);
+      } else {
+        array.push(arguments[i]);
+      }
+    }
+    return array;
+  }
+}
+var Hash = function(object) {
+  if (object instanceof Hash) this.merge(object);
+  else Object.extend(this, object || {});
+};
+
+Object.extend(Hash, {
+  toQueryString: function(obj) {
+    var parts = [];
+    parts.add = arguments.callee.addPair;
+
+    this.prototype._each.call(obj, function(pair) {
+      if (!pair.key) return;
+      var value = pair.value;
+
+      if (value && typeof value == 'object') {
+        if (value.constructor == Array) value.each(function(value) {
+          parts.add(pair.key, value);
+        });
+        return;
+      }
+      parts.add(pair.key, value);
+    });
+
+    return parts.join('&');
+  },
+
+  toJSON: function(object) {
+    var results = [];
+    this.prototype._each.call(object, function(pair) {
+      var value = Object.toJSON(pair.value);
+      if (value !== undefined) results.push(pair.key.toJSON() + ': ' + value);
+    });
+    return '{' + results.join(', ') + '}';
+  }
+});
+
+Hash.toQueryString.addPair = function(key, value, prefix) {
+  key = encodeURIComponent(key);
+  if (value === undefined) this.push(key);
+  else this.push(key + '=' + (value == null ? '' : encodeURIComponent(value)));
+}
+
+Object.extend(Hash.prototype, Enumerable);
+Object.extend(Hash.prototype, {
+  _each: function(iterator) {
+    for (var key in this) {
+      var value = this[key];
+      if (value && value == Hash.prototype[key]) continue;
+
+      var pair = [key, value];
+      pair.key = key;
+      pair.value = value;
+      iterator(pair);
+    }
+  },
+
+  keys: function() {
+    return this.pluck('key');
+  },
+
+  values: function() {
+    return this.pluck('value');
+  },
+
+  merge: function(hash) {
+    return $H(hash).inject(this, function(mergedHash, pair) {
+      mergedHash[pair.key] = pair.value;
+      return mergedHash;
+    });
+  },
+
+  remove: function() {
+    var result;
+    for(var i = 0, length = arguments.length; i < length; i++) {
+      var value = this[arguments[i]];
+      if (value !== undefined){
+        if (result === undefined) result = value;
+        else {
+          if (result.constructor != Array) result = [result];
+          result.push(value)
+        }
+      }
+      delete this[arguments[i]];
+    }
+    return result;
+  },
+
+  toQueryString: function() {
+    return Hash.toQueryString(this);
+  },
+
+  inspect: function() {
+    return '#<Hash:{' + this.map(function(pair) {
+      return pair.map(Object.inspect).join(': ');
+    }).join(', ') + '}>';
+  },
+
+  toJSON: function() {
+    return Hash.toJSON(this);
+  }
+});
+
+function $H(object) {
+  if (object instanceof Hash) return object;
+  return new Hash(object);
+};
+
+// Safari iterates over shadowed properties
+if (function() {
+  var i = 0, Test = function(value) { this.key = value };
+  Test.prototype.key = 'foo';
+  for (var property in new Test('bar')) i++;
+  return i > 1;
+}()) Hash.prototype._each = function(iterator) {
+  var cache = [];
+  for (var key in this) {
+    var value = this[key];
+    if ((value && value == Hash.prototype[key]) || cache.include(key)) continue;
+    cache.push(key);
+    var pair = [key, value];
+    pair.key = key;
+    pair.value = value;
+    iterator(pair);
+  }
+};
+ObjectRange = Class.create();
+Object.extend(ObjectRange.prototype, Enumerable);
+Object.extend(ObjectRange.prototype, {
+  initialize: function(start, end, exclusive) {
+    this.start = start;
+    this.end = end;
+    this.exclusive = exclusive;
+  },
+
+  _each: function(iterator) {
+    var value = this.start;
+    while (this.include(value)) {
+      iterator(value);
+      value = value.succ();
+    }
+  },
+
+  include: function(value) {
+    if (value < this.start)
+      return false;
+    if (this.exclusive)
+      return value < this.end;
+    return value <= this.end;
+  }
+});
+
+var $R = function(start, end, exclusive) {
+  return new ObjectRange(start, end, exclusive);
+}
+
+var Ajax = {
+  getTransport: function() {
+    return Try.these(
+      function() {return new XMLHttpRequest()},
+      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
+    ) || false;
+  },
+
+  activeRequestCount: 0
+}
+
+Ajax.Responders = {
+  responders: [],
+
+  _each: function(iterator) {
+    this.responders._each(iterator);
+  },
+
+  register: function(responder) {
+    if (!this.include(responder))
+      this.responders.push(responder);
+  },
+
+  unregister: function(responder) {
+    this.responders = this.responders.without(responder);
+  },
+
+  dispatch: function(callback, request, transport, json) {
+    this.each(function(responder) {
+      if (typeof responder[callback] == 'function') {
+        try {
+          responder[callback].apply(responder, [request, transport, json]);
+        } catch (e) {}
+      }
+    });
+  }
+};
+
+Object.extend(Ajax.Responders, Enumerable);
+
+Ajax.Responders.register({
+  onCreate: function() {
+    Ajax.activeRequestCount++;
+  },
+  onComplete: function() {
+    Ajax.activeRequestCount--;
+  }
+});
+
+Ajax.Base = function() {};
+Ajax.Base.prototype = {
+  setOptions: function(options) {
+    this.options = {
+      method:       'post',
+      asynchronous: true,
+      contentType:  'application/x-www-form-urlencoded',
+      encoding:     'UTF-8',
+      parameters:   ''
+    }
+    Object.extend(this.options, options || {});
+
+    this.options.method = this.options.method.toLowerCase();
+    if (typeof this.options.parameters == 'string')
+      this.options.parameters = this.options.parameters.toQueryParams();
+  }
+}
+
+Ajax.Request = Class.create();
+Ajax.Request.Events =
+  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
+  _complete: false,
+
+  initialize: function(url, options) {
+    this.transport = Ajax.getTransport();
+    this.setOptions(options);
+    this.request(url);
+  },
+
+  request: function(url) {
+    this.url = url;
+    this.method = this.options.method;
+    var params = Object.clone(this.options.parameters);
+
+    if (!['get', 'post'].include(this.method)) {
+      // simulate other verbs over post
+      params['_method'] = this.method;
+      this.method = 'post';
+    }
+
+    this.parameters = params;
+
+    if (params = Hash.toQueryString(params)) {
+      // when GET, append parameters to URL
+      if (this.method == 'get')
+        this.url += (this.url.include('?') ? '&' : '?') + params;
+      else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
+        params += '&_=';
+    }
+
+    try {
+      if (this.options.onCreate) this.options.onCreate(this.transport);
+      Ajax.Responders.dispatch('onCreate', this, this.transport);
+
+      this.transport.open(this.method.toUpperCase(), this.url,
+        this.options.asynchronous);
+
+      if (this.options.asynchronous)
+        setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
+
+      this.transport.onreadystatechange = this.onStateChange.bind(this);
+      this.setRequestHeaders();
+
+      this.body = this.method == 'post' ? (this.options.postBody || params) : null;
+      this.transport.send(this.body);
+
+      /* Force Firefox to handle ready state 4 for synchronous requests */
+      if (!this.options.asynchronous && this.transport.overrideMimeType)
+        this.onStateChange();
+
+    }
+    catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
+  onStateChange: function() {
+    var readyState = this.transport.readyState;
+    if (readyState > 1 && !((readyState == 4) && this._complete))
+      this.respondToReadyState(this.transport.readyState);
+  },
+
+  setRequestHeaders: function() {
+    var headers = {
+      'X-Requested-With': 'XMLHttpRequest',
+      'X-Prototype-Version': Prototype.Version,
+      'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
+    };
+
+    if (this.method == 'post') {
+      headers['Content-type'] = this.options.contentType +
+        (this.options.encoding ? '; charset=' + this.options.encoding : '');
+
+      /* Force "Connection: close" for older Mozilla browsers to work
+       * around a bug where XMLHttpRequest sends an incorrect
+       * Content-length header. See Mozilla Bugzilla #246651.
+       */
+      if (this.transport.overrideMimeType &&
+          (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
+            headers['Connection'] = 'close';
+    }
+
+    // user-defined headers
+    if (typeof this.options.requestHeaders == 'object') {
+      var extras = this.options.requestHeaders;
+
+      if (typeof extras.push == 'function')
+        for (var i = 0, length = extras.length; i < length; i += 2)
+          headers[extras[i]] = extras[i+1];
+      else
+        $H(extras).each(function(pair) { headers[pair.key] = pair.value });
+    }
+
+    for (var name in headers)
+      this.transport.setRequestHeader(name, headers[name]);
+  },
+
+  success: function() {
+    return !this.transport.status
+        || (this.transport.status >= 200 && this.transport.status < 300);
+  },
+
+  respondToReadyState: function(readyState) {
+    var state = Ajax.Request.Events[readyState];
+    var transport = this.transport, json = this.evalJSON();
+
+    if (state == 'Complete') {
+      try {
+        this._complete = true;
+        (this.options['on' + this.transport.status]
+         || this.options['on' + (this.success() ? 'Success' : 'Failure')]
+         || Prototype.emptyFunction)(transport, json);
+      } catch (e) {
+        this.dispatchException(e);
+      }
+
+      var contentType = this.getHeader('Content-type');
+      if (contentType && contentType.strip().
+        match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
+          this.evalResponse();
+    }
+
+    try {
+      (this.options['on' + state] || Prototype.emptyFunction)(transport, json);
+      Ajax.Responders.dispatch('on' + state, this, transport, json);
+    } catch (e) {
+      this.dispatchException(e);
+    }
+
+    if (state == 'Complete') {
+      // avoid memory leak in MSIE: clean up
+      this.transport.onreadystatechange = Prototype.emptyFunction;
+    }
+  },
+
+  getHeader: function(name) {
+    try {
+      return this.transport.getResponseHeader(name);
+    } catch (e) { return null }
+  },
+
+  evalJSON: function() {
+    try {
+      var json = this.getHeader('X-JSON');
+      return json ? json.evalJSON() : null;
+    } catch (e) { return null }
+  },
+
+  evalResponse: function() {
+    try {
+      return eval((this.transport.responseText || '').unfilterJSON());
+    } catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
+  dispatchException: function(exception) {
+    (this.options.onException || Prototype.emptyFunction)(this, exception);
+    Ajax.Responders.dispatch('onException', this, exception);
+  }
+});
+
+Ajax.Updater = Class.create();
+
+Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
+  initialize: function(container, url, options) {
+    this.container = {
+      success: (container.success || container),
+      failure: (container.failure || (container.success ? null : container))
+    }
+
+    this.transport = Ajax.getTransport();
+    this.setOptions(options);
+
+    var onComplete = this.options.onComplete || Prototype.emptyFunction;
+    this.options.onComplete = (function(transport, param) {
+      this.updateContent();
+      onComplete(transport, param);
+    }).bind(this);
+
+    this.request(url);
+  },
+
+  updateContent: function() {
+    var receiver = this.container[this.success() ? 'success' : 'failure'];
+    var response = this.transport.responseText;
+
+    if (!this.options.evalScripts) response = response.stripScripts();
+
+    if (receiver = $(receiver)) {
+      if (this.options.insertion)
+        new this.options.insertion(receiver, response);
+      else
+        receiver.update(response);
+    }
+
+    if (this.success()) {
+      if (this.onComplete)
+        setTimeout(this.onComplete.bind(this), 10);
+    }
+  }
+});
+
+Ajax.PeriodicalUpdater = Class.create();
+Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
+  initialize: function(container, url, options) {
+    this.setOptions(options);
+    this.onComplete = this.options.onComplete;
+
+    this.frequency = (this.options.frequency || 2);
+    this.decay = (this.options.decay || 1);
+
+    this.updater = {};
+    this.container = container;
+    this.url = url;
+
+    this.start();
+  },
+
+  start: function() {
+    this.options.onComplete = this.updateComplete.bind(this);
+    this.onTimerEvent();
+  },
+
+  stop: function() {
+    this.updater.options.onComplete = undefined;
+    clearTimeout(this.timer);
+    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
+  },
+
+  updateComplete: function(request) {
+    if (this.options.decay) {
+      this.decay = (request.responseText == this.lastText ?
+        this.decay * this.options.decay : 1);
+
+      this.lastText = request.responseText;
+    }
+    this.timer = setTimeout(this.onTimerEvent.bind(this),
+      this.decay * this.frequency * 1000);
+  },
+
+  onTimerEvent: function() {
+    this.updater = new Ajax.Updater(this.container, this.url, this.options);
+  }
+});
+function $(element) {
+  if (arguments.length > 1) {
+    for (var i = 0, elements = [], length = arguments.length; i < length; i++)
+      elements.push($(arguments[i]));
+    return elements;
+  }
+  if (typeof element == 'string')
+    element = document.getElementById(element);
+  return Element.extend(element);
+}
+
+if (Prototype.BrowserFeatures.XPath) {
+  document._getElementsByXPath = function(expression, parentElement) {
+    var results = [];
+    var query = document.evaluate(expression, $(parentElement) || document,
+      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+    for (var i = 0, length = query.snapshotLength; i < length; i++)
+      results.push(query.snapshotItem(i));
+    return results;
+  };
+
+  document.getElementsByClassName = function(className, parentElement) {
+    var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
+    return document._getElementsByXPath(q, parentElement);
+  }
+
+} else document.getElementsByClassName = function(className, parentElement) {
+  var children = ($(parentElement) || document.body).getElementsByTagName('*');
+  var elements = [], child;
+  for (var i = 0, length = children.length; i < length; i++) {
+    child = children[i];
+    if (Element.hasClassName(child, className))
+      elements.push(Element.extend(child));
+  }
+  return elements;
+};
+
+/*--------------------------------------------------------------------------*/
+
+if (!window.Element) var Element = {};
+
+Element.extend = function(element) {
+  var F = Prototype.BrowserFeatures;
+  if (!element || !element.tagName || element.nodeType == 3 ||
+   element._extended || F.SpecificElementExtensions || element == window)
+    return element;
+
+  var methods = {}, tagName = element.tagName, cache = Element.extend.cache,
+   T = Element.Methods.ByTag;
+
+  // extend methods for all tags (Safari doesn't need this)
+  if (!F.ElementExtensions) {
+    Object.extend(methods, Element.Methods),
+    Object.extend(methods, Element.Methods.Simulated);
+  }
+
+  // extend methods for specific tags
+  if (T[tagName]) Object.extend(methods, T[tagName]);
+
+  for (var property in methods) {
+    var value = methods[property];
+    if (typeof value == 'function' && !(property in element))
+      element[property] = cache.findOrStore(value);
+  }
+
+  element._extended = Prototype.emptyFunction;
+  return element;
+};
+
+Element.extend.cache = {
+  findOrStore: function(value) {
+    return this[value] = this[value] || function() {
+      return value.apply(null, [this].concat($A(arguments)));
+    }
+  }
+};
+
+Element.Methods = {
+  visible: function(element) {
+    return $(element).style.display != 'none';
+  },
+
+  toggle: function(element) {
+    element = $(element);
+    Element[Element.visible(element) ? 'hide' : 'show'](element);
+    return element;
+  },
+
+  hide: function(element) {
+    $(element).style.display = 'none';
+    return element;
+  },
+
+  show: function(element) {
+    $(element).style.display = '';
+    return element;
+  },
+
+  remove: function(element) {
+    element = $(element);
+    element.parentNode.removeChild(element);
+    return element;
+  },
+
+  update: function(element, html) {
+    html = typeof html == 'undefined' ? '' : html.toString();
+    $(element).innerHTML = html.stripScripts();
+    setTimeout(function() {html.evalScripts()}, 10);
+    return element;
+  },
+
+  replace: function(element, html) {
+    element = $(element);
+    html = typeof html == 'undefined' ? '' : html.toString();
+    if (element.outerHTML) {
+      element.outerHTML = html.stripScripts();
+    } else {
+      var range = element.ownerDocument.createRange();
+      range.selectNodeContents(element);
+      element.parentNode.replaceChild(
+        range.createContextualFragment(html.stripScripts()), element);
+    }
+    setTimeout(function() {html.evalScripts()}, 10);
+    return element;
+  },
+
+  inspect: function(element) {
+    element = $(element);
+    var result = '<' + element.tagName.toLowerCase();
+    $H({'id': 'id', 'className': 'class'}).each(function(pair) {
+      var property = pair.first(), attribute = pair.last();
+      var value = (element[property] || '').toString();
+      if (value) result += ' ' + attribute + '=' + value.inspect(true);
+    });
+    return result + '>';
+  },
+
+  recursivelyCollect: function(element, property) {
+    element = $(element);
+    var elements = [];
+    while (element = element[property])
+      if (element.nodeType == 1)
+        elements.push(Element.extend(element));
+    return elements;
+  },
+
+  ancestors: function(element) {
+    return $(element).recursivelyCollect('parentNode');
+  },
+
+  descendants: function(element) {
+    return $A($(element).getElementsByTagName('*')).each(Element.extend);
+  },
+
+  firstDescendant: function(element) {
+    element = $(element).firstChild;
+    while (element && element.nodeType != 1) element = element.nextSibling;
+    return $(element);
+  },
+
+  immediateDescendants: function(element) {
+    if (!(element = $(element).firstChild)) return [];
+    while (element && element.nodeType != 1) element = element.nextSibling;
+    if (element) return [element].concat($(element).nextSiblings());
+    return [];
+  },
+
+  previousSiblings: function(element) {
+    return $(element).recursivelyCollect('previousSibling');
+  },
+
+  nextSiblings: function(element) {
+    return $(element).recursivelyCollect('nextSibling');
+  },
+
+  siblings: function(element) {
+    element = $(element);
+    return element.previousSiblings().reverse().concat(element.nextSiblings());
+  },
+
+  match: function(element, selector) {
+    if (typeof selector == 'string')
+      selector = new Selector(selector);
+    return selector.match($(element));
+  },
+
+  up: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return $(element.parentNode);
+    var ancestors = element.ancestors();
+    return expression ? Selector.findElement(ancestors, expression, index) :
+      ancestors[index || 0];
+  },
+
+  down: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return element.firstDescendant();
+    var descendants = element.descendants();
+    return expression ? Selector.findElement(descendants, expression, index) :
+      descendants[index || 0];
+  },
+
+  previous: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
+    var previousSiblings = element.previousSiblings();
+    return expression ? Selector.findElement(previousSiblings, expression, index) :
+      previousSiblings[index || 0];
+  },
+
+  next: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
+    var nextSiblings = element.nextSiblings();
+    return expression ? Selector.findElement(nextSiblings, expression, index) :
+      nextSiblings[index || 0];
+  },
+
+  getElementsBySelector: function() {
+    var args = $A(arguments), element = $(args.shift());
+    return Selector.findChildElements(element, args);
+  },
+
+  getElementsByClassName: function(element, className) {
+    return document.getElementsByClassName(className, element);
+  },
+
+  readAttribute: function(element, name) {
+    element = $(element);
+    if (Prototype.Browser.IE) {
+      if (!element.attributes) return null;
+      var t = Element._attributeTranslations;
+      if (t.values[name]) return t.values[name](element, name);
+      if (t.names[name])  name = t.names[name];
+      var attribute = element.attributes[name];
+      return attribute ? attribute.nodeValue : null;
+    }
+    return element.getAttribute(name);
+  },
+
+  getHeight: function(element) {
+    return $(element).getDimensions().height;
+  },
+
+  getWidth: function(element) {
+    return $(element).getDimensions().width;
+  },
+
+  classNames: function(element) {
+    return new Element.ClassNames(element);
+  },
+
+  hasClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    var elementClassName = element.className;
+    if (elementClassName.length == 0) return false;
+    if (elementClassName == className ||
+        elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
+      return true;
+    return false;
+  },
+
+  addClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    Element.classNames(element).add(className);
+    return element;
+  },
+
+  removeClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    Element.classNames(element).remove(className);
+    return element;
+  },
+
+  toggleClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
+    return element;
+  },
+
+  observe: function() {
+    Event.observe.apply(Event, arguments);
+    return $A(arguments).first();
+  },
+
+  stopObserving: function() {
+    Event.stopObserving.apply(Event, arguments);
+    return $A(arguments).first();
+  },
+
+  // removes whitespace-only text node children
+  cleanWhitespace: function(element) {
+    element = $(element);
+    var node = element.firstChild;
+    while (node) {
+      var nextNode = node.nextSibling;
+      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
+        element.removeChild(node);
+      node = nextNode;
+    }
+    return element;
+  },
+
+  empty: function(element) {
+    return $(element).innerHTML.blank();
+  },
+
+  descendantOf: function(element, ancestor) {
+    element = $(element), ancestor = $(ancestor);
+    while (element = element.parentNode)
+      if (element == ancestor) return true;
+    return false;
+  },
+
+  scrollTo: function(element) {
+    element = $(element);
+    var pos = Position.cumulativeOffset(element);
+    window.scrollTo(pos[0], pos[1]);
+    return element;
+  },
+
+  getStyle: function(element, style) {
+    element = $(element);
+    style = style == 'float' ? 'cssFloat' : style.camelize();
+    var value = element.style[style];
+    if (!value) {
+      var css = document.defaultView.getComputedStyle(element, null);
+      value = css ? css[style] : null;
+    }
+    if (style == 'opacity') return value ? parseFloat(value) : 1.0;
+    return value == 'auto' ? null : value;
+  },
+
+  getOpacity: function(element) {
+    return $(element).getStyle('opacity');
+  },
+
+  setStyle: function(element, styles, camelized) {
+    element = $(element);
+    var elementStyle = element.style;
+
+    for (var property in styles)
+      if (property == 'opacity') element.setOpacity(styles[property])
+      else
+        elementStyle[(property == 'float' || property == 'cssFloat') ?
+          (elementStyle.styleFloat === undefined ? 'cssFloat' : 'styleFloat') :
+          (camelized ? property : property.camelize())] = styles[property];
+
+    return element;
+  },
+
+  setOpacity: function(element, value) {
+    element = $(element);
+    element.style.opacity = (value == 1 || value === '') ? '' :
+      (value < 0.00001) ? 0 : value;
+    return element;
+  },
+
+  getDimensions: function(element) {
+    element = $(element);
+    var display = $(element).getStyle('display');
+    if (display != 'none' && display != null) // Safari bug
+      return {width: element.offsetWidth, height: element.offsetHeight};
+
+    // All *Width and *Height properties give 0 on elements with display none,
+    // so enable the element temporarily
+    var els = element.style;
+    var originalVisibility = els.visibility;
+    var originalPosition = els.position;
+    var originalDisplay = els.display;
+    els.visibility = 'hidden';
+    els.position = 'absolute';
+    els.display = 'block';
+    var originalWidth = element.clientWidth;
+    var originalHeight = element.clientHeight;
+    els.display = originalDisplay;
+    els.position = originalPosition;
+    els.visibility = originalVisibility;
+    return {width: originalWidth, height: originalHeight};
+  },
+
+  makePositioned: function(element) {
+    element = $(element);
+    var pos = Element.getStyle(element, 'position');
+    if (pos == 'static' || !pos) {
+      element._madePositioned = true;
+      element.style.position = 'relative';
+      // Opera returns the offset relative to the positioning context, when an
+      // element is position relative but top and left have not been defined
+      if (window.opera) {
+        element.style.top = 0;
+        element.style.left = 0;
+      }
+    }
+    return element;
+  },
+
+  undoPositioned: function(element) {
+    element = $(element);
+    if (element._madePositioned) {
+      element._madePositioned = undefined;
+      element.style.position =
+        element.style.top =
+        element.style.left =
+        element.style.bottom =
+        element.style.right = '';
+    }
+    return element;
+  },
+
+  makeClipping: function(element) {
+    element = $(element);
+    if (element._overflow) return element;
+    element._overflow = element.style.overflow || 'auto';
+    if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
+      element.style.overflow = 'hidden';
+    return element;
+  },
+
+  undoClipping: function(element) {
+    element = $(element);
+    if (!element._overflow) return element;
+    element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
+    element._overflow = null;
+    return element;
+  }
+};
+
+Object.extend(Element.Methods, {
+  childOf: Element.Methods.descendantOf,
+  childElements: Element.Methods.immediateDescendants
+});
+
+if (Prototype.Browser.Opera) {
+  Element.Methods._getStyle = Element.Methods.getStyle;
+  Element.Methods.getStyle = function(element, style) {
+    switch(style) {
+      case 'left':
+      case 'top':
+      case 'right':
+      case 'bottom':
+        if (Element._getStyle(element, 'position') == 'static') return null;
+      default: return Element._getStyle(element, style);
+    }
+  };
+}
+else if (Prototype.Browser.IE) {
+  Element.Methods.getStyle = function(element, style) {
+    element = $(element);
+    style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
+    var value = element.style[style];
+    if (!value && element.currentStyle) value = element.currentStyle[style];
+
+    if (style == 'opacity') {
+      if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
+        if (value[1]) return parseFloat(value[1]) / 100;
+      return 1.0;
+    }
+
+    if (value == 'auto') {
+      if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
+        return element['offset'+style.capitalize()] + 'px';
+      return null;
+    }
+    return value;
+  };
+
+  Element.Methods.setOpacity = function(element, value) {
+    element = $(element);
+    var filter = element.getStyle('filter'), style = element.style;
+    if (value == 1 || value === '') {
+      style.filter = filter.replace(/alpha\([^\)]*\)/gi,'');
+      return element;
+    } else if (value < 0.00001) value = 0;
+    style.filter = filter.replace(/alpha\([^\)]*\)/gi, '') +
+      'alpha(opacity=' + (value * 100) + ')';
+    return element;
+  };
+
+  // IE is missing .innerHTML support for TABLE-related elements
+  Element.Methods.update = function(element, html) {
+    element = $(element);
+    html = typeof html == 'undefined' ? '' : html.toString();
+    var tagName = element.tagName.toUpperCase();
+    if (['THEAD','TBODY','TR','TD'].include(tagName)) {
+      var div = document.createElement('div');
+      switch (tagName) {
+        case 'THEAD':
+        case 'TBODY':
+          div.innerHTML = '<table><tbody>' +  html.stripScripts() + '</tbody></table>';
+          depth = 2;
+          break;
+        case 'TR':
+          div.innerHTML = '<table><tbody><tr>' +  html.stripScripts() + '</tr></tbody></table>';
+          depth = 3;
+          break;
+        case 'TD':
+          div.innerHTML = '<table><tbody><tr><td>' +  html.stripScripts() + '</td></tr></tbody></table>';
+          depth = 4;
+      }
+      $A(element.childNodes).each(function(node) { element.removeChild(node) });
+      depth.times(function() { div = div.firstChild });
+      $A(div.childNodes).each(function(node) { element.appendChild(node) });
+    } else {
+      element.innerHTML = html.stripScripts();
+    }
+    setTimeout(function() { html.evalScripts() }, 10);
+    return element;
+  }
+}
+else if (Prototype.Browser.Gecko) {
+  Element.Methods.setOpacity = function(element, value) {
+    element = $(element);
+    element.style.opacity = (value == 1) ? 0.999999 :
+      (value === '') ? '' : (value < 0.00001) ? 0 : value;
+    return element;
+  };
+}
+
+Element._attributeTranslations = {
+  names: {
+    colspan:   "colSpan",
+    rowspan:   "rowSpan",
+    valign:    "vAlign",
+    datetime:  "dateTime",
+    accesskey: "accessKey",
+    tabindex:  "tabIndex",
+    enctype:   "encType",
+    maxlength: "maxLength",
+    readonly:  "readOnly",
+    longdesc:  "longDesc"
+  },
+  values: {
+    _getAttr: function(element, attribute) {
+      return element.getAttribute(attribute, 2);
+    },
+    _flag: function(element, attribute) {
+      return $(element).hasAttribute(attribute) ? attribute : null;
+    },
+    style: function(element) {
+      return element.style.cssText.toLowerCase();
+    },
+    title: function(element) {
+      var node = element.getAttributeNode('title');
+      return node.specified ? node.nodeValue : null;
+    }
+  }
+};
+
+(function() {
+  Object.extend(this, {
+    href: this._getAttr,
+    src:  this._getAttr,
+    disabled: this._flag,
+    checked:  this._flag,
+    readonly: this._flag,
+    multiple: this._flag
+  });
+}).call(Element._attributeTranslations.values);
+
+Element.Methods.Simulated = {
+  hasAttribute: function(element, attribute) {
+    var t = Element._attributeTranslations, node;
+    attribute = t.names[attribute] || attribute;
+    node = $(element).getAttributeNode(attribute);
+    return node && node.specified;
+  }
+};
+
+Element.Methods.ByTag = {};
+
+Object.extend(Element, Element.Methods);
+
+if (!Prototype.BrowserFeatures.ElementExtensions &&
+ document.createElement('div').__proto__) {
+  window.HTMLElement = {};
+  window.HTMLElement.prototype = document.createElement('div').__proto__;
+  Prototype.BrowserFeatures.ElementExtensions = true;
+}
+
+Element.hasAttribute = function(element, attribute) {
+  if (element.hasAttribute) return element.hasAttribute(attribute);
+  return Element.Methods.Simulated.hasAttribute(element, attribute);
+};
+
+Element.addMethods = function(methods) {
+  var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
+  if (arguments.length == 2) {
+    var tagName = methods;
+    methods = arguments[1];
+  }
+
+  if (!tagName) Object.extend(Element.Methods, methods || {});
+  else {
+    if (tagName.constructor == Array) tagName.each(extend);
+    else extend(tagName);
+  }
+
+  function extend(tagName) {
+    tagName = tagName.toUpperCase();
+    if (!Element.Methods.ByTag[tagName])
+      Element.Methods.ByTag[tagName] = {};
+    Object.extend(Element.Methods.ByTag[tagName], methods);
+  }
+
+  function copy(methods, destination, onlyIfAbsent) {
+    onlyIfAbsent = onlyIfAbsent || false;
+    var cache = Element.extend.cache;
+    for (var property in methods) {
+      var value = methods[property];
+      if (!onlyIfAbsent || !(property in destination))
+        destination[property] = cache.findOrStore(value);
+    }
+  }
+
+  function findDOMClass(tagName) {
+    var klass;
+    var trans = {
+      "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
+      "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
+      "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
+      "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
+      "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
+      "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
+      "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
+      "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
+      "FrameSet", "IFRAME": "IFrame"
+    };
+    if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
+    if (window[klass]) return window[klass];
+    klass = 'HTML' + tagName + 'Element';
+    if (window[klass]) return window[klass];
+    klass = 'HTML' + tagName.capitalize() + 'Element';
+    if (window[klass]) return window[klass];
+
+    window[klass] = {};
+    window[klass].prototype = document.createElement(tagName).__proto__;
+    return window[klass];
+  }
+
+  if (F.ElementExtensions) {
+    copy(Element.Methods, HTMLElement.prototype);
+    copy(Element.Methods.Simulated, HTMLElement.prototype, true);
+  }
+
+  if (F.SpecificElementExtensions) {
+    for (var tag in Element.Methods.ByTag) {
+      var klass = findDOMClass(tag);
+      if (typeof klass == "undefined") continue;
+      copy(T[tag], klass.prototype);
+    }
+  }
+
+  Object.extend(Element, Element.Methods);
+  delete Element.ByTag;
+};
+
+var Toggle = { display: Element.toggle };
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.Insertion = function(adjacency) {
+  this.adjacency = adjacency;
+}
+
+Abstract.Insertion.prototype = {
+  initialize: function(element, content) {
+    this.element = $(element);
+    this.content = content.stripScripts();
+
+    if (this.adjacency && this.element.insertAdjacentHTML) {
+      try {
+        this.element.insertAdjacentHTML(this.adjacency, this.content);
+      } catch (e) {
+        var tagName = this.element.tagName.toUpperCase();
+        if (['TBODY', 'TR'].include(tagName)) {
+          this.insertContent(this.contentFromAnonymousTable());
+        } else {
+          throw e;
+        }
+      }
+    } else {
+      this.range = this.element.ownerDocument.createRange();
+      if (this.initializeRange) this.initializeRange();
+      this.insertContent([this.range.createContextualFragment(this.content)]);
+    }
+
+    setTimeout(function() {content.evalScripts()}, 10);
+  },
+
+  contentFromAnonymousTable: function() {
+    var div = document.createElement('div');
+    div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
+    return $A(div.childNodes[0].childNodes[0].childNodes);
+  }
+}
+
+var Insertion = new Object();
+
+Insertion.Before = Class.create();
+Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
+  initializeRange: function() {
+    this.range.setStartBefore(this.element);
+  },
+
+  insertContent: function(fragments) {
+    fragments.each((function(fragment) {
+      this.element.parentNode.insertBefore(fragment, this.element);
+    }).bind(this));
+  }
+});
+
+Insertion.Top = Class.create();
+Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
+  initializeRange: function() {
+    this.range.selectNodeContents(this.element);
+    this.range.collapse(true);
+  },
+
+  insertContent: function(fragments) {
+    fragments.reverse(false).each((function(fragment) {
+      this.element.insertBefore(fragment, this.element.firstChild);
+    }).bind(this));
+  }
+});
+
+Insertion.Bottom = Class.create();
+Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
+  initializeRange: function() {
+    this.range.selectNodeContents(this.element);
+    this.range.collapse(this.element);
+  },
+
+  insertContent: function(fragments) {
+    fragments.each((function(fragment) {
+      this.element.appendChild(fragment);
+    }).bind(this));
+  }
+});
+
+Insertion.After = Class.create();
+Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
+  initializeRange: function() {
+    this.range.setStartAfter(this.element);
+  },
+
+  insertContent: function(fragments) {
+    fragments.each((function(fragment) {
+      this.element.parentNode.insertBefore(fragment,
+        this.element.nextSibling);
+    }).bind(this));
+  }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Element.ClassNames = Class.create();
+Element.ClassNames.prototype = {
+  initialize: function(element) {
+    this.element = $(element);
+  },
+
+  _each: function(iterator) {
+    this.element.className.split(/\s+/).select(function(name) {
+      return name.length > 0;
+    })._each(iterator);
+  },
+
+  set: function(className) {
+    this.element.className = className;
+  },
+
+  add: function(classNameToAdd) {
+    if (this.include(classNameToAdd)) return;
+    this.set($A(this).concat(classNameToAdd).join(' '));
+  },
+
+  remove: function(classNameToRemove) {
+    if (!this.include(classNameToRemove)) return;
+    this.set($A(this).without(classNameToRemove).join(' '));
+  },
+
+  toString: function() {
+    return $A(this).join(' ');
+  }
+};
+
+Object.extend(Element.ClassNames.prototype, Enumerable);
+/* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
+ * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
+ * license.  Please see http://www.yui-ext.com/ for more information. */
+
+var Selector = Class.create();
+
+Selector.prototype = {
+  initialize: function(expression) {
+    this.expression = expression.strip();
+    this.compileMatcher();
+  },
+
+  compileMatcher: function() {
+    // Selectors with namespaced attributes can't use the XPath version
+    if (Prototype.BrowserFeatures.XPath && !(/\[[\w-]*?:/).test(this.expression))
+      return this.compileXPathMatcher();
+
+    var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
+        c = Selector.criteria, le, p, m;
+
+    if (Selector._cache[e]) {
+      this.matcher = Selector._cache[e]; return;
+    }
+    this.matcher = ["this.matcher = function(root) {",
+                    "var r = root, h = Selector.handlers, c = false, n;"];
+
+    while (e && le != e && (/\S/).test(e)) {
+      le = e;
+      for (var i in ps) {
+        p = ps[i];
+        if (m = e.match(p)) {
+          this.matcher.push(typeof c[i] == 'function' ? c[i](m) :
+             new Template(c[i]).evaluate(m));
+          e = e.replace(m[0], '');
+          break;
+        }
+      }
+    }
+
+    this.matcher.push("return h.unique(n);\n}");
+    eval(this.matcher.join('\n'));
+    Selector._cache[this.expression] = this.matcher;
+  },
+
+  compileXPathMatcher: function() {
+    var e = this.expression, ps = Selector.patterns,
+        x = Selector.xpath, le,  m;
+
+    if (Selector._cache[e]) {
+      this.xpath = Selector._cache[e]; return;
+    }
+
+    this.matcher = ['.//*'];
+    while (e && le != e && (/\S/).test(e)) {
+      le = e;
+      for (var i in ps) {
+        if (m = e.match(ps[i])) {
+          this.matcher.push(typeof x[i] == 'function' ? x[i](m) :
+            new Template(x[i]).evaluate(m));
+          e = e.replace(m[0], '');
+          break;
+        }
+      }
+    }
+
+    this.xpath = this.matcher.join('');
+    Selector._cache[this.expression] = this.xpath;
+  },
+
+  findElements: function(root) {
+    root = root || document;
+    if (this.xpath) return document._getElementsByXPath(this.xpath, root);
+    return this.matcher(root);
+  },
+
+  match: function(element) {
+    return this.findElements(document).include(element);
+  },
+
+  toString: function() {
+    return this.expression;
+  },
+
+  inspect: function() {
+    return "#<Selector:" + this.expression.inspect() + ">";
+  }
+};
+
+Object.extend(Selector, {
+  _cache: {},
+
+  xpath: {
+    descendant:   "//*",
+    child:        "/*",
+    adjacent:     "/following-sibling::*[1]",
+    laterSibling: '/following-sibling::*',
+    tagName:      function(m) {
+      if (m[1] == '*') return '';
+      return "[local-name()='" + m[1].toLowerCase() +
+             "' or local-name()='" + m[1].toUpperCase() + "']";
+    },
+    className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]",
+    id:           "[@id='#{1}']",
+    attrPresence: "[@#{1}]",
+    attr: function(m) {
+      m[3] = m[5] || m[6];
+      return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
+    },
+    pseudo: function(m) {
+      var h = Selector.xpath.pseudos[m[1]];
+      if (!h) return '';
+      if (typeof h === 'function') return h(m);
+      return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
+    },
+    operators: {
+      '=':  "[@#{1}='#{3}']",
+      '!=': "[@#{1}!='#{3}']",
+      '^=': "[starts-with(@#{1}, '#{3}')]",
+      '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
+      '*=': "[contains(@#{1}, '#{3}')]",
+      '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
+      '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
+    },
+    pseudos: {
+      'first-child': '[not(preceding-sibling::*)]',
+      'last-child':  '[not(following-sibling::*)]',
+      'only-child':  '[not(preceding-sibling::* or following-sibling::*)]',
+      'empty':       "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
+      'checked':     "[@checked]",
+      'disabled':    "[@disabled]",
+      'enabled':     "[not(@disabled)]",
+      'not': function(m) {
+        var e = m[6], p = Selector.patterns,
+            x = Selector.xpath, le, m, v;
+
+        var exclusion = [];
+        while (e && le != e && (/\S/).test(e)) {
+          le = e;
+          for (var i in p) {
+            if (m = e.match(p[i])) {
+              v = typeof x[i] == 'function' ? x[i](m) : new Template(x[i]).evaluate(m);
+              exclusion.push("(" + v.substring(1, v.length - 1) + ")");
+              e = e.replace(m[0], '');
+              break;
+            }
+          }
+        }
+        return "[not(" + exclusion.join(" and ") + ")]";
+      },
+      'nth-child':      function(m) {
+        return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
+      },
+      'nth-last-child': function(m) {
+        return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
+      },
+      'nth-of-type':    function(m) {
+        return Selector.xpath.pseudos.nth("position() ", m);
+      },
+      'nth-last-of-type': function(m) {
+        return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
+      },
+      'first-of-type':  function(m) {
+        m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
+      },
+      'last-of-type':   function(m) {
+        m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
+      },
+      'only-of-type':   function(m) {
+        var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
+      },
+      nth: function(fragment, m) {
+        var mm, formula = m[6], predicate;
+        if (formula == 'even') formula = '2n+0';
+        if (formula == 'odd')  formula = '2n+1';
+        if (mm = formula.match(/^(\d+)$/)) // digit only
+          return '[' + fragment + "= " + mm[1] + ']';
+        if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+          if (mm[1] == "-") mm[1] = -1;
+          var a = mm[1] ? Number(mm[1]) : 1;
+          var b = mm[2] ? Number(mm[2]) : 0;
+          predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
+          "((#{fragment} - #{b}) div #{a} >= 0)]";
+          return new Template(predicate).evaluate({
+            fragment: fragment, a: a, b: b });
+        }
+      }
+    }
+  },
+
+  criteria: {
+    tagName:      'n = h.tagName(n, r, "#{1}", c);   c = false;',
+    className:    'n = h.className(n, r, "#{1}", c); c = false;',
+    id:           'n = h.id(n, r, "#{1}", c);        c = false;',
+    attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;',
+    attr: function(m) {
+      m[3] = (m[5] || m[6]);
+      return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m);
+    },
+    pseudo:       function(m) {
+      if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
+      return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
+    },
+    descendant:   'c = "descendant";',
+    child:        'c = "child";',
+    adjacent:     'c = "adjacent";',
+    laterSibling: 'c = "laterSibling";'
+  },
+
+  patterns: {
+    // combinators must be listed first
+    // (and descendant needs to be last combinator)
+    laterSibling: /^\s*~\s*/,
+    child:        /^\s*>\s*/,
+    adjacent:     /^\s*\+\s*/,
+    descendant:   /^\s/,
+
+    // selectors follow
+    tagName:      /^\s*(\*|[\w\-]+)(\b|$)?/,
+    id:           /^#([\w\-\*]+)(\b|$)/,
+    className:    /^\.([\w\-\*]+)(\b|$)/,
+    pseudo:       /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|\s)/,
+    attrPresence: /^\[([\w]+)\]/,
+    attr:         /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\]]*?)\4|([^'"][^\]]*?)))?\]/
+  },
+
+  handlers: {
+    // UTILITY FUNCTIONS
+    // joins two collections
+    concat: function(a, b) {
+      for (var i = 0, node; node = b[i]; i++)
+        a.push(node);
+      return a;
+    },
+
+    // marks an array of nodes for counting
+    mark: function(nodes) {
+      for (var i = 0, node; node = nodes[i]; i++)
+        node._counted = true;
+      return nodes;
+    },
+
+    unmark: function(nodes) {
+      for (var i = 0, node; node = nodes[i]; i++)
+        node._counted = undefined;
+      return nodes;
+    },
+
+    // mark each child node with its position (for nth calls)
+    // "ofType" flag indicates whether we're indexing for nth-of-type
+    // rather than nth-child
+    index: function(parentNode, reverse, ofType) {
+      parentNode._counted = true;
+      if (reverse) {
+        for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
+          node = nodes[i];
+          if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
+        }
+      } else {
+        for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
+          if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
+      }
+    },
+
+    // filters out duplicates and extends all nodes
+    unique: function(nodes) {
+      if (nodes.length == 0) return nodes;
+      var results = [], n;
+      for (var i = 0, l = nodes.length; i < l; i++)
+        if (!(n = nodes[i])._counted) {
+          n._counted = true;
+          results.push(Element.extend(n));
+        }
+      return Selector.handlers.unmark(results);
+    },
+
+    // COMBINATOR FUNCTIONS
+    descendant: function(nodes) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        h.concat(results, node.getElementsByTagName('*'));
+      return results;
+    },
+
+    child: function(nodes) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        for (var j = 0, children = [], child; child = node.childNodes[j]; j++)
+          if (child.nodeType == 1 && child.tagName != '!') results.push(child);
+      }
+      return results;
+    },
+
+    adjacent: function(nodes) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        var next = this.nextElementSibling(node);
+        if (next) results.push(next);
+      }
+      return results;
+    },
+
+    laterSibling: function(nodes) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        h.concat(results, Element.nextSiblings(node));
+      return results;
+    },
+
+    nextElementSibling: function(node) {
+      while (node = node.nextSibling)
+             if (node.nodeType == 1) return node;
+      return null;
+    },
+
+    previousElementSibling: function(node) {
+      while (node = node.previousSibling)
+        if (node.nodeType == 1) return node;
+      return null;
+    },
+
+    // TOKEN FUNCTIONS
+    tagName: function(nodes, root, tagName, combinator) {
+      tagName = tagName.toUpperCase();
+      var results = [], h = Selector.handlers;
+      if (nodes) {
+        if (combinator) {
+          // fastlane for ordinary descendant combinators
+          if (combinator == "descendant") {
+            for (var i = 0, node; node = nodes[i]; i++)
+              h.concat(results, node.getElementsByTagName(tagName));
+            return results;
+          } else nodes = this[combinator](nodes);
+          if (tagName == "*") return nodes;
+        }
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node.tagName.toUpperCase() == tagName) results.push(node);
+        return results;
+      } else return root.getElementsByTagName(tagName);
+    },
+
+    id: function(nodes, root, id, combinator) {
+      var targetNode = $(id), h = Selector.handlers;
+      if (!nodes && root == document) return targetNode ? [targetNode] : [];
+      if (nodes) {
+        if (combinator) {
+          if (combinator == 'child') {
+            for (var i = 0, node; node = nodes[i]; i++)
+              if (targetNode.parentNode == node) return [targetNode];
+          } else if (combinator == 'descendant') {
+            for (var i = 0, node; node = nodes[i]; i++)
+              if (Element.descendantOf(targetNode, node)) return [targetNode];
+          } else if (combinator == 'adjacent') {
+            for (var i = 0, node; node = nodes[i]; i++)
+              if (Selector.handlers.previousElementSibling(targetNode) == node)
+                return [targetNode];
+          } else nodes = h[combinator](nodes);
+        }
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node == targetNode) return [targetNode];
+        return [];
+      }
+      return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
+    },
+
+    className: function(nodes, root, className, combinator) {
+      if (nodes && combinator) nodes = this[combinator](nodes);
+      return Selector.handlers.byClassName(nodes, root, className);
+    },
+
+    byClassName: function(nodes, root, className) {
+      if (!nodes) nodes = Selector.handlers.descendant([root]);
+      var needle = ' ' + className + ' ';
+      for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
+        nodeClassName = node.className;
+        if (nodeClassName.length == 0) continue;
+        if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
+          results.push(node);
+      }
+      return results;
+    },
+
+    attrPresence: function(nodes, root, attr) {
+      var results = [];
+      for (var i = 0, node; node = nodes[i]; i++)
+        if (Element.hasAttribute(node, attr)) results.push(node);
+      return results;
+    },
+
+    attr: function(nodes, root, attr, value, operator) {
+      if (!nodes) nodes = root.getElementsByTagName("*");
+      var handler = Selector.operators[operator], results = [];
+      for (var i = 0, node; node = nodes[i]; i++) {
+        var nodeValue = Element.readAttribute(node, attr);
+        if (nodeValue === null) continue;
+        if (handler(nodeValue, value)) results.push(node);
+      }
+      return results;
+    },
+
+    pseudo: function(nodes, name, value, root, combinator) {
+      if (nodes && combinator) nodes = this[combinator](nodes);
+      if (!nodes) nodes = root.getElementsByTagName("*");
+      return Selector.pseudos[name](nodes, value, root);
+    }
+  },
+
+  pseudos: {
+    'first-child': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        if (Selector.handlers.previousElementSibling(node)) continue;
+          results.push(node);
+      }
+      return results;
+    },
+    'last-child': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        if (Selector.handlers.nextElementSibling(node)) continue;
+          results.push(node);
+      }
+      return results;
+    },
+    'only-child': function(nodes, value, root) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
+          results.push(node);
+      return results;
+    },
+    'nth-child':        function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root);
+    },
+    'nth-last-child':   function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root, true);
+    },
+    'nth-of-type':      function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root, false, true);
+    },
+    'nth-last-of-type': function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root, true, true);
+    },
+    'first-of-type':    function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, "1", root, false, true);
+    },
+    'last-of-type':     function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, "1", root, true, true);
+    },
+    'only-of-type':     function(nodes, formula, root) {
+      var p = Selector.pseudos;
+      return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
+    },
+
+    // handles the an+b logic
+    getIndices: function(a, b, total) {
+      if (a == 0) return b > 0 ? [b] : [];
+      return $R(1, total).inject([], function(memo, i) {
+        if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
+        return memo;
+      });
+    },
+
+    // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
+    nth: function(nodes, formula, root, reverse, ofType) {
+      if (nodes.length == 0) return [];
+      if (formula == 'even') formula = '2n+0';
+      if (formula == 'odd')  formula = '2n+1';
+      var h = Selector.handlers, results = [], indexed = [], m;
+      h.mark(nodes);
+      for (var i = 0, node; node = nodes[i]; i++) {
+        if (!node.parentNode._counted) {
+          h.index(node.parentNode, reverse, ofType);
+          indexed.push(node.parentNode);
+        }
+      }
+      if (formula.match(/^\d+$/)) { // just a number
+        formula = Number(formula);
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node.nodeIndex == formula) results.push(node);
+      } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+        if (m[1] == "-") m[1] = -1;
+        var a = m[1] ? Number(m[1]) : 1;
+        var b = m[2] ? Number(m[2]) : 0;
+        var indices = Selector.pseudos.getIndices(a, b, nodes.length);
+        for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
+          for (var j = 0; j < l; j++)
+            if (node.nodeIndex == indices[j]) results.push(node);
+        }
+      }
+      h.unmark(nodes);
+      h.unmark(indexed);
+      return results;
+    },
+
+    'empty': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        // IE treats comments as element nodes
+        if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
+        results.push(node);
+      }
+      return results;
+    },
+
+    'not': function(nodes, selector, root) {
+      var h = Selector.handlers, selectorType, m;
+      var exclusions = new Selector(selector).findElements(root);
+      h.mark(exclusions);
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (!node._counted) results.push(node);
+      h.unmark(exclusions);
+      return results;
+    },
+
+    'enabled': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (!node.disabled) results.push(node);
+      return results;
+    },
+
+    'disabled': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (node.disabled) results.push(node);
+      return results;
+    },
+
+    'checked': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (node.checked) results.push(node);
+      return results;
+    }
+  },
+
+  operators: {
+    '=':  function(nv, v) { return nv == v; },
+    '!=': function(nv, v) { return nv != v; },
+    '^=': function(nv, v) { return nv.startsWith(v); },
+    '$=': function(nv, v) { return nv.endsWith(v); },
+    '*=': function(nv, v) { return nv.include(v); },
+    '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
+    '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
+  },
+
+  matchElements: function(elements, expression) {
+    var matches = new Selector(expression).findElements(), h = Selector.handlers;
+    h.mark(matches);
+    for (var i = 0, results = [], element; element = elements[i]; i++)
+      if (element._counted) results.push(element);
+    h.unmark(matches);
+    return results;
+  },
+
+  findElement: function(elements, expression, index) {
+    if (typeof expression == 'number') {
+      index = expression; expression = false;
+    }
+    return Selector.matchElements(elements, expression || '*')[index || 0];
+  },
+
+  findChildElements: function(element, expressions) {
+    var exprs = expressions.join(','), expressions = [];
+    exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
+      expressions.push(m[1].strip());
+    });
+    var results = [], h = Selector.handlers;
+    for (var i = 0, l = expressions.length, selector; i < l; i++) {
+      selector = new Selector(expressions[i].strip());
+      h.concat(results, selector.findElements(element));
+    }
+    return (l > 1) ? h.unique(results) : results;
+  }
+});
+
+function $$() {
+  return Selector.findChildElements(document, $A(arguments));
+}
+var Form = {
+  reset: function(form) {
+    $(form).reset();
+    return form;
+  },
+
+  serializeElements: function(elements, getHash) {
+    var data = elements.inject({}, function(result, element) {
+      if (!element.disabled && element.name) {
+        var key = element.name, value = $(element).getValue();
+        if (value != null) {
+               if (key in result) {
+            if (result[key].constructor != Array) result[key] = [result[key]];
+            result[key].push(value);
+          }
+          else result[key] = value;
+        }
+      }
+      return result;
+    });
+
+    return getHash ? data : Hash.toQueryString(data);
+  }
+};
+
+Form.Methods = {
+  serialize: function(form, getHash) {
+    return Form.serializeElements(Form.getElements(form), getHash);
+  },
+
+  getElements: function(form) {
+    return $A($(form).getElementsByTagName('*')).inject([],
+      function(elements, child) {
+        if (Form.Element.Serializers[child.tagName.toLowerCase()])
+          elements.push(Element.extend(child));
+        return elements;
+      }
+    );
+  },
+
+  getInputs: function(form, typeName, name) {
+    form = $(form);
+    var inputs = form.getElementsByTagName('input');
+
+    if (!typeName && !name) return $A(inputs).map(Element.extend);
+
+    for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
+      var input = inputs[i];
+      if ((typeName && input.type != typeName) || (name && input.name != name))
+        continue;
+      matchingInputs.push(Element.extend(input));
+    }
+
+    return matchingInputs;
+  },
+
+  disable: function(form) {
+    form = $(form);
+    Form.getElements(form).invoke('disable');
+    return form;
+  },
+
+  enable: function(form) {
+    form = $(form);
+    Form.getElements(form).invoke('enable');
+    return form;
+  },
+
+  findFirstElement: function(form) {
+    return $(form).getElements().find(function(element) {
+      return element.type != 'hidden' && !element.disabled &&
+        ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
+    });
+  },
+
+  focusFirstElement: function(form) {
+    form = $(form);
+    form.findFirstElement().activate();
+    return form;
+  },
+
+  request: function(form, options) {
+    form = $(form), options = Object.clone(options || {});
+
+    var params = options.parameters;
+    options.parameters = form.serialize(true);
+
+    if (params) {
+      if (typeof params == 'string') params = params.toQueryParams();
+      Object.extend(options.parameters, params);
+    }
+
+    if (form.hasAttribute('method') && !options.method)
+      options.method = form.method;
+
+    return new Ajax.Request(form.readAttribute('action'), options);
+  }
+}
+
+Object.extend(Form, Form.Methods);
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element = {
+  focus: function(element) {
+    $(element).focus();
+    return element;
+  },
+
+  select: function(element) {
+    $(element).select();
+    return element;
+  }
+}
+
+Form.Element.Methods = {
+  serialize: function(element) {
+    element = $(element);
+    if (!element.disabled && element.name) {
+      var value = element.getValue();
+      if (value != undefined) {
+        var pair = {};
+        pair[element.name] = value;
+        return Hash.toQueryString(pair);
+      }
+    }
+    return '';
+  },
+
+  getValue: function(element) {
+    element = $(element);
+    var method = element.tagName.toLowerCase();
+    return Form.Element.Serializers[method](element);
+  },
+
+  clear: function(element) {
+    $(element).value = '';
+    return element;
+  },
+
+  present: function(element) {
+    return $(element).value != '';
+  },
+
+  activate: function(element) {
+    element = $(element);
+    try {
+      element.focus();
+      if (element.select && (element.tagName.toLowerCase() != 'input' ||
+        !['button', 'reset', 'submit'].include(element.type)))
+        element.select();
+    } catch (e) {}
+    return element;
+  },
+
+  disable: function(element) {
+    element = $(element);
+    element.blur();
+    element.disabled = true;
+    return element;
+  },
+
+  enable: function(element) {
+    element = $(element);
+    element.disabled = false;
+    return element;
+  }
+}
+
+Object.extend(Form.Element, Form.Element.Methods);
+Object.extend(Element.Methods.ByTag, {
+  "FORM":     Object.clone(Form.Methods),
+  "INPUT":    Object.clone(Form.Element.Methods),
+  "SELECT":   Object.clone(Form.Element.Methods),
+  "TEXTAREA": Object.clone(Form.Element.Methods)
+});
+
+/*--------------------------------------------------------------------------*/
+
+var Field = Form.Element;
+var $F = Form.Element.getValue;
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element.Serializers = {
+  input: function(element) {
+    switch (element.type.toLowerCase()) {
+      case 'checkbox':
+      case 'radio':
+        return Form.Element.Serializers.inputSelector(element);
+      default:
+        return Form.Element.Serializers.textarea(element);
+    }
+  },
+
+  inputSelector: function(element) {
+    return element.checked ? element.value : null;
+  },
+
+  textarea: function(element) {
+    return element.value;
+  },
+
+  select: function(element) {
+    return this[element.type == 'select-one' ?
+      'selectOne' : 'selectMany'](element);
+  },
+
+  selectOne: function(element) {
+    var index = element.selectedIndex;
+    return index >= 0 ? this.optionValue(element.options[index]) : null;
+  },
+
+  selectMany: function(element) {
+    var values, length = element.length;
+    if (!length) return null;
+
+    for (var i = 0, values = []; i < length; i++) {
+      var opt = element.options[i];
+      if (opt.selected) values.push(this.optionValue(opt));
+    }
+    return values;
+  },
+
+  optionValue: function(opt) {
+    // extend element because hasAttribute may not be native
+    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.TimedObserver = function() {}
+Abstract.TimedObserver.prototype = {
+  initialize: function(element, frequency, callback) {
+    this.frequency = frequency;
+    this.element   = $(element);
+    this.callback  = callback;
+
+    this.lastValue = this.getValue();
+    this.registerCallback();
+  },
+
+  registerCallback: function() {
+    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+  },
+
+  onTimerEvent: function() {
+    var value = this.getValue();
+    var changed = ('string' == typeof this.lastValue && 'string' == typeof value
+      ? this.lastValue != value : String(this.lastValue) != String(value));
+    if (changed) {
+      this.callback(this.element, value);
+      this.lastValue = value;
+    }
+  }
+}
+
+Form.Element.Observer = Class.create();
+Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+  getValue: function() {
+    return Form.Element.getValue(this.element);
+  }
+});
+
+Form.Observer = Class.create();
+Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+  getValue: function() {
+    return Form.serialize(this.element);
+  }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.EventObserver = function() {}
+Abstract.EventObserver.prototype = {
+  initialize: function(element, callback) {
+    this.element  = $(element);
+    this.callback = callback;
+
+    this.lastValue = this.getValue();
+    if (this.element.tagName.toLowerCase() == 'form')
+      this.registerFormCallbacks();
+    else
+      this.registerCallback(this.element);
+  },
+
+  onElementEvent: function() {
+    var value = this.getValue();
+    if (this.lastValue != value) {
+      this.callback(this.element, value);
+      this.lastValue = value;
+    }
+  },
+
+  registerFormCallbacks: function() {
+    Form.getElements(this.element).each(this.registerCallback.bind(this));
+  },
+
+  registerCallback: function(element) {
+    if (element.type) {
+      switch (element.type.toLowerCase()) {
+        case 'checkbox':
+        case 'radio':
+          Event.observe(element, 'click', this.onElementEvent.bind(this));
+          break;
+        default:
+          Event.observe(element, 'change', this.onElementEvent.bind(this));
+          break;
+      }
+    }
+  }
+}
+
+Form.Element.EventObserver = Class.create();
+Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+  getValue: function() {
+    return Form.Element.getValue(this.element);
+  }
+});
+
+Form.EventObserver = Class.create();
+Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+  getValue: function() {
+    return Form.serialize(this.element);
+  }
+});
+if (!window.Event) {
+  var Event = new Object();
+}
+
+Object.extend(Event, {
+  KEY_BACKSPACE: 8,
+  KEY_TAB:       9,
+  KEY_RETURN:   13,
+  KEY_ESC:      27,
+  KEY_LEFT:     37,
+  KEY_UP:       38,
+  KEY_RIGHT:    39,
+  KEY_DOWN:     40,
+  KEY_DELETE:   46,
+  KEY_HOME:     36,
+  KEY_END:      35,
+  KEY_PAGEUP:   33,
+  KEY_PAGEDOWN: 34,
+
+  element: function(event) {
+    return $(event.target || event.srcElement);
+  },
+
+  isLeftClick: function(event) {
+    return (((event.which) && (event.which == 1)) ||
+            ((event.button) && (event.button == 1)));
+  },
+
+  pointerX: function(event) {
+    return event.pageX || (event.clientX +
+      (document.documentElement.scrollLeft || document.body.scrollLeft));
+  },
+
+  pointerY: function(event) {
+    return event.pageY || (event.clientY +
+      (document.documentElement.scrollTop || document.body.scrollTop));
+  },
+
+  stop: function(event) {
+    if (event.preventDefault) {
+      event.preventDefault();
+      event.stopPropagation();
+    } else {
+      event.returnValue = false;
+      event.cancelBubble = true;
+    }
+  },
+
+  // find the first node with the given tagName, starting from the
+  // node the event was triggered on; traverses the DOM upwards
+  findElement: function(event, tagName) {
+    var element = Event.element(event);
+    while (element.parentNode && (!element.tagName ||
+        (element.tagName.toUpperCase() != tagName.toUpperCase())))
+      element = element.parentNode;
+    return element;
+  },
+
+  observers: false,
+
+  _observeAndCache: function(element, name, observer, useCapture) {
+    if (!this.observers) this.observers = [];
+    if (element.addEventListener) {
+      this.observers.push([element, name, observer, useCapture]);
+      element.addEventListener(name, observer, useCapture);
+    } else if (element.attachEvent) {
+      this.observers.push([element, name, observer, useCapture]);
+      element.attachEvent('on' + name, observer);
+    }
+  },
+
+  unloadCache: function() {
+    if (!Event.observers) return;
+    for (var i = 0, length = Event.observers.length; i < length; i++) {
+      Event.stopObserving.apply(this, Event.observers[i]);
+      Event.observers[i][0] = null;
+    }
+    Event.observers = false;
+  },
+
+  observe: function(element, name, observer, useCapture) {
+    element = $(element);
+    useCapture = useCapture || false;
+
+    if (name == 'keypress' &&
+      (Prototype.Browser.WebKit || element.attachEvent))
+      name = 'keydown';
+
+    Event._observeAndCache(element, name, observer, useCapture);
+  },
+
+  stopObserving: function(element, name, observer, useCapture) {
+    element = $(element);
+    useCapture = useCapture || false;
+
+    if (name == 'keypress' &&
+        (Prototype.Browser.WebKit || element.attachEvent))
+      name = 'keydown';
+
+    if (element.removeEventListener) {
+      element.removeEventListener(name, observer, useCapture);
+    } else if (element.detachEvent) {
+      try {
+        element.detachEvent('on' + name, observer);
+      } catch (e) {}
+    }
+  }
+});
+
+/* prevent memory leaks in IE */
+if (Prototype.Browser.IE)
+  Event.observe(window, 'unload', Event.unloadCache, false);
+var Position = {
+  // set to true if needed, warning: firefox performance problems
+  // NOT neeeded for page scrolling, only if draggable contained in
+  // scrollable elements
+  includeScrollOffsets: false,
+
+  // must be called before calling withinIncludingScrolloffset, every time the
+  // page is scrolled
+  prepare: function() {
+    this.deltaX =  window.pageXOffset
+                || document.documentElement.scrollLeft
+                || document.body.scrollLeft
+                || 0;
+    this.deltaY =  window.pageYOffset
+                || document.documentElement.scrollTop
+                || document.body.scrollTop
+                || 0;
+  },
+
+  realOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.scrollTop  || 0;
+      valueL += element.scrollLeft || 0;
+      element = element.parentNode;
+    } while (element);
+    return [valueL, valueT];
+  },
+
+  cumulativeOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+    } while (element);
+    return [valueL, valueT];
+  },
+
+  positionedOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+      if (element) {
+        if(element.tagName=='BODY') break;
+        var p = Element.getStyle(element, 'position');
+        if (p == 'relative' || p == 'absolute') break;
+      }
+    } while (element);
+    return [valueL, valueT];
+  },
+
+  offsetParent: function(element) {
+    if (element.offsetParent) return element.offsetParent;
+    if (element == document.body) return element;
+
+    while ((element = element.parentNode) && element != document.body)
+      if (Element.getStyle(element, 'position') != 'static')
+        return element;
+
+    return document.body;
+  },
+
+  // caches x/y coordinate pair to use with overlap
+  within: function(element, x, y) {
+    if (this.includeScrollOffsets)
+      return this.withinIncludingScrolloffsets(element, x, y);
+    this.xcomp = x;
+    this.ycomp = y;
+    this.offset = this.cumulativeOffset(element);
+
+    return (y >= this.offset[1] &&
+            y <  this.offset[1] + element.offsetHeight &&
+            x >= this.offset[0] &&
+            x <  this.offset[0] + element.offsetWidth);
+  },
+
+  withinIncludingScrolloffsets: function(element, x, y) {
+    var offsetcache = this.realOffset(element);
+
+    this.xcomp = x + offsetcache[0] - this.deltaX;
+    this.ycomp = y + offsetcache[1] - this.deltaY;
+    this.offset = this.cumulativeOffset(element);
+
+    return (this.ycomp >= this.offset[1] &&
+            this.ycomp <  this.offset[1] + element.offsetHeight &&
+            this.xcomp >= this.offset[0] &&
+            this.xcomp <  this.offset[0] + element.offsetWidth);
+  },
+
+  // within must be called directly before
+  overlap: function(mode, element) {
+    if (!mode) return 0;
+    if (mode == 'vertical')
+      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
+        element.offsetHeight;
+    if (mode == 'horizontal')
+      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
+        element.offsetWidth;
+  },
+
+  page: function(forElement) {
+    var valueT = 0, valueL = 0;
+
+    var element = forElement;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+
+      // Safari fix
+      if (element.offsetParent == document.body)
+        if (Element.getStyle(element,'position')=='absolute') break;
+
+    } while (element = element.offsetParent);
+
+    element = forElement;
+    do {
+      if (!window.opera || element.tagName=='BODY') {
+        valueT -= element.scrollTop  || 0;
+        valueL -= element.scrollLeft || 0;
+      }
+    } while (element = element.parentNode);
+
+    return [valueL, valueT];
+  },
+
+  clone: function(source, target) {
+    var options = Object.extend({
+      setLeft:    true,
+      setTop:     true,
+      setWidth:   true,
+      setHeight:  true,
+      offsetTop:  0,
+      offsetLeft: 0
+    }, arguments[2] || {})
+
+    // find page position of source
+    source = $(source);
+    var p = Position.page(source);
+
+    // find coordinate system to use
+    target = $(target);
+    var delta = [0, 0];
+    var parent = null;
+    // delta [0,0] will do fine with position: fixed elements,
+    // position:absolute needs offsetParent deltas
+    if (Element.getStyle(target,'position') == 'absolute') {
+      parent = Position.offsetParent(target);
+      delta = Position.page(parent);
+    }
+
+    // correct by body offsets (fixes Safari)
+    if (parent == document.body) {
+      delta[0] -= document.body.offsetLeft;
+      delta[1] -= document.body.offsetTop;
+    }
+
+    // set position
+    if(options.setLeft)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
+    if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
+    if(options.setWidth)  target.style.width = source.offsetWidth + 'px';
+    if(options.setHeight) target.style.height = source.offsetHeight + 'px';
+  },
+
+  absolutize: function(element) {
+    element = $(element);
+    if (element.style.position == 'absolute') return;
+    Position.prepare();
+
+    var offsets = Position.positionedOffset(element);
+    var top     = offsets[1];
+    var left    = offsets[0];
+    var width   = element.clientWidth;
+    var height  = element.clientHeight;
+
+    element._originalLeft   = left - parseFloat(element.style.left  || 0);
+    element._originalTop    = top  - parseFloat(element.style.top || 0);
+    element._originalWidth  = element.style.width;
+    element._originalHeight = element.style.height;
+
+    element.style.position = 'absolute';
+    element.style.top    = top + 'px';
+    element.style.left   = left + 'px';
+    element.style.width  = width + 'px';
+    element.style.height = height + 'px';
+  },
+
+  relativize: function(element) {
+    element = $(element);
+    if (element.style.position == 'relative') return;
+    Position.prepare();
+
+    element.style.position = 'relative';
+    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
+    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
+
+    element.style.top    = top + 'px';
+    element.style.left   = left + 'px';
+    element.style.height = element._originalHeight;
+    element.style.width  = element._originalWidth;
+  }
+}
+
+// Safari returns margins on body which is incorrect if the child is absolutely
+// positioned.  For performance reasons, redefine Position.cumulativeOffset for
+// KHTML/WebKit only.
+if (Prototype.Browser.WebKit) {
+  Position.cumulativeOffset = function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      if (element.offsetParent == document.body)
+        if (Element.getStyle(element, 'position') == 'absolute') break;
+
+      element = element.offsetParent;
+    } while (element);
+
+    return [valueL, valueT];
+  }
+}
+
+Element.addMethods();
\ No newline at end of file
diff --git a/NP_TrackBack/trunk/trackback/js/rico/rico.js b/NP_TrackBack/trunk/trackback/js/rico/rico.js
new file mode 100644 (file)
index 0000000..00e187e
--- /dev/null
@@ -0,0 +1,214 @@
+/**
+  *
+  *  Copyright 2005 Sabre Airline Solutions
+  *
+  *  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.
+  **/
+
+
+// This module does NOT depend on prototype.js
+
+var Rico = {
+  Version: '2.0 beta2',
+  loadRequested: 1,
+  loadComplete: 2,
+  init : function() {
+    try {  // fix IE background image flicker (credit: www.mister-pixel.com)
+      document.execCommand("BackgroundImageCache", false, true);
+    } catch(err) {}
+    this.preloadMsgs='';
+    var elements = document.getElementsByTagName('script');
+    this.baseHref= location.protocol + "//" + location.host;
+    this.loadedFiles={};
+    this.loadQueue=[];    
+    this.windowIsLoaded=false;
+    this.onLoadCallbacks=[];
+    for (var i=0; i<elements.length; i++) {
+      if (!elements[i].src) continue;
+      var src = elements[i].src;
+      var slashIdx = src.lastIndexOf('/');
+      var path = src.substring(0, slashIdx+1);
+      var filename = src.substring(slashIdx+1);
+      this.loadedFiles[filename]=this.loadComplete;
+      if (filename == 'rico.js') {
+        this.jsDir = path;
+        this.cssDir= path+'css/';
+        this.imgDir= path+'images/';
+        this.htmDir= path;
+        this.xslDir= path;
+      }
+    }
+    if (typeof Prototype=='undefined')
+      this.include('prototype.js');
+    this.include('ricoCommon.js');
+    var func=function() { Rico.windowLoaded(); };
+    if (window.addEventListener)
+      window.addEventListener('load', func, false);
+    else if (window.attachEvent)
+      window.attachEvent('onload', func);
+    this.onLoad(function() { Rico.writeDebugMsg('Pre-load messages:\n'+Rico.preloadMsgs); });
+  },
+  
+  // Array entries can reference a javascript file or css stylesheet
+  // A dependency on another module can be indicated with a plus-sign prefix: '+DependsOnModule'
+  moduleDependencies : {
+    Accordion  : ['ricoBehaviors.js','ricoEffects.js','ricoComponents.js'],
+    Color      : ['ricoColor.js'],
+    Corner     : ['ricoStyles.js'],
+    DragAndDrop: ['ricoDragDrop.js'],
+    Effect     : ['ricoEffects.js'],
+    Calendar   : ['ricoCalendar.js', 'ricoCalendar.css'],
+    Tree       : ['ricoTree.js', 'ricoTree.css'],
+    ColorPicker: ['ricoColorPicker.js', 'ricoStyles.js'],
+    SimpleGrid : ['ricoCommon.js', 'ricoGridCommon.js', 'ricoGrid.css', 'ricoSimpleGrid.js'],
+    LiveGrid   : ['ricoCommon.js', 'ricoGridCommon.js', 'ricoGrid.css', 'ricoBehaviors.js', 'ricoLiveGrid.js'],
+    CustomMenu : ['ricoMenu.js', 'ricoMenu.css'],
+    LiveGridMenu : ['+CustomMenu', 'ricoLiveGridMenu.js'],
+    LiveGridAjax : ['+LiveGrid', 'ricoLiveGridAjax.js'],
+    LiveGridForms: ['+LiveGridAjax', '+LiveGridMenu', '+Accordion', '+Corner', 'ricoLiveGridForms.js', 'ricoLiveGridForms.css'],
+    SpreadSheet  : ['+SimpleGrid', 'ricoSheet.js']
+  },
+  
+  // not reliable when used with XSLT
+  loadModule : function(name) {
+    var dep=this.moduleDependencies[name];
+    if (!dep) return;
+    for (var i=0; i<dep.length; i++)
+      if (dep[i].substring(0,1)=='+')
+        this.loadModule(dep[i].slice(1));
+      else
+        this.include(dep[i]);
+  },
+  
+  // not reliable when used with XSLT
+  include : function(filename) {
+    if (this.loadedFiles[filename]) return;
+    this.addPreloadMsg('include: '+filename);
+    var ext = filename.substr(filename.lastIndexOf('.')+1);
+    switch (ext.toLowerCase()) {
+      case 'js':
+        this.loadQueue.push(filename);
+        this.loadedFiles[filename]=this.loadRequested;
+        this.checkLoadQueue();
+        return;
+      case 'css':
+        var el = document.createElement('link');
+        el.type = 'text/css';
+        el.rel = 'stylesheet'
+        el.href = this.cssDir+filename;
+        this.loadedFiles[filename]=this.loadComplete;
+        document.getElementsByTagName('head')[0].appendChild(el);
+        return;
+    }
+  },
+  
+  checkLoadQueue: function() {
+    if (this.loadQueue.length==0) return;
+    if (this.inProcess) return;  // seems to only be required by IE, but applied to all browsers just to be safe
+    this.addScriptToDOM(this.loadQueue.shift());
+  },
+  
+  addScriptToDOM: function(filename) {
+    this.addPreloadMsg('addScriptToDOM: '+filename);
+    var js = document.createElement('script');
+    js.type = 'text/javascript';
+    js.src = this.jsDir+filename;
+    this.loadedFiles[filename]=this.loadRequested;
+    this.inProcess=filename;
+    var head=document.getElementsByTagName('head')[0];
+    if (filename.substring(0,4)=='rico') {
+      head.appendChild(js);
+    } else if(/WebKit|Khtml/i.test(navigator.userAgent)) {
+      head.appendChild(js);
+      this.includeLoaded(filename);
+    } else {
+      js.onload = js.onreadystatechange = function() {
+        if (js.readyState && js.readyState != 'loaded' && js.readyState != 'complete') return;
+        js.onreadystatechange = js.onload = null;
+        Rico.includeLoaded(filename);
+      };
+      head.appendChild(js);
+    }
+  },
+  
+  // called after a script file has finished loading
+  includeLoaded: function(filename) {
+    this.addPreloadMsg('loaded: '+filename);
+    this.loadedFiles[filename]=this.loadComplete;
+    if (filename==this.inProcess) {
+      this.inProcess=null;
+      this.checkLoadQueue();
+      this.checkIfComplete();
+    }
+  },
+
+  // called by the document onload event
+  windowLoaded: function() {
+    this.windowIsLoaded=true;
+    this.checkIfComplete();
+  },
+  
+  checkIfComplete: function() {
+    var waitingFor=this.windowIsLoaded ? '' : 'window';
+    for(var filename in  this.loadedFiles) {
+      if (this.loadedFiles[filename]==this.loadRequested)
+        waitingFor+=' '+filename;
+    }
+    //window.status='waitingFor: '+waitingFor;
+    this.addPreloadMsg('waitingFor: '+waitingFor);
+    if (waitingFor.length==0) {
+      this.addPreloadMsg('Processing callbacks');
+      while (this.onLoadCallbacks.length > 0) {
+        var callback=this.onLoadCallbacks.pop();
+        if (callback) callback();
+      }
+    }
+  },
+  
+  onLoad: function(callback) {
+    this.onLoadCallbacks.push(callback);
+    this.checkIfComplete();
+  },
+
+  isKonqueror : navigator.userAgent.toLowerCase().indexOf("konqueror") >= 0,
+
+  // logging funtions
+   
+  startTime : new Date(),
+
+  timeStamp: function() {
+    var stamp = new Date();
+    return (stamp.getTime()-this.startTime.getTime())+": ";
+  },
+  
+  setDebugArea: function(id, forceit) {
+    if (!this.debugArea || forceit) {
+      var newarea=document.getElementById(id);
+      if (!newarea) return;
+      this.debugArea=newarea;
+      newarea.value='';
+    }
+  },
+
+  addPreloadMsg: function(msg) {
+    this.preloadMsgs+=Rico.timeStamp()+msg+"\n";
+  },
+
+  writeDebugMsg: function(msg, resetFlag) {
+    if (this.debugArea) {
+      if (resetFlag) this.debugArea.value='';
+      this.debugArea.value+=this.timeStamp()+msg+"\n";
+    }
+  }
+
+}
+
+Rico.init();
diff --git a/NP_TrackBack/trunk/trackback/js/rico/ricoAjaxEngine.js b/NP_TrackBack/trunk/trackback/js/rico/ricoAjaxEngine.js
new file mode 100644 (file)
index 0000000..5363991
--- /dev/null
@@ -0,0 +1,178 @@
+//-------------------- ricoAjaxEngine.js
+Rico.AjaxEngine = Class.create();
+
+Rico.AjaxEngine.prototype = {
+
+   initialize: function() {
+      this.ajaxElements = new Array();
+      this.ajaxObjects  = new Array();
+      this.requestURLS  = new Array();
+      this.options = {};
+   },
+
+   registerAjaxElement: function( anId, anElement ) {
+      if ( !anElement )
+         anElement = $(anId);
+      this.ajaxElements[anId] = anElement;
+   },
+
+   registerAjaxObject: function( anId, anObject ) {
+      this.ajaxObjects[anId] = anObject;
+   },
+
+   registerRequest: function (requestLogicalName, requestURL) {
+      this.requestURLS[requestLogicalName] = requestURL;
+   },
+
+   sendRequest: function(requestName, options) {
+      // Allow for backwards Compatibility
+      if ( arguments.length >= 2 )
+       if (typeof arguments[1] == 'string')
+         options = {parameters: this._createQueryString(arguments, 1)};
+      this.sendRequestWithData(requestName, null, options);
+   },
+
+   sendRequestWithData: function(requestName, xmlDocument, options) {
+      var requestURL = this.requestURLS[requestName];
+      if ( requestURL == null )
+         return;
+
+      // Allow for backwards Compatibility
+      if ( arguments.length >= 3 )
+        if (typeof arguments[2] == 'string')
+          options.parameters = this._createQueryString(arguments, 2);
+
+      new Ajax.Request(requestURL, this._requestOptions(options,xmlDocument));
+   },
+
+   sendRequestAndUpdate: function(requestName,container,options) {
+      // Allow for backwards Compatibility
+      if ( arguments.length >= 3 )
+        if (typeof arguments[2] == 'string')
+          options.parameters = this._createQueryString(arguments, 2);
+
+      this.sendRequestWithDataAndUpdate(requestName, null, container, options);
+   },
+
+   sendRequestWithDataAndUpdate: function(requestName,xmlDocument,container,options) {
+      var requestURL = this.requestURLS[requestName];
+      if ( requestURL == null )
+         return;
+
+      // Allow for backwards Compatibility
+      if ( arguments.length >= 4 )
+        if (typeof arguments[3] == 'string')
+          options.parameters = this._createQueryString(arguments, 3);
+
+      var updaterOptions = this._requestOptions(options,xmlDocument);
+
+      new Ajax.Updater(container, requestURL, updaterOptions);
+   },
+
+   // Private -- not part of intended engine API --------------------------------------------------------------------
+
+   _requestOptions: function(options,xmlDoc) {
+      var requestHeaders = ['X-Rico-Version', Rico.Version ];
+      var sendMethod = 'post';
+      if ( xmlDoc == null )
+        if (Rico.prototypeVersion < 1.4)
+        requestHeaders.push( 'Content-type', 'text/xml' );
+      else
+          sendMethod = 'get';
+      (!options) ? options = {} : '';
+
+      if (!options._RicoOptionsProcessed){
+      // Check and keep any user onComplete functions
+        if (options.onComplete)
+             options.onRicoComplete = options.onComplete;
+        // Fix onComplete
+        if (options.overrideOnComplete)
+          options.onComplete = options.overrideOnComplete;
+        else
+          options.onComplete = this._onRequestComplete.bind(this);
+        options._RicoOptionsProcessed = true;
+      }
+
+     // Set the default options and extend with any user options
+     this.options = {
+                     requestHeaders: requestHeaders,
+                     parameters:     options.parameters,
+                     postBody:       xmlDoc,
+                     method:         sendMethod,
+                     onComplete:     options.onComplete
+                    };
+     // Set any user options:
+     Object.extend(this.options, options);
+     return this.options;
+   },
+
+   _createQueryString: function( theArgs, offset ) {
+      var queryString = ""
+      for ( var i = offset ; i < theArgs.length ; i++ ) {
+          if ( i != offset )
+            queryString += "&";
+
+          var anArg = theArgs[i];
+
+          if ( anArg.name != undefined && anArg.value != undefined ) {
+            queryString += anArg.name +  "=" + escape(anArg.value);
+          }
+          else {
+             var ePos  = anArg.indexOf('=');
+             var argName  = anArg.substring( 0, ePos );
+             var argValue = anArg.substring( ePos + 1 );
+             queryString += argName + "=" + escape(argValue);
+          }
+      }
+      return queryString;
+   },
+
+   _onRequestComplete : function(request) {
+      if(!request)
+          return;
+      // User can set an onFailure option - which will be called by prototype
+      if (request.status != 200)
+        return;
+
+      var response = request.responseXML.getElementsByTagName("ajax-response");
+      if (response == null || response.length != 1)
+         return;
+      this._processAjaxResponse( response[0].childNodes );
+      
+      // Check if user has set a onComplete function
+      var onRicoComplete = this.options.onRicoComplete;
+      if (onRicoComplete != null)
+          onRicoComplete();
+   },
+
+   _processAjaxResponse: function( xmlResponseElements ) {
+      for ( var i = 0 ; i < xmlResponseElements.length ; i++ ) {
+         var responseElement = xmlResponseElements[i];
+
+         // only process nodes of type element.....
+         if ( responseElement.nodeType != 1 )
+            continue;
+
+         var responseType = responseElement.getAttribute("type");
+         var responseId   = responseElement.getAttribute("id");
+
+         if ( responseType == "object" )
+            this._processAjaxObjectUpdate( this.ajaxObjects[ responseId ], responseElement );
+         else if ( responseType == "element" )
+            this._processAjaxElementUpdate( this.ajaxElements[ responseId ], responseElement );
+         else
+            alert('unrecognized AjaxResponse type : ' + responseType );
+      }
+   },
+
+   _processAjaxObjectUpdate: function( ajaxObject, responseElement ) {
+      ajaxObject.ajaxUpdate( responseElement );
+   },
+
+   _processAjaxElementUpdate: function( ajaxElement, responseElement ) {
+      ajaxElement.innerHTML = RicoUtil.getContentAsString(responseElement);
+   }
+
+}
+
+Rico.includeLoaded('ricoAjaxEngine.js');
\ No newline at end of file
diff --git a/NP_TrackBack/trunk/trackback/js/rico/ricoBehaviors.js b/NP_TrackBack/trunk/trackback/js/rico/ricoBehaviors.js
new file mode 100644 (file)
index 0000000..ed5d572
--- /dev/null
@@ -0,0 +1,188 @@
+/**
+  *  (c) 2005-2007 Richard Cowin (http://openrico.org)
+  *
+  *  Rico is 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
+  **/
+
+
+Rico.selectionSet = function(set, options){
+  new Rico.SelectionSet(set, options)
+}
+
+Rico.SelectionSet = Class.create(); 
+Rico.SelectionSet.prototype = {
+       initialize: function(selectionSet, options){
+               this.options = options || {}
+    if (typeof selectionSet == 'string')
+      selectionSet = $$(selectionSet)
+         this.previouslySelected = [];
+               this.selectionSet = selectionSet;
+               this.selectedClassName = this.options.selectedClass || "selected";
+               this.selectNode = this.options.selectNode || function(e){return e};
+               this.onSelect = this.options.onSelect;
+    this.onFirstSelect = this.options.onFirstSelect;
+               this.clickHandler = this.click.bind(this);
+               selectionSet.each(function(e) {Event.observe(e, "click", new Rico.EventWrapper(this.clickHandler,e).wrapper);}.bind(this))
+    if (!this.options.noDefault)
+                 this.selectIndex(this.options.selectedIndex || 0)
+       },
+       reset: function(){
+         this.previouslySelected = [];
+         this.notifySelected(this.selected);
+       },
+       select: function(element){
+               if (this.selected == element)
+                       return;
+
+               if (this.selected)
+                 new Element.ClassNames(this.selectNode(this.selected)).remove(this.selectedClassName)
+    
+    this.notifySelected(element)
+
+               this.selected = element;
+               new Element.ClassNames(this.selectNode(this.selected)).add(this.selectedClassName)
+       },
+       notifySelected: function(element){
+    var index = this.selectionSet.indexOf(element)
+    if (this.onFirstSelect && !this.previouslySelected[index]){
+      this.onFirstSelect(element, index)
+      this.previouslySelected[index] = true;
+    }
+       if (this.onSelect)
+      try{
+           this.onSelect(element, index)
+      } catch (e) {}
+       },
+       selectIndex: function(index){
+               this.select(this.selectionSet[index])
+       },  
+       nextSelectItem: function(index){
+    var index = this.selectionSet.indexOf(this.selected)
+    if (index + 1 >= this.selectionSet.length)
+      return this.selectionSet[index - 1];
+    else
+      return this.selectionSet[index + 1];       
+       },
+       selectNext: function(){
+    var index = this.selectionSet.indexOf(this.selected)
+    if (index >= this.selectionSet.length)
+      this.selectIndex(index - 1)
+    else
+      this.selectIndex(index + 1)
+       },
+       click: function(event,target) {
+               this.select(target);
+       },
+       add: function(item){
+       //      this.selectionSet.push(item)
+         if (item.constructur == Array)
+           item.each(function(e){
+               Event.observe(e, "click", new Rico.EventWrapper(this.clickHandler,item).wrapper);
+           }.bind(this))
+         else
+                 Event.observe(item, "click", new Rico.EventWrapper(this.clickHandler,item).wrapper);
+       },
+       remove: function(item){
+         this.selectionSet = this.selectionSet.without(item)
+                       //Todo: need to cleanup all events on item - need to keep track of eventwrappers
+       },
+       removeAll: function(){
+               
+       }
+ }
+
+Rico.HoverSet = Class.create();
+Rico.HoverSet.prototype = {
+    initialize: function(hoverSet, options){
+      options = options || [];
+      this.hoverSet = hoverSet;
+      this.hoverClassName = options.hoverClass || "hover";
+      this.hoverNodes = options.hoverNodes || function(e){return [e]};
+               this.listenerHover    = this._onHover.bind(this)
+      this.listenerEndHover = this._onUnHover.bind(this)
+      
+         this.hoverSet.each((function(e) {Event.observe(e, "mousemove", new Rico.EventWrapper(this.listenerHover,e).wrapper);}).bind(this))
+         this.hoverSet.each((function(e) {Event.observe(e, "mouseout", new Rico.EventWrapper(this.listenerEndHover,e).wrapper);}).bind(this))  
+       },
+       _onHover: function(event,target) {
+         this.hover(target);
+       },      
+       _onUnHover: function(event,target) {
+         this.unHover(target);
+       },
+       hover: function(target) {
+         this.hoverNodes(target).each((function(t){Element.classNames(t).add(this.hoverClassName)}).bind(this));
+       },      
+       unHover: function(target) {
+         this.hoverNodes(target).each((function(t){Element.classNames(t).remove(this.hoverClassName)}).bind(this));
+       },
+               add: function(item){
+         Event.observe(item, "mousemove", new Rico.EventWrapper(this.listenerHover,item).wrapper);
+         Event.observe(item, "mouseout", new Rico.EventWrapper(this.listenerEndHover,item).wrapper);
+               },
+               remove: function(item){
+                       //Todo: need to cleanup all events on item - need to keep terack of eventwrappers
+                       //stopObserving
+                       //Event.stopObserving(e, "mousemove", new Rico.EventWrapper(this.listenerHover,e).wrapper);}).bind(this))
+         //this.hoverSet.each((function(e) {Event.observe(e, "mouseout", new Rico.EventWrapper(this.listenerEndHover,e).wrapper);}).bind(this))
+                       //hoverSet
+               },
+               removeAll: function(item){
+               }
+}
+
+Rico.Hover = {
+  groups: {},
+  clearCurrent: function(group) {
+    var last_hover = Rico.Hover.groups[group];
+    if(!last_hover) return  
+    clearTimeout(last_hover[0])
+    last_hover[1].end()
+    Rico.Hover.groups[group] = null;
+  }, 
+  end: function(group) {
+       Rico.Hover.groups[group][1].end();
+  },
+  endWith: function(hover, group) {
+       var timer = setTimeout('Rico.Hover.end("'+ group + '")', hover.exitDelay)
+    Rico.Hover.groups[group] = [timer, hover]
+  }
+}
+
+Rico.HoverDisplay = Class.create();
+Rico.HoverDisplay.prototype = {
+  initialize: function(element, options) {
+       this.element = element;
+       this.options = options || {};
+       this.group = this.options.group;
+       this.exitDelay = this.options.delay || 1000;
+  },
+  begin: function() {
+    Rico.Hover.clearCurrent(this.group)
+               Element.show(this.element)
+  },
+  end: function(delay) {
+    if(delay)
+               Rico.Hover.endWith(this, this.group);
+    else 
+                 Element.hide(this.element)              
+  }
+}
+
+
+Rico.EventWrapper = Class.create();
+Rico.EventWrapper.prototype = {
+  initialize: function(handler, target){
+    this.handler = handler;
+    this.target = target;
+    this.wrapper = this.wrapperCall.bindAsEventListener(this)
+  },
+  wrapperCall: function(event){
+    this.handler(event, this.target)
+  }
+}
+
+Rico.includeLoaded('ricoBehaviors.js');
diff --git a/NP_TrackBack/trunk/trackback/js/rico/ricoCalendar.js b/NP_TrackBack/trunk/trackback/js/rico/ricoCalendar.js
new file mode 100644 (file)
index 0000000..8aa596c
--- /dev/null
@@ -0,0 +1,443 @@
+//  By Matt Brown\r
+//  June-October 2006\r
+//  email: dowdybrown@yahoo.com\r
+//  Implements a pop-up Gregorian calendar.\r
+//  Dates of adoption of the Gregorian calendar vary by country - accurate as a US & British calendar from 14 Sept 1752 to present.\r
+//  Mark special dates with calls to addHoliday()\r
+//  Inspired by code originally written by Tan Ling Wee on 2 Dec 2001\r
+\r
+//  Requires prototype.js and ricoCommon.js\r
+\r
+Rico.CalendarControl = Class.create();\r
+\r
+Rico.CalendarControl.prototype = {\r
+\r
+  initialize: function(id,options) {\r
+    this.id=id;\r
+    var today=new Date();\r
+    Object.extend(this, new Rico.Popup({ignoreClicks:true}));\r
+    Object.extend(this.options, {\r
+      startAt : 0,           // week starts with 0=sunday, 1=monday\r
+      showWeekNumber : 0,    // show week number in first column?\r
+      showToday : 1,         // show "Today is..." in footer?\r
+      cursorColor: '#FDD',   // color used to highlight dates as the user moves their mouse\r
+      repeatInterval : 100,  // when left/right arrow is pressed, repeat action every x milliseconds\r
+      dateFmt : 'ISO8601',   // default is ISO-8601, 'rico'=use format stored in ricoTranslate object\r
+      selectedDateBorder : "#666666",  // border to indicate currently selected date\r
+      minDate : new Date(today.getFullYear()-50,0,1),  // default to +-50 yrs from current date\r
+      maxDate : new Date(today.getFullYear()+50,11,31)\r
+    });\r
+    Object.extend(this.options, options || {});\r
+    this.close=this.closePopup;\r
+    this.bPageLoaded=false;\r
+    this.img=new Array();\r
+    this.Holidays={};\r
+    this.todayString=RicoTranslate.getPhrase("Today is ");\r
+    this.weekString=RicoTranslate.getPhrase("Wk");\r
+    if (this.options.dateFmt=='rico') this.options.dateFmt=RicoTranslate.dateFmt;\r
+    this.dateParts=new Array();\r
+    this.re=/^\s*(\w+)(\W)(\w+)(\W)(\w+)/i;\r
+    if (this.re.exec(this.options.dateFmt)) {\r
+      this.dateParts[RegExp.$1]=0;\r
+      this.dateParts[RegExp.$3]=1;\r
+      this.dateParts[RegExp.$5]=2;\r
+    }\r
+  },\r
+\r
+\r
+  // y=0 implies a repeating holiday\r
+  addHoliday : function(d, m, y, desc, bgColor, txtColor) {\r
+    this.Holidays[this.holidayKey(y,m-1,d)]={desc:desc, txtColor:txtColor, bgColor:bgColor || '#DDF'};\r
+  },\r
+  \r
+  holidayKey : function(y,m,d) {\r
+    return 'h'+y.toPaddedString(4)+m.toPaddedString(2)+d.toPaddedString(2);\r
+  },\r
+\r
+  atLoad : function() {\r
+    this.container=document.createElement("div");\r
+    this.container.style.display="none"\r
+    this.container.id=this.id;\r
+    this.container.className='ricoCalContainer';\r
+\r
+    this.maintab=document.createElement("table");\r
+    this.maintab.cellSpacing=0;\r
+    this.maintab.cellPadding=0;\r
+    this.maintab.border=0;\r
+    this.maintab.className='ricoCalTab';\r
+\r
+    for (var i=0; i<7; i++) {\r
+      var r=this.maintab.insertRow(-1);\r
+      r.className='row'+i;\r
+      for (var c=0; c<8; c++)\r
+        r.insertCell(-1);\r
+    }\r
+    this.tbody=this.maintab.tBodies[0];\r
+    var r=this.tbody.rows[0];\r
+    r.className='ricoCalDayNames';\r
+    if (this.options.showWeekNumber) {\r
+      r.cells[0].innerHTML=this.weekString;\r
+      for (var i=0; i<7; i++)\r
+        this.tbody.rows[i].cells[0].className='ricoCalWeekNum';\r
+    }\r
+    this.styles=[];\r
+    for (var i=0; i<7; i++) {\r
+      var dow=(i+this.options.startAt) % 7;\r
+      r.cells[i+1].innerHTML=RicoTranslate.dayNames[dow].substring(0,3);\r
+      this.styles[i+1]='ricoCal'+dow;\r
+    }\r
+    \r
+    // table header (navigation controls)\r
+    this.thead=this.maintab.createTHead()\r
+    var r=this.thead.insertRow(-1);\r
+    var c=r.insertCell(-1);\r
+    c.colSpan=8;\r
+    var img=this.createNavArrow('decMonth','left');\r
+    c.appendChild(document.createElement("a")).appendChild(img);\r
+    this.titleMonth=document.createElement("a");\r
+    c.appendChild(this.titleMonth);\r
+    Event.observe(this.titleMonth,"click", this.popUpMonth.bindAsEventListener(this), false);\r
+    var img=this.createNavArrow('incMonth','right');\r
+    c.appendChild(document.createElement("a")).appendChild(img);\r
+    var s=document.createElement("span");\r
+    s.innerHTML='&nbsp;';\r
+    s.style.paddingLeft='3em';\r
+    c.appendChild(s);\r
+\r
+    var img=this.createNavArrow('decYear','left');\r
+    c.appendChild(document.createElement("a")).appendChild(img);\r
+    this.titleYear=document.createElement("a");\r
+    Event.observe(this.titleYear,"click", this.popUpYear.bindAsEventListener(this), false);\r
+    c.appendChild(this.titleYear);\r
+    var img=this.createNavArrow('incYear','right');\r
+    c.appendChild(document.createElement("a")).appendChild(img);\r
+\r
+    // table footer (today)\r
+    if (this.options.showToday) {\r
+      this.tfoot=this.maintab.createTFoot()\r
+      var r=this.tfoot.insertRow(-1);\r
+      this.todayCell=r.insertCell(-1);\r
+      this.todayCell.colSpan=8;\r
+      Event.observe(this.todayCell,"click", this.selectNow.bindAsEventListener(this), false);\r
+    }\r
+    \r
+\r
+    this.container.appendChild(this.maintab);\r
+    \r
+    // close icon (upper right)\r
+    var img=document.createElement("img");\r
+    img.src=Rico.imgDir+'close.gif';\r
+    img.onclick=this.close.bind(this);\r
+    img.style.cursor='pointer';\r
+    img.style.position='absolute';\r
+    img.style.top='1px';   /* assumes a 1px border */\r
+    img.style.right='1px';\r
+    this.container.appendChild(img);\r
+    \r
+    // month selector\r
+    this.monthSelect=document.createElement("table");\r
+    this.monthSelect.className='ricoCalMenu';\r
+    this.monthSelect.cellPadding=2;\r
+    this.monthSelect.cellSpacing=0;\r
+    this.monthSelect.border=0;\r
+    for (var i=0; i<4; i++) {\r
+      var r=this.monthSelect.insertRow(-1);\r
+      for (var j=0; j<3; j++) {\r
+        var c=r.insertCell(-1);\r
+        var a=document.createElement("a");\r
+        a.innerHTML=RicoTranslate.monthNames[i*3+j].substring(0,3);\r
+        a.name=i*3+j;\r
+        c.appendChild(a);\r
+        Event.observe(a,"click", this.selectMonth.bindAsEventListener(this), false);\r
+      }\r
+    }\r
+    this.monthSelect.style.display='none';\r
+    this.container.appendChild(this.monthSelect);\r
+    \r
+    // fix anchors so they work in IE6\r
+    var a=this.container.getElementsByTagName('a');\r
+    for (var i=0; i<a.length; i++)\r
+      a[i].href='#';\r
+    \r
+    Event.observe(this.tbody,"click", this.saveAndClose.bindAsEventListener(this));\r
+    Event.observe(this.tbody,"mouseover", this.mouseOver.bindAsEventListener(this));
+    Event.observe(this.tbody,"mouseout",  this.mouseOut.bindAsEventListener(this));
+    document.getElementsByTagName("body")[0].appendChild(this.container);\r
+    this.setDiv(this.container);\r
+    this.close()\r
+    this.bPageLoaded=true\r
+  },\r
+  \r
+  selectNow : function() {\r
+    this.monthSelected=this.monthNow;\r
+    this.yearSelected=this.yearNow;\r
+    this.constructCalendar();\r
+  },\r
+  \r
+  createNavArrow: function(funcname,gifname) {\r
+    var img=document.createElement("img");\r
+    img.src=Rico.imgDir+gifname+'.gif';\r
+    img.name=funcname;\r
+    Event.observe(img,"click", this[funcname].bindAsEventListener(this), false);\r
+    Event.observe(img,"mousedown", this.mouseDown.bindAsEventListener(this), false);\r
+    Event.observe(img,"mouseup", this.mouseUp.bindAsEventListener(this), false);\r
+    Event.observe(img,"mouseout", this.mouseUp.bindAsEventListener(this), false);\r
+    return img\r
+  },\r
+\r
+  mouseOver: function(e) {\r
+    var el=Event.element(e);\r
+    if (this.lastHighlight==el) return;\r
+    this.unhighlight();\r
+    var s=el.innerHTML.replace(/&nbsp;/g,'');\r
+    if (s=='' || el.className=='ricoCalWeekNum') return;\r
+    var day=parseInt(s);\r
+    if (isNaN(day)) return;\r
+    this.lastHighlight=el;\r
+    this.tmpColor=el.style.backgroundColor;\r
+    el.style.backgroundColor=this.options.cursorColor;\r
+  },\r
+  \r
+  unhighlight: function() {\r
+    if (!this.lastHighlight) return;\r
+    this.lastHighlight.style.backgroundColor=this.tmpColor;\r
+    this.lastHighlight=null;\r
+  },\r
+  \r
+  mouseOut: function(e) {\r
+    var el=Event.element(e);\r
+    if (el==this.lastHighlight) this.unhighlight();\r
+  },\r
+  \r
+  mouseDown: function(e) {\r
+    var el=Event.element(e);\r
+    this.repeatFunc=this[el.name].bind(this);\r
+    this.timeoutID=setTimeout(this.repeatStart.bind(this),500);\r
+  },\r
+  \r
+  mouseUp: function(e) {\r
+    clearTimeout(this.timeoutID);\r
+    clearInterval(this.intervalID)\r
+  },\r
+  \r
+  repeatStart : function() {\r
+    clearInterval(this.intervalID);\r
+    this.intervalID=setInterval(this.repeatFunc,this.options.repeatInterval);\r
+  },\r
+  \r
+  // is yr/mo within minDate/MaxDate?\r
+  isValidMonth : function(yr,mo) {\r
+    if (yr < this.options.minDate.getFullYear()) return false;\r
+    if (yr == this.options.minDate.getFullYear() && mo < this.options.minDate.getMonth()) return false;\r
+    if (yr > this.options.maxDate.getFullYear()) return false;\r
+    if (yr == this.options.maxDate.getFullYear() && mo > this.options.maxDate.getMonth()) return false;\r
+    return true;\r
+  },\r
+\r
+  incMonth : function() {\r
+    var newMonth=this.monthSelected+1;\r
+    var newYear=this.yearSelected;\r
+    if (newMonth>11) {\r
+      newMonth=0;\r
+      newYear++;\r
+    }\r
+    if (!this.isValidMonth(newYear,newMonth)) return;\r
+    this.monthSelected=newMonth;\r
+    this.yearSelected=newYear;\r
+    this.constructCalendar()\r
+  },\r
+\r
+  decMonth : function() {\r
+    var newMonth=this.monthSelected-1;\r
+    var newYear=this.yearSelected;\r
+    if (newMonth<0) {\r
+      newMonth=11;\r
+      newYear--;\r
+    }\r
+    if (!this.isValidMonth(newYear,newMonth)) return;\r
+    this.monthSelected=newMonth;\r
+    this.yearSelected=newYear;\r
+    this.constructCalendar()\r
+  },\r
+  \r
+  selectMonth : function(e) {\r
+    var el=Event.element(e);\r
+    this.monthSelected=parseInt(el.name);\r
+    this.constructCalendar();\r
+    Event.stop(e);\r
+  },\r
+\r
+  popUpMonth : function() {\r
+    this.monthSelect.style.display=this.monthSelect.style.display=='none' ? 'block' : 'none';\r
+  },\r
+\r
+  popDownMonth : function() {\r
+    this.monthSelect.style.display='none';\r
+  },\r
+\r
+  /*** Year Pulldown ***/\r
+\r
+  popUpYear : function() {\r
+    var newYear=prompt(RicoTranslate.getPhrase("Year ("+this.options.minDate.getFullYear()+"-"+this.options.maxDate.getFullYear()+")"),this.yearSelected);\r
+    if (newYear==null) return;\r
+    newYear=parseInt(newYear);\r
+    if (isNaN(newYear) || newYear<this.options.minDate.getFullYear() || newYear>this.options.maxDate.getFullYear()) {\r
+      alert(RicoTranslate.getPhrase("Invalid year"));\r
+    } else {\r
+      this.yearSelected=newYear;\r
+      this.constructCalendar();\r
+    }\r
+  },\r
+  \r
+  incYear : function() {\r
+    if (this.yearSelected>=this.options.maxDate.getFullYear()) return;\r
+    this.yearSelected++;\r
+    this.constructCalendar();\r
+  },\r
+\r
+  decYear : function() {\r
+    if (this.yearSelected<=this.options.minDate.getFullYear()) return;\r
+    this.yearSelected--;\r
+    this.constructCalendar();\r
+  },\r
+\r
+  // tried a number of different week number functions posted on the net\r
+  // this is the only one that produced consistent results when comparing week numbers for December and the following January\r
+  WeekNbr : function(year,month,day) {\r
+    var when = new Date(year,month,day);\r
+    var newYear = new Date(year,0,1);\r
+    var offset = 7 + 1 - newYear.getDay();\r
+    if (offset == 8) offset = 1;\r
+    var daynum = ((Date.UTC(year,when.getMonth(),when.getDate(),0,0,0) - Date.UTC(year,0,1,0,0,0)) /1000/60/60/24) + 1;\r
+    var weeknum = Math.floor((daynum-offset+7)/7);\r
+    if (weeknum == 0) {\r
+        year--;\r
+        var prevNewYear = new Date(year,0,1);\r
+        var prevOffset = 7 + 1 - prevNewYear.getDay();\r
+        if (prevOffset == 2 || prevOffset == 8) weeknum = 53; else weeknum = 52;\r
+    }\r
+    return weeknum;\r
+  },\r
+\r
+  constructCalendar : function() {\r
+    var aNumDays = Array (31,0,31,30,31,30,31,31,30,31,30,31)\r
+    var startDate = new Date (this.yearSelected,this.monthSelected,1)\r
+    var endDate,numDaysInMonth\r
+\r
+    if (typeof this.monthSelected!='number' || this.monthSelected>=12 || this.monthSelected<0) {\r
+      alert('ERROR in calendar: monthSelected='+this.monthSelected);\r
+      return;\r
+    }\r
+    var today = new Date();\r
+    this.dateNow  = today.getDate();\r
+    this.monthNow = today.getMonth();\r
+    this.yearNow  = today.getFullYear();\r
+\r
+    if (this.monthSelected==1) {\r
+      endDate = new Date (this.yearSelected,this.monthSelected+1,1);\r
+      endDate = new Date (endDate - (24*60*60*1000));\r
+      numDaysInMonth = endDate.getDate()\r
+    } else {\r
+      numDaysInMonth = aNumDays[this.monthSelected];\r
+    }\r
+    var dayPointer = startDate.getDay() - this.options.startAt\r
+    if (dayPointer<0) dayPointer+=7;\r
+    this.popDownMonth();\r
+\r
+    this.bgcolor=Element.getStyle(this.tbody,'background-color');\r
+    this.bgcolor=this.bgcolor.replace(/\"/g,'');\r
+    if (this.options.showWeekNumber) {\r
+      for (var i=1; i<7; i++)\r
+        this.tbody.rows[i].cells[0].innerHTML='&nbsp;';\r
+    }\r
+    for ( var i=1; i<=dayPointer; i++ )\r
+      this.resetCell(this.tbody.rows[1].cells[i]);\r
+\r
+    for ( var datePointer=1,r=1; datePointer<=numDaysInMonth; datePointer++,dayPointer++ ) {\r
+      var colnum=dayPointer % 7 + 1;\r
+      if (this.options.showWeekNumber==1 && colnum==1)\r
+        this.tbody.rows[r].cells[0].innerHTML=this.WeekNbr(this.yearSelected,this.monthSelected,datePointer);\r
+      var dateClass=this.styles[colnum];\r
+      if ((datePointer==this.dateNow)&&(this.monthSelected==this.monthNow)&&(this.yearSelected==this.yearNow))\r
+        dateClass='ricoCalToday';\r
+      var c=this.tbody.rows[r].cells[colnum];\r
+      c.innerHTML="&nbsp;" + datePointer + "&nbsp;";\r
+      c.className=dateClass;\r
+      var bordercolor=(datePointer==this.odateSelected) && (this.monthSelected==this.omonthSelected) && (this.yearSelected==this.oyearSelected) ? this.options.selectedDateBorder : this.bgcolor;\r
+      c.style.border='1px solid '+bordercolor;\r
+      var h=this.Holidays[this.holidayKey(this.yearSelected,this.monthSelected,datePointer)];\r
+      if (!h)  h=this.Holidays[this.holidayKey(0,this.monthSelected,datePointer)];\r
+      c.style.color=h ? h.txtColor : '';\r
+      c.style.backgroundColor=h ? h.bgColor : '';\r
+      c.title=h ? h.desc : '';\r
+      if (colnum==7) r++;\r
+    }\r
+    while (dayPointer<42) {\r
+      var colnum=dayPointer % 7 + 1;\r
+      this.resetCell(this.tbody.rows[r].cells[colnum]);\r
+      dayPointer++;\r
+      if (colnum==7) r++;\r
+    }\r
+\r
+    this.titleMonth.innerHTML = RicoTranslate.monthNames[this.monthSelected].substring(0,3);\r
+    this.titleYear.innerHTML = this.yearSelected;\r
+    if (this.options.showToday)\r
+      this.todayCell.innerHTML=this.todayString+'<span>'+this.dateNow + " " + RicoTranslate.monthNames[this.monthNow].substring(0,3) + " " + this.yearNow+'</span>';\r
+    this.monthSelect.style.top=this.thead.offsetHeight+'px';\r
+    this.monthSelect.style.left=this.titleMonth.offsetLeft+'px';\r
+  },\r
+  \r
+  resetCell: function(c) {\r
+    c.innerHTML="&nbsp;";\r
+    c.className='ricoCalEmpty';\r
+    c.style.border='1px solid '+this.bgcolor;\r
+    c.style.color='';\r
+    c.style.backgroundColor='';\r
+    c.title='';\r
+  },\r
+  \r
+  saveAndClose : function(e) {\r
+    Event.stop(e);\r
+    var el=Event.element(e);\r
+    var s=el.innerHTML.replace(/&nbsp;/g,'');\r
+    if (s=='' || el.className=='ricoCalWeekNum') return;\r
+    var day=parseInt(s);\r
+    if (isNaN(day)) return;\r
+    var d=new Date(this.yearSelected,this.monthSelected,day);\r
+    var dateStr=d.formatDate(this.options.dateFmt=='ISO8601' ? 'yyyy-mm-dd' : this.options.dateFmt);\r
+    if (this.returnValue) this.returnValue(dateStr);\r
+    this.close();\r
+  },\r
+\r
+  open : function(curval) {\r
+    if (!this.bPageLoaded) return;\r
+    if (typeof curval=='object') {\r
+      this.dateSelected  = curval.getDate();\r
+      this.monthSelected = curval.getMonth();\r
+      this.yearSelected  = curval.getFullYear();\r
+    } else if (this.options.dateFmt=='ISO8601') {\r
+      var d=new Date;\r
+      d.setISO8601(curval);\r
+      this.dateSelected  = d.getDate();\r
+      this.monthSelected = d.getMonth();\r
+      this.yearSelected  = d.getFullYear();\r
+    } else if (this.re.exec(curval)) {\r
+      var aDate=new Array(RegExp.$1,RegExp.$3,RegExp.$5);\r
+      this.dateSelected  = parseInt(aDate[this.dateParts['dd']], 10);\r
+      this.monthSelected = parseInt(aDate[this.dateParts['mm']], 10) - 1;\r
+      this.yearSelected  = parseInt(aDate[this.dateParts['yyyy']], 10);\r
+    } else {\r
+      if (curval) alert('ERROR: invalid date passed to calendar ('+curval+')');\r
+      this.dateSelected  = this.dateNow\r
+      this.monthSelected = this.monthNow\r
+      this.yearSelected  = this.yearNow\r
+    }\r
+    this.odateSelected=this.dateSelected\r
+    this.omonthSelected=this.monthSelected\r
+    this.oyearSelected=this.yearSelected\r
+    this.constructCalendar();\r
+    this.openPopup();\r
+  }\r
+}\r
+\r
+Rico.includeLoaded('ricoCalendar.js');
diff --git a/NP_TrackBack/trunk/trackback/js/rico/ricoColor.js b/NP_TrackBack/trunk/trackback/js/rico/ricoColor.js
new file mode 100644 (file)
index 0000000..84c3b98
--- /dev/null
@@ -0,0 +1,29 @@
+function attachValueChangeListeners() {
+   $('red').onkeypress   = colorChangedDeffered;
+   $('green').onkeypress = colorChangedDeffered;
+   $('blue').onkeypress  = colorChangedDeffered;
+}
+
+function colorChangedDeffered() {
+  setTimeout( colorChanged, 1 );
+}
+
+function colorChanged() {
+   var red   = Math.min( parseInt($('red').value)   || 0, 255);
+   var green = Math.min( parseInt($('green').value) || 0, 255);
+   var blue  = Math.min( parseInt($('blue').value)  || 0, 255);
+
+   var color = new Rico.Color( red, green, blue );
+
+   var newIllustrateString = "&nbsp;var color = new Rico.Color( ";
+   newIllustrateString += red + ", ";
+   newIllustrateString += green + ", ";
+   newIllustrateString += blue + " ); // color.asHex() = ";
+   newIllustrateString += color.asHex();
+
+   $('rgbCode').innerHTML = newIllustrateString;
+   $('colorBox').style.backgroundColor = color.asHex();
+   //$('colorBox').innerHTML = color.asHex();
+}
+
+Rico.includeLoaded('ricoColor.js');
diff --git a/NP_TrackBack/trunk/trackback/js/rico/ricoColorPicker.js b/NP_TrackBack/trunk/trackback/js/rico/ricoColorPicker.js
new file mode 100644 (file)
index 0000000..86ab657
--- /dev/null
@@ -0,0 +1,75 @@
+// ===================================================================\r
+// Original author: Matt Kruse <matt@mattkruse.com>\r
+// WWW: http://www.mattkruse.com/\r
+//\r
+// Adapted to Rico by Matt Brown\r
+// ===================================================================\r
+\r
+\r
+Rico.ColorPicker = Class.create();\r
+\r
+Rico.ColorPicker.prototype = {\r
+\r
+  initialize: function(id,options) {\r
+    this.id=id;\r
+    this.currentValue = "#FFFFFF";\r
+    Object.extend(this, new Rico.Popup());
+    Object.extend(this.options, {\r
+      showColorCode : false,\r
+      cellsPerRow   : 18,\r
+      palette       : []\r
+    });
+    var hexvals=['00','33','66','99','CC','FF'];\r
+    for (var g=0; g<hexvals.length; g++)\r
+      for (var r=0; r<hexvals.length; r++)\r
+        for (var b=0; b<hexvals.length; b++)\r
+          this.options.palette.push(hexvals[r]+hexvals[g]+hexvals[b]);\r
+    Object.extend(this.options, options || {});\r
+  },\r
+\r
+  atLoad : function() {\r
+    this.container=document.createElement("div");\r
+    this.container.style.display="none"\r
+    this.container.className='ricoColorPicker';\r
+    var width = this.options.cellsPerRow;\r
+    var cp_contents = "<TABLE BORDER='1' CELLSPACING='1' CELLPADDING='0'>";\r
+    for (var i=0; i<this.options.palette.length; i++) {\r
+      if ((i % width) == 0) { cp_contents += "<TR>"; }\r
+      cp_contents += '<TD BGCOLOR="'+this.options.palette[i]+'">&nbsp;</TD>';\r
+      if ( ((i+1)>=this.options.palette.length) || (((i+1) % width) == 0))\r
+        cp_contents += "</TR>";\r
+    }\r
+    var halfwidth = Math.floor(width/2);\r
+    if (this.options.showColorCode)\r
+      cp_contents += "<TR><TD COLSPAN='"+halfwidth+"' ID='colorPickerSelectedColor'>&nbsp;</TD><TD COLSPAN='"+(width-halfwidth)+"' ALIGN='CENTER' ID='colorPickerSelectedColorValue'>#FFFFFF</TD></TR>";\r
+    else\r
+      cp_contents += "<TR><TD COLSPAN='"+width+"' ID='colorPickerSelectedColor'>&nbsp;</TD></TR>";\r
+    cp_contents += "</TABLE>";\r
+    this.container.innerHTML=cp_contents;\r
+    document.body.appendChild(this.container);\r
+    this.setDiv(this.container);\r
+    this.open=this.openPopup;\r
+    this.close=this.closePopup;\r
+    Event.observe(this.container,"mouseover", this.highlightColor.bindAsEventListener(this), false);\r
+    Event.observe(this.container,"click", this.selectColor.bindAsEventListener(this), false);\r
+    this.close();\r
+  },\r
+\r
+  selectColor: function(e) {\r
+    if (this.returnValue) this.returnValue(this.currentValue);\r
+    this.close();\r
+  },\r
+\r
+  // This function runs when you move your mouse over a color block, if you have a newer browser\r
+  highlightColor: function(e) {\r
+    var elem = Event.element(e);\r
+    if (!elem.tagName || elem.tagName.toLowerCase() != 'td') return;\r
+    var c=Rico.Color.createColorFromBackground(elem).toString();\r
+    this.currentValue = c;\r
+    Element.setStyle('colorPickerSelectedColor',{'background-color':c});\r
+    d = $("colorPickerSelectedColorValue");\r
+    if (d) d.innerHTML = c;\r
+  }\r
+}\r
+\r
+Rico.includeLoaded('ricoColorPicker.js');
diff --git a/NP_TrackBack/trunk/trackback/js/rico/ricoCommon.js b/NP_TrackBack/trunk/trackback/js/rico/ricoCommon.js
new file mode 100644 (file)
index 0000000..0e6f4ea
--- /dev/null
@@ -0,0 +1,739 @@
+/**
+  *
+  *  Copyright 2005 Sabre Airline Solutions
+  *
+  *  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.
+  **/
+
+if (typeof Rico=='undefined')
+  throw("Cannot find the Rico object");
+if (typeof Prototype=='undefined')
+  throw("Rico requires the Prototype JavaScript framework");
+Rico.prototypeVersion = parseFloat(Prototype.Version.split(".")[0] + "." + Prototype.Version.split(".")[1]);
+if (Rico.prototypeVersion < 1.3)
+  throw("Rico requires Prototype JavaScript framework version 1.3 or greater");
+
+/** @singleton */
+var RicoUtil = {
+
+   getDirectChildrenByTag: function(e, tagName) {
+      var kids = new Array();
+      var allKids = e.childNodes;
+      tagName=tagName.toLowerCase();
+      for( var i = 0 ; i < allKids.length ; i++ )
+         if ( allKids[i] && allKids[i].tagName && allKids[i].tagName.toLowerCase() == tagName )
+            kids.push(allKids[i]);
+      return kids;
+   },
+
+   createXmlDocument : function() {
+      if (document.implementation && document.implementation.createDocument) {
+         var doc = document.implementation.createDocument("", "", null);
+
+         if (doc.readyState == null) {
+            doc.readyState = 1;
+            doc.addEventListener("load", function () {
+               doc.readyState = 4;
+               if (typeof doc.onreadystatechange == "function")
+                  doc.onreadystatechange();
+            }, false);
+         }
+
+         return doc;
+      }
+
+      if (window.ActiveXObject)
+          return Try.these(
+            function() { return new ActiveXObject('MSXML2.DomDocument')   },
+            function() { return new ActiveXObject('Microsoft.DomDocument')},
+            function() { return new ActiveXObject('MSXML.DomDocument')    },
+            function() { return new ActiveXObject('MSXML3.DomDocument')   }
+          ) || false;
+
+      return null;
+   },
+
+   getInnerText: function(el) {
+     if (typeof el == "string") return el;
+     if (typeof el == "undefined") { return el };
+     var cs = el.childNodes;
+     var l = cs.length;
+     var str = "";
+     for (var i = 0; i < l; i++) {
+       switch (cs[i].nodeType) {
+         case 1: //ELEMENT_NODE
+           if (Element.getStyle(cs[i],'display')=='none') continue;
+           switch (cs[i].tagName.toLowerCase()) {
+             case 'img':   str += cs[i].alt || cs[i].title || cs[i].src; break;
+             case 'input': if (cs[i].type=='hidden') continue;
+             case 'select':
+             case 'textarea': str += $F(cs[i]) || ''; break;
+             default:      str += this.getInnerText(cs[i]); break;
+           }
+           break;
+         case 3: //TEXT_NODE
+           str += cs[i].nodeValue;
+           break;
+       }
+     }
+     return str;
+   },
+
+   // For Konqueror 3.5, isEncoded must be true
+   getContentAsString: function( parentNode, isEncoded ) {
+      if (isEncoded) return this._getEncodedContent(parentNode);
+      if (typeof parentNode.xml != 'undefined') return this._getContentAsStringIE(parentNode);
+      return this._getContentAsStringMozilla(parentNode);
+   },
+
+   _getEncodedContent: function(parentNode) {
+      if (parentNode.innerHTML) return parentNode.innerHTML;
+      switch (parentNode.childNodes.length) {
+        case 0:  return "";
+        case 1:  return parentNode.firstChild.nodeValue;
+        default: return parentNode.childNodes[1].nodeValue;
+      }
+   },
+
+  _getContentAsStringIE: function(parentNode) {
+     var contentStr = "";
+     for ( var i = 0 ; i < parentNode.childNodes.length ; i++ ) {
+         var n = parentNode.childNodes[i];
+         if (n.nodeType == 4) {
+             contentStr += n.nodeValue;
+         }
+         else {
+           contentStr += n.xml;
+       }
+     }
+     return contentStr;
+  },
+
+  _getContentAsStringMozilla: function(parentNode) {
+     var xmlSerializer = new XMLSerializer();
+     var contentStr = "";
+     for ( var i = 0 ; i < parentNode.childNodes.length ; i++ ) {
+          var n = parentNode.childNodes[i];
+          if (n.nodeType == 4) { // CDATA node
+              contentStr += n.nodeValue;
+          }
+          else {
+            contentStr += xmlSerializer.serializeToString(n);
+        }
+     }
+     return contentStr;
+  },
+
+  docElement: function() {
+    return (document.compatMode && document.compatMode.indexOf("CSS")!=-1) ? document.documentElement : document.getElementsByTagName("body")[0];
+  },
+
+/**
+ * return available height - excludes scrollbar & margin
+ */
+  windowHeight: function() {
+    return window.innerHeight? window.innerHeight : this.docElement().clientHeight;
+    //return this.docElement().clientHeight;
+  },
+
+/**
+ * return available width - excludes scrollbar & margin
+ */
+  windowWidth: function() {
+    return this.docElement().clientWidth;
+  },
+
+  docScrollLeft: function() {
+     if ( window.pageXOffset )
+        return window.pageXOffset;
+     else if ( document.documentElement && document.documentElement.scrollLeft )
+        return document.documentElement.scrollLeft;
+     else if ( document.body )
+        return document.body.scrollLeft;
+     else
+        return 0;
+  },
+
+  docScrollTop: function() {
+     if ( window.pageYOffset )
+        return window.pageYOffset;
+     else if ( document.documentElement && document.documentElement.scrollTop )
+        return document.documentElement.scrollTop;
+     else if ( document.body )
+        return document.body.scrollTop;
+     else
+        return 0;
+  },
+
+  nan2zero: function(n) {
+    if (typeof(n)=='string') n=parseInt(n);
+    return isNaN(n) || typeof(n)=='undefined' ? 0 : n;
+  },
+
+  eventKey: function(e) {
+    if( typeof( e.keyCode ) == 'number'  ) {
+      return e.keyCode; //DOM
+    } else if( typeof( e.which ) == 'number' ) {
+      return e.which;   //NS 4 compatible
+    } else if( typeof( e.charCode ) == 'number'  ) {
+      return e.charCode; //also NS 6+, Mozilla 0.9+
+    }
+    return -1;  //total failure, we have no way of obtaining the key code
+  },
+
+/**
+ * Return the previous sibling that has the specified tagName
+ */
+   getPreviosSiblingByTagName: function(el,tagName) {
+       var sib=el.previousSibling;
+       while (sib) {
+               if ((sib.tagName==tagName) && (sib.style.display!='none')) return sib;
+               sib=sib.previousSibling;
+       }
+       return null;
+   },
+
+/**
+ * Return the parent HTML element that has the specified tagName.
+ * @param className optional
+ */
+   getParentByTagName: function(el,tagName,className) {
+       var par=el;
+       tagName=tagName.toLowerCase();
+       while (par) {
+               if (par.tagName && par.tagName.toLowerCase()==tagName)
+        if (!className || par.className.indexOf(className)>=0) return par;
+               par=par.parentNode;
+       }
+       return null;
+   },
+
+/**
+ * Wrap the children of a DOM element in a new element
+ * @param el the element whose children are to be wrapped
+ * @param cls class name of the wrapper (optional)
+ * @param id id of the wrapper (optional)
+ * @param wrapperTag type of wrapper element to be created (optional, defaults to DIV)
+ */
+  wrapChildren: function(el,cls,id,wrapperTag) {
+    var tag=wrapperTag || 'div';
+    var wrapper = document.createElement(tag);
+    if (id) wrapper.id=id;
+    if (cls) wrapper.className=cls;
+    while (el.firstChild)
+      wrapper.appendChild(el.firstChild);
+    el.appendChild(wrapper);
+    return wrapper;
+  },
+
+/**
+ * Format a positive number
+ * @param decPlaces the number of digits to display after the decimal point
+ * @param thouSep the character to use as the thousands separator
+ * @param decPoint the character to use as the decimal point
+ */
+  formatPosNumber: function(posnum,decPlaces,thouSep,decPoint) {
+    var a=posnum.toFixed(decPlaces).split(/\./);
+    if (thouSep) {
+      var rgx = /(\d+)(\d{3})/;
+      while (rgx.test(a[0]))
+        a[0]=a[0].replace(rgx, '$1'+thouSep+'$2');
+    }
+    return a.join(decPoint);
+  },
+
+/**
+ * Post condition - if childNodes[n] is refChild, than childNodes[n+1] is newChild.
+ */
+  DOMNode_insertAfter: function(newChild,refChild) {
+    var parentx=refChild.parentNode;
+    if(parentx.lastChild==refChild) { return parentx.appendChild(newChild);}
+    else {return parentx.insertBefore(newChild,refChild.nextSibling);}
+  },
+
+  positionCtlOverIcon: function(ctl,icon) {
+    if (ctl.style.display=='none') ctl.style.display='block';
+    var offsets=Position.page(icon);
+    var correction=Prototype.Browser.IE ? 1 : 2;  // based on a 1px border
+    var lpad=this.nan2zero(Element.getStyle(icon,'padding-left'))
+    ctl.style.left = (offsets[0]+lpad+correction)+'px';
+    var scrTop=this.docScrollTop();
+    var newTop=offsets[1] + correction + scrTop;
+    var ctlht=ctl.offsetHeight;
+    var iconht=icon.offsetHeight;
+    if (newTop+iconht+ctlht < this.windowHeight()+scrTop)
+      newTop+=iconht;  // display below icon
+    else
+      newTop=Math.max(newTop-ctlht,scrTop);  // display above icon
+    ctl.style.top = newTop+'px';
+  },
+
+  createFormField: function(parent,elemTag,elemType,id,name) {
+    if (typeof name!='string') name=id;
+    if (Prototype.Browser.IE) {
+      // IE cannot set NAME attribute on dynamically created elements
+      var s=elemTag+' id="'+id+'"';
+      if (elemType) s+=' type="'+elemType+'"';
+      if (elemTag.match(/^(form|input|select|textarea|object|button|img)$/)) s+=' name="'+name+'"';
+      var field=document.createElement('<'+s+' />');
+    } else {
+      var field=document.createElement(elemTag);
+      if (elemType) field.type=elemType;
+      field.id=id;
+      if (typeof field.name=='string') field.name=name;
+    }
+    parent.appendChild(field);
+    return field;
+  },
+
+/**
+ * Gets the value of the specified cookie
+ */
+  getCookie: function(itemName) {
+    var arg = itemName+'=';
+    var alen = arg.length;
+    var clen = document.cookie.length;
+    var i = 0;
+    while (i < clen) {
+      var j = i + alen;
+      if (document.cookie.substring(i, j) == arg) {
+        var endstr = document.cookie.indexOf (';', j);
+        if (endstr == -1) endstr=document.cookie.length;
+        return unescape(document.cookie.substring(j, endstr));
+      }
+      i = document.cookie.indexOf(' ', i) + 1;
+      if (i == 0) break;
+    }
+    return null;
+  },
+
+/**
+ * Write information to cookie.
+ * For cookies to be retained for the current session only, set daysToKeep=null.
+ * To erase a cookie, pass a negative daysToKeep value.
+ */
+  setCookie: function(itemName,itemValue,daysToKeep,cookiePath,cookieDomain) {
+       var c = itemName+"="+escape(itemValue);
+       if (typeof(daysToKeep)=='number') {
+               var date = new Date();
+               date.setTime(date.getTime()+(daysToKeep*24*60*60*1000));
+               c+="; expires="+date.toGMTString();
+       }
+       if (typeof(cookiePath)=='string') c+="; path="+cookiePath;
+       if (typeof(cookieDomain)=='string') c+="; domain="+cookieDomain;
+    document.cookie = c;
+  }
+
+};
+
+
+// Translation helper object
+/** @singleton */
+var RicoTranslate = {
+  phrases : {},
+  thouSep : ",",
+  decPoint: ".",
+  langCode: "en",
+  re      : /^(\W*)\b(.*)\b(\W*)$/,
+  dateFmt : "mm/dd/yyyy",
+  timeFmt : "hh:nn:ss a/pm",
+  monthNames: ['January','February','March','April','May','June',
+               'July','August','September','October','November','December'],
+  dayNames: ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],
+
+  addPhrase: function(fromPhrase, toPhrase) {
+    this.phrases[fromPhrase]=toPhrase;
+  },
+
+/**
+ * fromPhrase may contain multiple words/phrases separated by tabs
+ * and each portion will be looked up separately.
+ * Punctuation & spaces at the beginning or
+ * ending of a phrase are ignored.
+ */
+  getPhrase: function(fromPhrase) {
+    var words=fromPhrase.split(/\t/);
+    var transWord,translated = '';
+    for (var i=0; i<words.length; i++) {
+      if (this.re.exec(words[i])) {
+        transWord=this.phrases[RegExp.$2];
+        translated += (typeof transWord=='string') ? RegExp.$1+transWord+RegExp.$3 : words[i];
+      } else {
+        translated += words[i];
+      }
+    }
+    return translated;
+  }
+}
+
+
+if (!Date.prototype.formatDate) {
+  Date.prototype.formatDate = function(fmt) {
+    var d=this;
+    var datefmt=(typeof fmt=='string') ? datefmt=fmt : 'translateDate';
+    switch (datefmt) {
+      case 'locale':
+      case 'localeDateTime':
+        return d.toLocaleString();
+      case 'localeDate':
+        return d.toLocaleDateString();
+      case 'translate':
+      case 'translateDateTime':
+        datefmt=RicoTranslate.dateFmt+' '+RicoTranslate.timeFmt;
+        break;
+      case 'translateDate':
+        datefmt=RicoTranslate.dateFmt;
+        break;
+    }
+    return datefmt.replace(/(yyyy|mmmm|mmm|mm|dddd|ddd|dd|hh|nn|ss|a\/p)/gi,
+      function($1) {
+        switch ($1.toLowerCase()) {
+        case 'yyyy': return d.getFullYear();
+        case 'mmmm': return RicoTranslate.monthNames[d.getMonth()];
+        case 'mmm':  return RicoTranslate.monthNames[d.getMonth()].substr(0, 3);
+        case 'mm':   return (d.getMonth() + 1).toPaddedString(2);
+        case 'm':    return (d.getMonth() + 1);
+        case 'dddd': return RicoTranslate.dayNames[d.getDay()];
+        case 'ddd':  return RicoTranslate.dayNames[d.getDay()].substr(0, 3);
+        case 'dd':   return d.getDate().toPaddedString(2);
+        case 'd':    return d.getDate();
+        case 'hh':   return ((h = d.getHours() % 12) ? h : 12).toPaddedString(2);
+        case 'h':    return ((h = d.getHours() % 12) ? h : 12);
+        case 'HH':   return d.getHours().toPaddedString(2);
+        case 'H':    return d.getHours();
+        case 'nn':   return d.getMinutes().toPaddedString(2);
+        case 'ss':   return d.getSeconds().toPaddedString(2);
+        case 'a/p':  return d.getHours() < 12 ? 'a' : 'p';
+        }
+      }
+    );
+  }
+}
+
+if (!Date.prototype.setISO8601) {
+/**
+ * Converts a string in ISO 8601 format to a date object.
+ * Returns true if string is a valid date or date-time.
+ * Based on info at http://delete.me.uk/2005/03/iso8601.html
+ */
+  Date.prototype.setISO8601 = function (string) {
+    if (!string) return false;
+    var d = string.match(/(\d\d\d\d)(?:-?(\d\d)(?:-?(\d\d)(?:[T ](\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|(?:([-+])(\d\d)(?::?(\d\d))?)?)?)?)?)?/);
+    if (!d) return false;
+    var offset = 0;
+    var date = new Date(d[1], 0, 1);
+
+    if (d[2]) { date.setMonth(d[2] - 1); }
+    if (d[3]) { date.setDate(d[3]); }
+    if (d[4]) { date.setHours(d[4]); }
+    if (d[5]) { date.setMinutes(d[5]); }
+    if (d[6]) { date.setSeconds(d[6]); }
+    if (d[7]) { date.setMilliseconds(Number("0." + d[7]) * 1000); }
+    if (d[8]) {
+        if (d[10] && d[11]) offset = (Number(d[10]) * 60) + Number(d[11]);
+        offset *= ((d[9] == '-') ? 1 : -1);
+        offset -= date.getTimezoneOffset();
+    }
+    var time = (Number(date) + (offset * 60 * 1000));
+    this.setTime(Number(time));
+    return true;
+  }
+}
+
+if (!Date.prototype.toISO8601String) {
+/**
+ * Convert date to an ISO 8601 formatted string.
+ * <p>format is an integer in the range 1-6:<dl>
+ * <dt>1 (year)</dt>
+ *   <dd>YYYY (eg 1997)</dd>
+ * <dt>2 (year and month)</dt>
+ *   <dd>YYYY-MM (eg 1997-07)</dd>
+ * <dt>3 (complete date)</dt>
+ *   <dd>YYYY-MM-DD (eg 1997-07-16)</dd>
+ * <dt>4 (complete date plus hours and minutes)</dt>
+ *   <dd>YYYY-MM-DDThh:mmTZD (eg 1997-07-16T19:20+01:00)</dd>
+ * <dt>5 (complete date plus hours, minutes and seconds)</dt>
+ *   <dd>YYYY-MM-DDThh:mm:ssTZD (eg 1997-07-16T19:20:30+01:00)</dd>
+ * <dt>6 (complete date plus hours, minutes, seconds and a decimal
+ *   fraction of a second)</dt>
+ *   <dd>YYYY-MM-DDThh:mm:ss.sTZD (eg 1997-07-16T19:20:30.45+01:00)</dd>
+ *</dl>
+ * Based on: http://www.codeproject.com/jscript/dateformat.asp
+ */
+  Date.prototype.toISO8601String = function (format, offset) {
+    if (!format) { var format = 6; }
+    if (!offset) {
+        var offset = 'Z';
+        var date = this;
+    } else {
+        var d = offset.match(/([-+])([0-9]{2}):([0-9]{2})/);
+        var offsetnum = (Number(d[2]) * 60) + Number(d[3]);
+        offsetnum *= ((d[1] == '-') ? -1 : 1);
+        var date = new Date(Number(Number(this) + (offsetnum * 60000)));
+    }
+
+    var zeropad = function (num) { return ((num < 10) ? '0' : '') + num; }
+
+    var str = "";
+    str += date.getUTCFullYear();
+    if (format > 1) { str += "-" + zeropad(date.getUTCMonth() + 1); }
+    if (format > 2) { str += "-" + zeropad(date.getUTCDate()); }
+    if (format > 3) {
+        str += "T" + zeropad(date.getUTCHours()) +
+               ":" + zeropad(date.getUTCMinutes());
+    }
+    if (format > 5) {
+        var secs = Number(date.getUTCSeconds() + "." +
+                   ((date.getUTCMilliseconds() < 100) ? '0' : '') +
+                   zeropad(date.getUTCMilliseconds()));
+        str += ":" + zeropad(secs);
+    } else if (format > 4) { str += ":" + zeropad(date.getUTCSeconds()); }
+
+    if (format > 3) { str += offset; }
+    return str;
+  }
+}
+
+if (!String.prototype.formatDate) {
+  String.prototype.formatDate = function(fmt) {
+    var s=this.replace(/-/g,'/');
+    var d = new Date(s);
+    return isNaN(d) ? this : d.formatDate(fmt);
+  }
+}
+
+if (!Number.prototype.formatNumber) {
+/**
+ * Format a number according to the specs in assoc array 'fmt'.
+ * Result is a string, wrapped in a span element with a class of: negNumber, zeroNumber, posNumber
+ * These classes can be set in CSS to display negative numbers in red, for example.
+ *
+ * <p>fmt may contain:<dl>
+ *   <dt>multiplier </dt><dd> the original number is multiplied by this amount before formatting</dd>
+ *   <dt>decPlaces  </dt><dd> number of digits to the right of the decimal point</dd>
+ *   <dt>decPoint   </dt><dd> character to be used as the decimal point</dd>
+ *   <dt>thouSep    </dt><dd> character to use as the thousands separator</dd>
+ *   <dt>prefix     </dt><dd> string added to the beginning of the result (e.g. a currency symbol)</dd>
+ *   <dt>suffix     </dt><dd> string added to the end of the result (e.g. % symbol)</dd>
+ *   <dt>negSign    </dt><dd> specifies format for negative numbers: L=leading minus, T=trailing minus, P=parens</dd>
+ *</dl>
+ */
+  Number.prototype.formatNumber = function(fmt) {
+    if (isNaN(this)) return 'NaN';
+    var n=this;
+    if (typeof fmt.multiplier=='number') n*=fmt.multiplier;
+    var decPlaces=typeof fmt.decPlaces=='number' ? fmt.decPlaces : 0;
+    var thouSep=typeof fmt.thouSep=='string' ? fmt.thouSep : RicoTranslate.thouSep;
+    var decPoint=typeof fmt.decPoint=='string' ? fmt.decPoint : RicoTranslate.decPoint;
+    var prefix=fmt.prefix || "";
+    var suffix=fmt.suffix || "";
+    var negSign=typeof fmt.negSign=='string' ? fmt.negSign : "L";
+    negSign=negSign.toUpperCase();
+    var s,cls;
+    if (n<0.0) {
+      s=RicoUtil.formatPosNumber(-n,decPlaces,thouSep,decPoint);
+      if (negSign=="P") s="("+s+")";
+      s=prefix+s;
+      if (negSign=="L") s="-"+s;
+      if (negSign=="T") s+="-";
+      cls='negNumber';
+    } else {
+      cls=n==0.0 ? 'zeroNumber' : 'posNumber';
+      s=prefix+RicoUtil.formatPosNumber(n,decPlaces,thouSep,decPoint);
+    }
+    return "<span class='"+cls+"'>"+s+suffix+"</span>";
+  }
+}
+
+if (!String.prototype.formatNumber) {
+/**
+ * Take a string that can be converted via parseFloat
+ * and format it according to the specs in assoc array 'fmt'.
+ */
+  String.prototype.formatNumber = function(fmt) {
+    var n=parseFloat(this);
+    return isNaN(n) ? this : n.formatNumber(fmt);
+  }
+}
+
+/**
+ * Fix select control bleed-thru on floating divs in IE.
+ * Based on technique published by Joe King at:
+ * http://dotnetjunkies.com/WebLog/jking/archive/2003/10/30/2975.aspx
+ */
+Rico.Shim = Class.create();
+
+if (Prototype.Browser.IE) {
+  Rico.Shim.prototype = {
+
+    initialize: function(DivRef) {
+      this.ifr = document.createElement('iframe');
+      this.ifr.style.position="absolute";
+      this.ifr.style.display = "none";
+      this.ifr.src="javascript:false;";
+      DivRef.parentNode.appendChild(this.ifr);
+      this.DivRef=DivRef;
+    },
+
+    hide: function() {
+      this.ifr.style.display = "none";
+    },
+
+    show: function() {
+      this.ifr.style.width   = this.DivRef.offsetWidth;
+      this.ifr.style.height  = this.DivRef.offsetHeight;
+      this.ifr.style.top     = this.DivRef.style.top;
+      this.ifr.style.left    = this.DivRef.style.left;
+      this.ifr.style.zIndex  = this.DivRef.currentStyle.zIndex - 1;
+      this.ifr.style.display = "block";
+    }
+  }
+} else {
+  Rico.Shim.prototype = {
+    initialize: function() {},
+    hide: function() {},
+    show: function() {}
+  }
+}
+
+
+/**
+ * Rico.Shadow is intended for positioned elements.
+ * Uses blur filter in IE, and alpha-transparent png images for all other browsers.
+ * Based on: http://www.positioniseverything.net/articles/dropshadows.html
+ */
+Rico.Shadow = Class.create();
+
+Rico.Shadow.prototype = {
+
+  initialize: function(DivRef) {
+    this.div = document.createElement('div');
+    this.div.style.position="absolute";
+    if (typeof this.div.style.filter=='undefined') {
+      new Image().src = Rico.imgDir+"shadow.png";
+      new Image().src = Rico.imgDir+"shadow_ur.png";
+      new Image().src = Rico.imgDir+"shadow_ll.png";
+      this.createShadow();
+      this.offset=5;
+    } else {
+      this.div.style.backgroundColor='#888';
+      this.div.style.filter='progid:DXImageTransform.Microsoft.Blur(makeShadow=1, shadowOpacity=0.3, pixelRadius=3)';
+      this.offset=0; // MS blur filter already does offset
+    }
+    this.div.style.display = "none";
+    DivRef.parentNode.appendChild(this.div);
+    this.DivRef=DivRef;
+  },
+
+  createShadow: function() {
+    var tab = document.createElement('table');
+    tab.style.height='100%';
+    tab.style.width='100%';
+    tab.cellSpacing=0;
+    tab.dir='ltr';
+
+    var tr1=tab.insertRow(-1);
+    tr1.style.height='8px';
+    var td11=tr1.insertCell(-1);
+    td11.style.width='8px';
+    var td12=tr1.insertCell(-1);
+    td12.style.background="transparent url("+Rico.imgDir+"shadow_ur.png"+") no-repeat right bottom"
+
+    var tr2=tab.insertRow(-1);
+    var td21=tr2.insertCell(-1);
+    td21.style.background="transparent url("+Rico.imgDir+"shadow_ll.png"+") no-repeat right bottom"
+    var td22=tr2.insertCell(-1);
+    td22.style.background="transparent url("+Rico.imgDir+"shadow.png"+") no-repeat right bottom"
+
+    this.div.appendChild(tab);
+  },
+
+  hide: function() {
+    this.div.style.display = "none";
+  },
+
+  show: function() {
+    this.div.style.width = this.DivRef.offsetWidth + 'px';
+    this.div.style.height= this.DivRef.offsetHeight + 'px';
+    this.div.style.top   = (parseInt(this.DivRef.style.top)+this.offset)+'px';
+    this.div.style.left  = (parseInt(this.DivRef.style.left)+this.offset)+'px';
+    this.div.style.zIndex= parseInt(Element.getStyle(this.DivRef,'z-index')) - 1;
+    this.div.style.display = "block";
+  }
+}
+
+
+Rico.Popup = Class.create();
+
+Rico.Popup.prototype = {
+
+  initialize: function(options,DivRef) {
+    this.options = {
+      hideOnEscape  : true,
+      hideOnClick   : true,
+      ignoreClicks  : false,
+      position      : 'absolute',
+      shadow        : true
+    }
+    Object.extend(this.options, options || {});
+    if (DivRef) this.setDiv(DivRef);
+  },
+
+  setDiv: function(DivRef,closeFunc) {
+    this.divPopup=$(DivRef);
+    var position=this.options.position == 'auto' ? Element.getStyle(this.divPopup,'position').toLowerCase() : this.options.position;
+    if (!this.divPopup || position != 'absolute') return;
+    this.closeFunc=closeFunc || this.closePopup.bindAsEventListener(this);
+    this.shim=new Rico.Shim(this.divPopup);
+    if (this.options.shadow)
+      this.shadow=new Rico.Shadow(this.divPopup);
+    if (this.options.hideOnClick)
+      Event.observe(document,"click", this.closeFunc);
+    if (this.options.hideOnEscape)
+      Event.observe(document,"keyup", this._checkKey.bindAsEventListener(this));
+    if (this.options.ignoreClicks) this.ignoreClicks();
+  },
+  
+  ignoreClicks: function() {
+    Event.observe(this.divPopup,"click", this._ignoreClick.bindAsEventListener(this));
+  },
+
+  _ignoreClick: function(e) {
+    if (e.stopPropagation)
+      e.stopPropagation();
+    else
+      e.cancelBubble = true;
+    return true;
+  },
+  
+  // event handler to process keyup events (hide menu on escape key)
+  _checkKey: function(e) {
+    if (RicoUtil.eventKey(e)==27) this.closeFunc();
+    return true;
+  },
+
+  openPopup: function(left,top) {
+    if (typeof left=='number') this.divPopup.style.left=left+'px';
+    if (typeof top=='number') this.divPopup.style.top=top+'px';
+    this.divPopup.style.display="block";
+    if (this.shim) this.shim.show();
+    if (this.shadow) this.shadow.show();
+  },
+
+  closePopup: function() {
+    if (this.shim) this.shim.hide();
+    if (this.shadow) this.shadow.hide();
+    this.divPopup.style.display="none"
+  }
+
+}
+
+Rico.includeLoaded('ricoCommon.js');
diff --git a/NP_TrackBack/trunk/trackback/js/rico/ricoComponents.js b/NP_TrackBack/trunk/trackback/js/rico/ricoComponents.js
new file mode 100644 (file)
index 0000000..1db5830
--- /dev/null
@@ -0,0 +1,194 @@
+/**
+  *  (c) 2005-2007 Richard Cowin (http://openrico.org)
+  *  (c) 2005-2007 Matt Brown (http://dowdybrown.com)
+  *
+  *  Rico is 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
+  **/
+  
+
+Rico.ContentTransitionBase = function() {}
+Rico.ContentTransitionBase.prototype = {
+       initialize: function(titles, contents, options) { 
+    if (typeof titles == 'string')
+      titles = $$(titles)
+    if (typeof contents == 'string')
+      contents = $$(contents)
+         
+         this.titles = titles;
+         this.contents = contents;
+               this.options = Object.extend({
+                       duration:200, 
+                       steps:8,
+                       rate:Rico.Effect.easeIn
+         }, options || {});
+         this.hoverSet = new Rico.HoverSet(titles, options);
+               contents.each(function(p){ if (p) Element.hide(p)})
+         this.selectionSet = new Rico.SelectionSet(titles, Object.extend(this.options, {onSelect: this.select.bind(this)}));
+               if (this.initContent) this.initContent();
+       },
+       reset: function(){
+         this.selectionSet.reset();
+       },
+       select: function(title) {
+         if ( this.selected == this.contentOf(title)) return
+               var panel = this.contentOf(title); 
+               if (this.transition){
+                       if (this.selected){
+                         var effect = this.transition(panel)
+                         if (effect) Rico.animate(effect, this.options)
+      }
+                       else
+                               Element.show(panel);
+               }else{
+                       if (this.selected)
+                               Element.hide(this.selected)
+                       Element.show(panel);
+               }
+               this.selected = panel;
+       },
+       add: function(title, content){
+               this.titles.push(title);
+               this.contents.push(content);
+               this.hoverSet.add(title);
+               this.selectionSet.add(title);   
+               this.selectionSet.select(title);
+       },
+       remove: function(title){},
+       removeAll: function(){
+               this.hoverSet.removeAll();
+               this.selectionSet.removeAll();
+       },
+       openByIndex: function(index){this.selectionSet.selectIndex(index)},
+       contentOf: function(title){ return this.contents[this.titles.indexOf(title)]}
+}
+
+Rico.ContentTransition = Class.create();
+Rico.ContentTransition.prototype = Object.extend(new Rico.ContentTransitionBase(),{});
+
+Rico.SlidingPanel = Class.create();
+Rico.SlidingPanel.prototype = {
+       initialize: function(panel) {
+               this.panel = panel;
+               this.options = arguments[1] || {};
+               this.closed = true;
+               this.showing = false
+               this.openEffect = this.options.openEffect;
+               this.closeEffect = this.options.closeEffect;
+               this.animator = new Rico.Effect.Animator();
+               Element.makeClipping(this.panel)
+       },
+       toggle: function () {
+               if(!this.showing){
+                       this.open();
+               } else { 
+                       this.close();
+    }
+       },
+       open: function () {
+         if (this.closed){
+           this.showing = true;
+                 Element.show(this.panel);
+               this.options.disabler.disableNative();
+    }
+               /*this.animator.stop();*/
+               this.animator.play(this.openEffect,
+                                                                                       { onFinish:function(){ Element.undoClipping($(this.panel))}.bind(this),
+                                                                                               rate:Rico.Effect.easeIn});
+       },
+       close: function () {
+               Element.makeClipping(this.panel)
+               this.animator.stop();
+               this.showing = false;
+               this.animator.play(this.closeEffect,
+                            { onFinish:function(){  Element.hide(this.panel);  
+                                                                                                                                                                                       this.options.disabler.enableNative()}.bind(this),       
+                                                                                               rate:Rico.Effect.easeOut});
+       }
+}
+
+
+//-------------------------------------------
+// Example components
+//-------------------------------------------
+
+Rico.Accordion = Class.create();
+Rico.Accordion.prototype = Object.extend(new Rico.ContentTransitionBase(), {
+  initContent: function() { 
+               this.selected.style.height = this.options.panelHeight + "px";
+       },
+  transition: function(p){ 
+    if (!this.options.noAnimate)
+                 return new Rico.AccordionEffect(this.selected, p, this.options.panelHeight);
+    else{
+      p.style.height = this.options.panelHeight + "px";
+      if (this.selected) Element.hide(this.selected);
+               Element.show(p);
+    }
+       }
+})
+
+
+Rico.TabbedPanel = Class.create();
+Rico.TabbedPanel.prototype = Object.extend(new Rico.ContentTransitionBase(), {
+  initContent: function() { 
+               if (false && (this.options.panelHeight=='auto' || this.options.panelWidth=='auto')) {
+                 // 'auto' is not working yet
+                 var maxwi=0, maxht=0;
+                 for (var i=0; i<this.contents.length; i++) {
+                   var d=Element.getDimensions(this.contents[i]);
+                   maxwi=Math.max(maxwi,d.width);
+                   maxht=Math.max(maxht,d.height);
+                 }
+                 //alert('maxwi='+maxwi+' maxht='+maxht);
+                 if (this.options.panelWidth=='auto') this.options.panelWidth=maxwi;
+                 if (this.options.panelHeight=='auto') this.options.panelHeight=maxht;
+               }
+         if (typeof this.options.panelWidth=='number') this.options.panelWidth+="px";
+         if (typeof this.options.panelHeight=='number') this.options.panelHeight+="px";
+    if (Rico.Corner) {
+      this.options.color='transparent';
+      this.options.corners='top';
+      for (var i=0; i<this.titles.length; i++)
+        if (this.titles[i]) {
+          if (this.options.panelHdrWidth) this.titles[i].style.width=this.options.panelHdrWidth;
+          Rico.Corner.round(this.titles[i], this.options);
+        }
+    }
+               this.transition(this.selected);
+       },
+  transition: function(p){ 
+    if (this.selected) Element.hide(this.selected);
+               Element.show(p);
+    if (this.options.panelHeight) p.style.height = this.options.panelHeight;
+    if (this.options.panelWidth) p.style.width = this.options.panelWidth;
+       }
+})
+
+
+Rico.SlidingPanel.top = function(panel, innerPanel){
+       var options = Object.extend({
+               disabler: Rico.Controls.defaultDisabler
+  }, arguments[2] || {});
+       var height = options.height || Element.getDimensions(innerPanel)[1] || innerPanel.offsetHeight;
+       var top = options.top || Position.positionedOffset(panel)[1];
+       options.openEffect = new Rico.Effect.SizeFromTop(panel, innerPanel, top, height, {baseHeight:height});
+       options.closeEffect = new Rico.Effect.SizeFromTop(panel, innerPanel, top, 1, {baseHeight:height});
+  panel.style.height = "0px";
+       innerPanel.style.top = -height + "px";  
+       return new Rico.SlidingPanel(panel, options);
+}
+
+Rico.SlidingPanel.bottom = function(panel){
+       var options = Object.extend({
+               disabler: Rico.Controls.blankDisabler
+  }, arguments[1] || {});
+       var height = options.height || Element.getDimensions(panel).height;
+       var top = Position.positionedOffset(panel)[1];
+       options.openEffect = new Rico.Effect.SizeFromBottom(panel, top - height, height);
+       options.closeEffect = new Rico.Effect.SizeFromBottom(panel, top, 1);
+       return new Rico.SlidingPanel(panel, options); 
+}
+
+Rico.includeLoaded('ricoComponents.js');
diff --git a/NP_TrackBack/trunk/trackback/js/rico/ricoDashboard.js b/NP_TrackBack/trunk/trackback/js/rico/ricoDashboard.js
new file mode 100644 (file)
index 0000000..17cc5a7
--- /dev/null
@@ -0,0 +1,329 @@
+
+Rico.Dashboard = Class.create();
+Rico.Dashboard.prototype = {
+       initialize: function(dashboardId, columnCount, options) {
+               this.dashboardDiv = $(dashboardId);
+               this.numCol = columnCount;
+               this.options = options || [];
+               this.cols = new Array(); 
+               this.insertionOutline = document.createElement("div");
+               this.insertionOutline.id = "insertionOutline";
+     
+               //get panels before adding collumns
+               var dashboard = this
+   this.panelList = [];
+   // this.panelList = parsePanels(this.dashboardDiv, function(title, content, panel)
+  //                                 { return new Rico.DashboardPanel(title, content, panel, dashboard);})
+               var colSizes = this.options.columnSizes 
+    if (!colSizes){
+      colSizes = [];
+      for(var i=0; i<this.numCol; i++)
+        colSizes[i] = 100 / columnCount;
+    }
+
+               for(var i=0; i< this.numCol;i++)        {
+          var newColDiv = document.createElement("div");
+          newColDiv.style.width = colSizes[i] + "%";
+          newColDiv.style.minHeight = "1px";
+          newColDiv.className = "column";
+          newColDiv.id = "" + (i+1) ;
+          this.cols.push(newColDiv);
+          this.dashboardDiv.appendChild(newColDiv);
+          //if (i < this.numCol-1){
+         //    var borderDiv = document.createElement("div");
+         //    borderDiv.style.width = "3px";
+         //    borderDiv.style.height = "100%";
+         //    borderDiv.style.background = "111111"
+         //    borderDiv.className = "border";
+         //    //borderDiv.style.visibility = "visible";
+         //    this.dashboardDiv.appendChild(borderDiv);
+         // }
+               }
+      //now add the panels to the columns
+    for (var i=0; i< this.panelList.length; i++) {
+         var panel = this.panelList[i];                
+         this._addToCol(panel, panel.panelDiv.getAttribute('column'));
+               }
+       },
+       
+       addPanel: function(panel, col){
+         this.panelList.push(panel)
+         this._addToCol(panel, col)
+       },
+       
+       _addToCol: function(panel, col) {
+               panel.addToCol(this.cols[col-1]);         
+       },
+       
+       closeAllPanels: function() {
+         var panels = this.panelList;
+         for (var i=0; i<panels.length; i++)
+           panels[i].close();
+         this.panelList = [];
+       },
+       
+       openAllPanels: function(open) {
+               for (var i=0; i<this.panelList.length; i++) 
+                       this.panelList[i].setVisibility(open);
+       },
+       
+       columnAt: function(x) {
+               for (var i=this.cols.length-1; i >=0; i--)      {
+                       if (x >= Position.positionedOffset(this.cols[i])[0])
+                               return this.cols[i];
+               }
+               return this.cols[0];
+       },
+       
+       destroy: function() {
+               try{
+                       for (var i=0; i<this.panelList.length; i++) {
+                               delete this.panelList[i];
+                               this.panelList[i] = null;
+                       }
+                       delete this;
+               }catch(e){}
+       },
+       
+       dropPanel: function(panel){
+         panel.column.removeChild(panel.panelDiv);
+         panel.column = this.insertionColumn;
+         this.insertionColumn.replaceChild(panel.panelDiv, this.insertionOutline);
+  },
+
+  dragPanel: function(panel, left, top){
+    var newCol = this.columnAt(left + panel.panelDiv.offsetWidth/2);
+
+    if (!newCol) return;  
+    
+               this._moveInsertion(newCol);
+               var panels = this.columnPanels(newCol);
+               var insertPos = this._getInsertionPos(panels);
+
+               if (insertPos != 0 && 
+                   top <= Position.positionedOffset(panels[insertPos-1])[1]) {
+                       this.insertionColumn.removeChild(this.insertionOutline);
+                       newCol.insertBefore(this.insertionOutline, panels[insertPos-1]);
+               }
+               if (insertPos != (panels.length-1) && 
+                   top >= Position.positionedOffset(panels[insertPos+1])[1]) {
+                       if (panels[insertPos + 2]) 
+                                 newCol.insertBefore(this.insertionOutline, panels[insertPos+2]);
+                        else
+                                 newCol.appendChild(this.insertionOutline);
+               }    
+               this.insertionColumn = newCol;
+  },
+
+  _moveInsertion: function(column){
+               if (this.insertionColumn != column) {
+                       this.insertionColumn.removeChild(this.insertionOutline)
+                       this.insertionColumn = column;
+                       column.appendChild(this.insertionOutline);
+       }
+  },
+  
+       columnPanels: function(column){
+                       var panels = [];
+                       for (var i=0; i<column.childNodes.length; i++) {
+                               if (!column.childNodes[i].isDragging)  {
+                                       panels.push(column.childNodes[i]);
+                               }
+                       }
+                       return panels;
+       },
+  
+       _getInsertionPos : function(panels) {
+               for (var i=0; i<panels.length; i++) {
+                       if (panels[i] == this.insertionOutline) 
+                         return i;
+               }
+       },
+       
+       startInsertionOutline: function(panelDiv){
+         this.insertionOutline.style.height = panelDiv.offsetHeight + "px";
+         panelDiv.parentNode.insertBefore(this.insertionOutline, panelDiv);
+         this.insertionColumn = panelDiv.parentNode;
+  }
+}
+
+Rico.PanelCreation = {
+  create: function(title, url, dashboard) {    
+               var panelDiv = document.createElement("div");
+    var titleDiv = PanelCreation.createHeader(title)
+    var contentDiv = PanelCreation.createContent()
+               panelDiv.className = "panel";
+               panelDiv.appendChild(titleDiv);
+               panelDiv.appendChild(contentDiv);       
+       return new Rico.DashboardPanel(titleDiv, contentDiv, panelDiv, dashboard)
+       },
+       createHeader: function(title) {
+               this.panelHeaderDiv = document.createElement("div");
+               this.panelHeaderDiv.className = "panelHeader";
+               this.panelHeaderDiv.innerHTML = document.createTextNode(this.title);
+               initializeHeader(this.panelHeaderDiv);
+               return this.panelHeaderDiv;
+       },
+       createContent: function() {
+               this.panelContentDiv = document.createElement("div");
+               this.panelContentDiv.className = "panelContent";
+               this.panelContentDiv.innerHTML = "Loading";
+               return this.panelContentDiv;
+       }
+}
+
+Rico.DashboardPanel = Class.create();
+Rico.DashboardPanel.prototype = {
+       initialize: function(headerDiv, contentDiv, panelDiv, dashboard) {
+               this.dashboard = dashboard;
+               this.panelHeaderDiv = headerDiv;
+               this.panelContentDiv = contentDiv;
+               this.panelDiv = panelDiv;
+               this.open = true;
+               panelDiv.style.zIndex = 1000;
+               this.initializeHeader(headerDiv);
+       Event.observe(headerDiv, "mousedown", this._startDrag.bind(this));
+  },
+    
+       initializeHeader: function(headerDiv) {
+               headerDiv.onmouseover = this.hover.bind(this);
+               headerDiv.onmouseout = this.unHover.bind(this);
+               
+//             this.visibilityToggleDiv = document.createElement("div");
+//             this.visibilityToggleDiv.className = "visibilityToggle";
+//             this.visibilityToggleDiv.innerHTML = '<img src="/images/bkgd_panel_arrow.png"/>';
+//             this.visibilityToggleDiv.style.visibility = "hidden";
+//             this.visibilityToggleDiv.onmousedown = this.toggleVisibility.bind(this);
+       
+               this.titleDiv = document.createElement("div");
+               this.titleDiv.innerHTML = headerDiv.innerHTML;          
+               this.titleDiv.className = "title";
+               
+               headerDiv.innerHTML = '';
+               
+               this.closeDiv = document.createElement("div");
+               this.closeDiv.className = "close";
+               this.closeDiv.innerHTML = '<img src="/images/icn_close.png" alt="Remove" title="Remove this metric from the report" />';
+               this.closeDiv.style.display = "none";
+               this.closeDiv.onmousedown = this.close.bind(this);    
+               
+//             headerDiv.appendChild(this.visibilityToggleDiv);
+               headerDiv.appendChild(this.closeDiv);
+               headerDiv.appendChild(this.titleDiv);
+       },
+
+       addToCol: function(col, isNew) {
+         this.column  = col;
+               if (isNew && toCol.hasChildNodes())
+                       this.column.insertBefore(this.panelDiv, this.column.firstChild);
+               else
+                       this.column.appendChild(this.panelDiv);
+       },
+       
+       moveToColumn: function(col){
+               if (this.column != col) {
+                       this.column.removeChild(this.panelDiv)
+                       this.column = col;
+                       col.appendChild(this.panelDiv);
+               }
+       },    
+    //this.obj.root.onDragStart(parseInt(panel.panelDiv.style.left), parseInt(pnel.panelDiv.style.top), 
+               //                          event.clientX, event.clientY);
+       _startDrag: function(event) {
+               if (this.dashboard.options.startingDrag)
+                       this.dashboard.options.startingDrag();
+
+               Position.absolutize(this.panelDiv)
+               this.dashboard.startInsertionOutline(this.panelDiv)
+               this.panelDiv.style.opacity = .7;
+               this.panelDiv.style.zIndex = 900;
+               //this.panelDiv.style.width = (parseInt(this.panelDiv.offestWidth)-4)+"px";
+               new DragPanel(this, event);
+               Event.stop(event);
+       },
+       
+       hover: function() {
+//             this.visibilityToggleDiv.style.visibility = "visible";
+               this.closeDiv.show();
+       },
+       
+       unHover: function() {
+//             this.visibilityToggleDiv.style.visibility = "hidden";
+               this.closeDiv.hide();
+       },
+       
+       setVisibility: function(visibility) {
+               if (visibility) {
+                       this.panelDiv.show(); 
+               } else {
+                  this.panelDiv.hide();
+               }
+       },
+       
+       toggleVisibility: function() {
+          this.setVisibility(this.panelContentDiv.style.display =='none');
+       },
+       
+       close: function() {
+    if (this.open)
+                 this.panelDiv.parentNode.removeChild(this.panelDiv);
+                 this.open = false;
+       },
+       
+       show: function() {
+               this.panelContentDiv.show();
+               this.visibilityToggleDiv.firstChild.setAttribute("src", "/images/bkgd_panel_arrow.png");
+       },
+
+       hide: function() {
+               this.panelContentDiv.hide();
+               this.visibilityToggleDiv.firstChild.setAttribute("src", "/images/bkgd_panel_arrow.png");
+       },  
+       
+       drop: function() {
+         this.dashboard.dropPanel(this);
+         
+         this.panelDiv.style.position = "static";
+               //this.panelDiv.style.width = "100%";
+               this.unHover();
+               this.panelDiv.style.opacity = 1;
+               if (this.dashboard.options.endingDrag)
+                       this.dashboard.options.endingDrag();
+       }
+}
+
+DragPanel = Class.create();
+DragPanel.prototype = {
+       initialize : function(panel,event){     
+           this.panel = panel;     
+                       this.lastMouseX = event.clientX;
+                       this.lastMouseY = event.clientY;
+                       this.dragHandler = this.drag.bindAsEventListener(this)
+                       this.dropHandler = this.endDrag.bindAsEventListener(this)
+                       Event.observe(document, "mousemove", this.dragHandler);
+                       Event.observe(document, "mouseup", this.dropHandler);
+                       this.panel.panelDiv.isDragging = true
+       },      
+       drag : function(event){
+
+         panelDiv = this.panel.panelDiv
+               var newLeft = parseInt(panelDiv.style.left) + event.clientX - this.lastMouseX;
+               var newTop = parseInt(panelDiv.style.top) + event.clientY - this.lastMouseY;
+               panelDiv.style.left = newLeft + "px";
+               panelDiv.style.top = newTop + "px";                     
+               this.lastMouseX = event.clientX;
+               this.lastMouseY = event.clientY;
+    this.panel.dashboard.dragPanel(this.panel, newLeft, newTop);
+    Event.stop(event);
+       },
+       endDrag : function(event){                      
+               Event.stopObserving(document, "mousemove", this.dragHandler);
+       Event.stopObserving(document, "mouseup", this.dropHandler);     
+               this.panel.drop();      
+               this.panel.panelDiv.style.zIndex = 1000;
+               this.panel.panelDiv.isDragging = false;
+               Event.stop(event);
+       }
+}
+
+Rico.includeLoaded('ricoDashboard.js');
diff --git a/NP_TrackBack/trunk/trackback/js/rico/ricoDragDrop.js b/NP_TrackBack/trunk/trackback/js/rico/ricoDragDrop.js
new file mode 100644 (file)
index 0000000..ce4f326
--- /dev/null
@@ -0,0 +1,770 @@
+/**
+  *
+  *  Copyright 2005 Sabre Airline Solutions
+  *
+  *  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.
+  **/
+//-------------------- ricoDragAndDrop.js
+Rico.DragAndDrop = Class.create();
+
+Rico.DragAndDrop.prototype = {
+
+   initialize: function() {
+      this.dropZones                = new Array();
+      this.draggables               = new Array();
+      this.currentDragObjects       = new Array();
+      this.dragElement              = null;
+      this.lastSelectedDraggable    = null;
+      this.currentDragObjectVisible = false;
+      this.interestedInMotionEvents = false;
+      this._mouseDown = this._mouseDownHandler.bindAsEventListener(this);
+      this._mouseMove = this._mouseMoveHandler.bindAsEventListener(this);
+      this._mouseUp = this._mouseUpHandler.bindAsEventListener(this);
+   },
+
+   registerDropZone: function(aDropZone) {
+      this.dropZones[ this.dropZones.length ] = aDropZone;
+   },
+
+   deregisterDropZone: function(aDropZone) {
+      var newDropZones = new Array();
+      var j = 0;
+      for ( var i = 0 ; i < this.dropZones.length ; i++ ) {
+         if ( this.dropZones[i] != aDropZone )
+            newDropZones[j++] = this.dropZones[i];
+      }
+
+      this.dropZones = newDropZones;
+   },
+
+   clearDropZones: function() {
+      this.dropZones = new Array();
+   },
+
+   registerDraggable: function( aDraggable ) {
+      this.draggables[ this.draggables.length ] = aDraggable;
+      this._addMouseDownHandler( aDraggable );
+   },
+
+   clearSelection: function() {
+      for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
+         this.currentDragObjects[i].deselect();
+      this.currentDragObjects = new Array();
+      this.lastSelectedDraggable = null;
+   },
+
+   hasSelection: function() {
+      return this.currentDragObjects.length > 0;
+   },
+
+   setStartDragFromElement: function( e, mouseDownElement ) {
+      this.origPos = RicoUtil.toDocumentPosition(mouseDownElement);
+      this.startx = e.screenX - this.origPos.x
+      this.starty = e.screenY - this.origPos.y
+      //this.startComponentX = e.layerX ? e.layerX : e.offsetX;
+      //this.startComponentY = e.layerY ? e.layerY : e.offsetY;
+      //this.adjustedForDraggableSize = false;
+
+      this.interestedInMotionEvents = this.hasSelection();
+      this._terminateEvent(e);
+   },
+
+   updateSelection: function( draggable, extendSelection ) {
+      if ( ! extendSelection )
+         this.clearSelection();
+
+      if ( draggable.isSelected() ) {
+         this.currentDragObjects.removeItem(draggable);
+         draggable.deselect();
+         if ( draggable == this.lastSelectedDraggable )
+            this.lastSelectedDraggable = null;
+      }
+      else {
+         this.currentDragObjects[ this.currentDragObjects.length ] = draggable;
+         draggable.select();
+         this.lastSelectedDraggable = draggable;
+      }
+   },
+
+   _mouseDownHandler: function(e) {
+      if ( arguments.length == 0 )
+         e = event;
+
+      // if not button 1 ignore it...
+      var nsEvent = e.which != undefined;
+      if ( (nsEvent && e.which != 1) || (!nsEvent && e.button != 1))
+         return;
+
+      var eventTarget      = e.target ? e.target : e.srcElement;
+      var draggableObject  = eventTarget.draggable;
+
+      var candidate = eventTarget;
+      while (draggableObject == null && candidate.parentNode) {
+         candidate = candidate.parentNode;
+         draggableObject = candidate.draggable;
+      }
+   
+      if ( draggableObject == null )
+         return;
+
+      this.updateSelection( draggableObject, e.ctrlKey );
+
+      // clear the drop zones postion cache...
+      if ( this.hasSelection() )
+         for ( var i = 0 ; i < this.dropZones.length ; i++ )
+            this.dropZones[i].clearPositionCache();
+
+      this.setStartDragFromElement( e, draggableObject.getMouseDownHTMLElement() );
+   },
+
+
+   _mouseMoveHandler: function(e) {
+      var nsEvent = e.which != undefined;
+      if ( !this.interestedInMotionEvents ) {
+         //this._terminateEvent(e);
+         return;
+      }
+
+      if ( ! this.hasSelection() )
+         return;
+
+      if ( ! this.currentDragObjectVisible )
+         this._startDrag(e);
+
+      if ( !this.activatedDropZones )
+         this._activateRegisteredDropZones();
+
+      //if ( !this.adjustedForDraggableSize )
+      //   this._adjustForDraggableSize(e);
+
+      this._updateDraggableLocation(e);
+      this._updateDropZonesHover(e);
+
+      this._terminateEvent(e);
+   },
+
+   _makeDraggableObjectVisible: function(e)
+   {
+      if ( !this.hasSelection() )
+         return;
+
+      var dragElement;
+      if ( this.currentDragObjects.length > 1 )
+         dragElement = this.currentDragObjects[0].getMultiObjectDragGUI(this.currentDragObjects);
+      else
+         dragElement = this.currentDragObjects[0].getSingleObjectDragGUI();
+
+      // go ahead and absolute position it...
+      if ( RicoUtil.getElementsComputedStyle(dragElement, "position")  != "absolute" )
+/*      if (Element.getStyle(dragElement,'position')=='absolute')*/
+         dragElement.style.position = "absolute";
+
+      // need to parent him into the document...
+      if ( dragElement.parentNode == null || dragElement.parentNode.nodeType == 11 )
+         document.body.appendChild(dragElement);
+
+      this.dragElement = dragElement;
+      this._updateDraggableLocation(e);
+
+      this.currentDragObjectVisible = true;
+   },
+
+   /**
+   _adjustForDraggableSize: function(e) {
+      var dragElementWidth  = this.dragElement.offsetWidth;
+      var dragElementHeight = this.dragElement.offsetHeight;
+      if ( this.startComponentX > dragElementWidth )
+         this.startx -= this.startComponentX - dragElementWidth + 2;
+      if ( e.offsetY ) {
+         if ( this.startComponentY > dragElementHeight )
+            this.starty -= this.startComponentY - dragElementHeight + 2;
+      }
+      this.adjustedForDraggableSize = true;
+   },
+   **/
+
+   _leftOffset: function(e) {
+          return e.offsetX ? document.body.scrollLeft : 0
+       },
+
+   _topOffset: function(e) {
+          return e.offsetY ? document.body.scrollTop:0
+       },
+
+               
+   _updateDraggableLocation: function(e) {
+      var dragObjectStyle = this.dragElement.style;
+      dragObjectStyle.left = (e.screenX + this._leftOffset(e) - this.startx) + "px"
+      dragObjectStyle.top  = (e.screenY + this._topOffset(e) - this.starty) + "px";
+   },
+
+   _updateDropZonesHover: function(e) {
+      var n = this.dropZones.length;
+      for ( var i = 0 ; i < n ; i++ ) {
+         if ( ! this._mousePointInDropZone( e, this.dropZones[i] ) )
+            this.dropZones[i].hideHover();
+      }
+
+      for ( var i = 0 ; i < n ; i++ ) {
+         if ( this._mousePointInDropZone( e, this.dropZones[i] ) ) {
+            if ( this.dropZones[i].canAccept(this.currentDragObjects) )
+               this.dropZones[i].showHover();
+         }
+      }
+   },
+
+   _startDrag: function(e) {
+      for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
+         this.currentDragObjects[i].startDrag();
+
+      this._makeDraggableObjectVisible(e);
+   },
+
+   _mouseUpHandler: function(e) {
+      if ( ! this.hasSelection() )
+         return;
+
+      var nsEvent = e.which != undefined;
+      if ( (nsEvent && e.which != 1) || (!nsEvent && e.button != 1))
+         return;
+
+      this.interestedInMotionEvents = false;
+
+      if ( this.dragElement == null ) {
+         this._terminateEvent(e);
+         return;
+      }
+
+      if ( this._placeDraggableInDropZone(e) )
+         this._completeDropOperation(e);
+      else {
+         this._terminateEvent(e);
+         Rico.animate(new Rico.Effect.Position( this.dragElement, this.origPos.x, this.origPos.y),
+                      {duration: 200,
+                       steps: 20,
+                       onFinish : this._doCancelDragProcessing.bind(this) } );
+      }
+
+     Event.stopObserving(document.body, "mousemove", this._mouseMove);
+     Event.stopObserving(document.body, "mouseup",  this._mouseUp);
+   },
+
+   _retTrue: function () {
+      return true;
+   },
+
+   _completeDropOperation: function(e) {
+      if ( this.dragElement != this.currentDragObjects[0].getMouseDownHTMLElement() ) {
+         if ( this.dragElement.parentNode != null )
+            this.dragElement.parentNode.removeChild(this.dragElement);
+      }
+
+      this._deactivateRegisteredDropZones();
+      this._endDrag();
+      this.clearSelection();
+      this.dragElement = null;
+      this.currentDragObjectVisible = false;
+      this._terminateEvent(e);
+   },
+
+   _doCancelDragProcessing: function() {
+      this._cancelDrag();
+
+        if ( this.dragElement != this.currentDragObjects[0].getMouseDownHTMLElement() && this.dragElement)
+           if ( this.dragElement.parentNode != null )
+              this.dragElement.parentNode.removeChild(this.dragElement);
+
+
+      this._deactivateRegisteredDropZones();
+      this.dragElement = null;
+      this.currentDragObjectVisible = false;
+   },
+
+   _placeDraggableInDropZone: function(e) {
+      var foundDropZone = false;
+      var n = this.dropZones.length;
+      for ( var i = 0 ; i < n ; i++ ) {
+         if ( this._mousePointInDropZone( e, this.dropZones[i] ) ) {
+            if ( this.dropZones[i].canAccept(this.currentDragObjects) ) {
+               this.dropZones[i].hideHover();
+               this.dropZones[i].accept(this.currentDragObjects);
+               foundDropZone = true;
+               break;
+            }
+         }
+      }
+
+      return foundDropZone;
+   },
+
+   _cancelDrag: function() {
+      for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
+         this.currentDragObjects[i].cancelDrag();
+   },
+
+   _endDrag: function() {
+      for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
+         this.currentDragObjects[i].endDrag();
+   },
+
+   _mousePointInDropZone: function( e, dropZone ) {
+
+      var absoluteRect = dropZone.getAbsoluteRect();
+
+      return e.clientX  > absoluteRect.left + this._leftOffset(e) &&
+             e.clientX  < absoluteRect.right + this._leftOffset(e) &&
+             e.clientY  > absoluteRect.top + this._topOffset(e)   &&
+             e.clientY  < absoluteRect.bottom + this._topOffset(e);
+   },
+
+   _addMouseDownHandler: function( aDraggable )
+   {
+       htmlElement  = aDraggable.getMouseDownHTMLElement();
+      if ( htmlElement  != null ) { 
+         htmlElement.draggable = aDraggable;
+         Event.observe(htmlElement , "mousedown", this._onmousedown.bindAsEventListener(this));
+         Event.observe(htmlElement, "mousedown", this._mouseDown);
+      }
+   },
+
+   _activateRegisteredDropZones: function() {
+      var n = this.dropZones.length;
+      for ( var i = 0 ; i < n ; i++ ) {
+         var dropZone = this.dropZones[i];
+         if ( dropZone.canAccept(this.currentDragObjects) )
+            dropZone.activate();
+      }
+
+      this.activatedDropZones = true;
+   },
+
+   _deactivateRegisteredDropZones: function() {
+      var n = this.dropZones.length;
+      for ( var i = 0 ; i < n ; i++ )
+         this.dropZones[i].deactivate();
+      this.activatedDropZones = false;
+   },
+
+   _onmousedown: function () {
+     Event.observe(document.body, "mousemove", this._mouseMove);
+     Event.observe(document.body, "mouseup",  this._mouseUp);
+   },
+
+   _terminateEvent: function(e) {
+      if ( e.stopPropagation != undefined )
+         e.stopPropagation();
+      else if ( e.cancelBubble != undefined )
+         e.cancelBubble = true;
+
+      if ( e.preventDefault != undefined )
+         e.preventDefault();
+      else
+         e.returnValue = false;
+   },
+
+
+          initializeEventHandlers: function() {
+             if ( typeof document.implementation != "undefined" &&
+                document.implementation.hasFeature("HTML",   "1.0") &&
+                document.implementation.hasFeature("Events", "2.0") &&
+                document.implementation.hasFeature("CSS",    "2.0") ) {
+                document.addEventListener("mouseup",   this._mouseUpHandler.bindAsEventListener(this),  false);
+                document.addEventListener("mousemove", this._mouseMoveHandler.bindAsEventListener(this), false);
+             }
+             else {
+                document.attachEvent( "onmouseup",   this._mouseUpHandler.bindAsEventListener(this) );
+                document.attachEvent( "onmousemove", this._mouseMoveHandler.bindAsEventListener(this) );
+             }
+          }
+       }
+
+       var dndMgr = new Rico.DragAndDrop();
+       dndMgr.initializeEventHandlers();
+
+
+//-------------------- ricoDraggable.js
+Rico.Draggable = Class.create();
+
+Rico.Draggable.prototype = {
+
+   initialize: function( type, htmlElement ) {
+      this.type          = type;
+      this.htmlElement   = $(htmlElement);
+      this.selected      = false;
+   },
+
+   /**
+    *   Returns the HTML element that should have a mouse down event
+    *   added to it in order to initiate a drag operation
+    *
+    **/
+   getMouseDownHTMLElement: function() {
+      return this.htmlElement;
+   },
+
+   select: function() {
+      this.selected = true;
+
+      if ( this.showingSelected )
+         return;
+
+      var htmlElement = this.getMouseDownHTMLElement();
+
+      var color = Rico.Color.createColorFromBackground(htmlElement);
+      color.isBright() ? color.darken(0.033) : color.brighten(0.033);
+
+      this.saveBackground = RicoUtil.getElementsComputedStyle(htmlElement, "backgroundColor", "background-color");
+//      this.saveBackground = Element.getStyle(htmlElement,'backgroundColor') || Element.getStyle(htmlElement,'background-color')
+      htmlElement.style.backgroundColor = color.asHex();
+      this.showingSelected = true;
+   },
+
+   deselect: function() {
+      this.selected = false;
+      if ( !this.showingSelected )
+         return;
+
+      var htmlElement = this.getMouseDownHTMLElement();
+
+      htmlElement.style.backgroundColor = this.saveBackground;
+      this.showingSelected = false;
+   },
+
+   isSelected: function() {
+      return this.selected;
+   },
+
+   startDrag: function() {
+   },
+
+   cancelDrag: function() {
+   },
+
+   endDrag: function() {
+   },
+
+   getSingleObjectDragGUI: function() {
+      return this.htmlElement;
+   },
+
+   getMultiObjectDragGUI: function( draggables ) {
+      return this.htmlElement;
+   },
+
+   getDroppedGUI: function() {
+      return this.htmlElement;
+   },
+
+   toString: function() {
+      return this.type + ":" + this.htmlElement + ":";
+   }
+
+}
+
+
+//-------------------- ricoDropzone.js
+Rico.Dropzone = Class.create();
+
+Rico.Dropzone.prototype = {
+
+   initialize: function( htmlElement ) {
+      this.htmlElement  = $(htmlElement);
+      this.absoluteRect = null;
+   },
+
+   getHTMLElement: function() {
+      return this.htmlElement;
+   },
+
+   clearPositionCache: function() {
+      this.absoluteRect = null;
+   },
+
+   getAbsoluteRect: function() {
+      if ( this.absoluteRect == null ) {
+         var htmlElement = this.getHTMLElement();
+         var pos = RicoUtil.toViewportPosition(htmlElement);
+
+         this.absoluteRect = {
+            top:    pos.y,
+            left:   pos.x,
+            bottom: pos.y + htmlElement.offsetHeight,
+            right:  pos.x + htmlElement.offsetWidth
+         };
+      }
+      return this.absoluteRect;
+   },
+
+   activate: function() {
+      var htmlElement = this.getHTMLElement();
+      if (htmlElement == null  || this.showingActive)
+         return;
+
+      this.showingActive = true;
+      this.saveBackgroundColor = htmlElement.style.backgroundColor;
+
+      var fallbackColor = "#ffea84";
+      var currentColor = Rico.Color.createColorFromBackground(htmlElement);
+      if ( currentColor == null )
+         htmlElement.style.backgroundColor = fallbackColor;
+      else {
+         currentColor.isBright() ? currentColor.darken(0.2) : currentColor.brighten(0.2);
+         htmlElement.style.backgroundColor = currentColor.asHex();
+      }
+   },
+
+   deactivate: function() {
+      var htmlElement = this.getHTMLElement();
+      if (htmlElement == null || !this.showingActive)
+         return;
+
+      htmlElement.style.backgroundColor = this.saveBackgroundColor;
+      this.showingActive = false;
+      this.saveBackgroundColor = null;
+   },
+
+   showHover: function() {
+      var htmlElement = this.getHTMLElement();
+      if ( htmlElement == null || this.showingHover )
+         return;
+
+      this.saveBorderWidth = htmlElement.style.borderWidth;
+      this.saveBorderStyle = htmlElement.style.borderStyle;
+      this.saveBorderColor = htmlElement.style.borderColor;
+
+      this.showingHover = true;
+      htmlElement.style.borderWidth = "1px";
+      htmlElement.style.borderStyle = "solid";
+      //htmlElement.style.borderColor = "#ff9900";
+      htmlElement.style.borderColor = "#ffff00";
+   },
+
+   hideHover: function() {
+      var htmlElement = this.getHTMLElement();
+      if ( htmlElement == null || !this.showingHover )
+         return;
+
+      htmlElement.style.borderWidth = this.saveBorderWidth;
+      htmlElement.style.borderStyle = this.saveBorderStyle;
+      htmlElement.style.borderColor = this.saveBorderColor;
+      this.showingHover = false;
+   },
+
+   canAccept: function(draggableObjects) {
+      return true;
+   },
+
+   accept: function(draggableObjects) {
+      var htmlElement = this.getHTMLElement();
+      if ( htmlElement == null )
+         return;
+
+      n = draggableObjects.length;
+      for ( var i = 0 ; i < n ; i++ )
+      {
+         var theGUI = draggableObjects[i].getDroppedGUI();
+/*         if (Element.getStyle(theGUI,'position')=='absolute')*/
+         if ( RicoUtil.getElementsComputedStyle( theGUI, "position" ) == "absolute" )
+         {
+            theGUI.style.position = "static";
+            theGUI.style.top = "";
+            theGUI.style.top = "";
+         }
+         htmlElement.appendChild(theGUI);
+      }
+   }
+}
+
+RicoUtil = Object.extend(RicoUtil, {
+   getElementsComputedStyle: function ( htmlElement, cssProperty, mozillaEquivalentCSS) {
+      if ( arguments.length == 2 )
+         mozillaEquivalentCSS = cssProperty;
+
+      var el = $(htmlElement);
+      if ( el.currentStyle )
+         return el.currentStyle[cssProperty];
+      else
+         return document.defaultView.getComputedStyle(el, null).getPropertyValue(mozillaEquivalentCSS);
+   },
+
+   createXmlDocument : function() {
+      if (document.implementation && document.implementation.createDocument) {
+         var doc = document.implementation.createDocument("", "", null);
+
+         if (doc.readyState == null) {
+            doc.readyState = 1;
+            doc.addEventListener("load", function () {
+               doc.readyState = 4;
+               if (typeof doc.onreadystatechange == "function")
+                  doc.onreadystatechange();
+            }, false);
+         }
+
+         return doc;
+      }
+
+      if (window.ActiveXObject)
+          return Try.these(
+            function() { return new ActiveXObject('MSXML2.DomDocument')   },
+            function() { return new ActiveXObject('Microsoft.DomDocument')},
+            function() { return new ActiveXObject('MSXML.DomDocument')    },
+            function() { return new ActiveXObject('MSXML3.DomDocument')   }
+          ) || false;
+
+      return null;
+   },
+
+   getContentAsString: function( parentNode ) {
+      return parentNode.xml != undefined ? 
+         this._getContentAsStringIE(parentNode) :
+         this._getContentAsStringMozilla(parentNode);
+   },
+
+  _getContentAsStringIE: function(parentNode) {
+     var contentStr = "";
+     for ( var i = 0 ; i < parentNode.childNodes.length ; i++ ) {
+         var n = parentNode.childNodes[i];
+         if (n.nodeType == 4) {
+             contentStr += n.nodeValue;
+         }
+         else {
+           contentStr += n.xml;
+       }
+     }
+     return contentStr;
+  },
+
+  _getContentAsStringMozilla: function(parentNode) {
+     var xmlSerializer = new XMLSerializer();
+     var contentStr = "";
+     for ( var i = 0 ; i < parentNode.childNodes.length ; i++ ) {
+          var n = parentNode.childNodes[i];
+          if (n.nodeType == 4) { // CDATA node
+              contentStr += n.nodeValue;
+          }
+          else {
+            contentStr += xmlSerializer.serializeToString(n);
+        }
+     }
+     return contentStr;
+  },
+
+   toViewportPosition: function(element) {
+      return this._toAbsolute(element,true);
+   },
+
+   toDocumentPosition: function(element) {
+      return this._toAbsolute(element,false);
+   },
+
+   /**
+    *  Compute the elements position in terms of the window viewport
+    *  so that it can be compared to the position of the mouse (dnd)
+    *  This is additions of all the offsetTop,offsetLeft values up the
+    *  offsetParent hierarchy, ...taking into account any scrollTop,
+    *  scrollLeft values along the way...
+    *
+    * IE has a bug reporting a correct offsetLeft of elements within a
+    * a relatively positioned parent!!!
+    **/
+   _toAbsolute: function(element,accountForDocScroll) {
+
+      if ( navigator.userAgent.toLowerCase().indexOf("msie") == -1 )
+         return this._toAbsoluteMozilla(element,accountForDocScroll);
+
+      var x = 0;
+      var y = 0;
+      var parent = element;
+      while ( parent ) {
+
+         var borderXOffset = 0;
+         var borderYOffset = 0;
+         if ( parent != element ) {
+            var borderXOffset = parseInt(this.getElementsComputedStyle(parent, "borderLeftWidth" ));
+            var borderYOffset = parseInt(this.getElementsComputedStyle(parent, "borderTopWidth" ));
+            borderXOffset = isNaN(borderXOffset) ? 0 : borderXOffset;
+            borderYOffset = isNaN(borderYOffset) ? 0 : borderYOffset;
+         }
+
+         x += parent.offsetLeft - parent.scrollLeft + borderXOffset;
+         y += parent.offsetTop - parent.scrollTop + borderYOffset;
+         parent = parent.offsetParent;
+      }
+
+      if ( accountForDocScroll ) {
+         x -= this.docScrollLeft();
+         y -= this.docScrollTop();
+      }
+
+      return { x:x, y:y };
+   },
+
+   /**
+    *  Mozilla did not report all of the parents up the hierarchy via the
+    *  offsetParent property that IE did.  So for the calculation of the
+    *  offsets we use the offsetParent property, but for the calculation of
+    *  the scrollTop/scrollLeft adjustments we navigate up via the parentNode
+    *  property instead so as to get the scroll offsets...
+    *
+    **/
+   _toAbsoluteMozilla: function(element,accountForDocScroll) {
+      var x = 0;
+      var y = 0;
+      var parent = element;
+      while ( parent ) {
+         x += parent.offsetLeft;
+         y += parent.offsetTop;
+         parent = parent.offsetParent;
+      }
+
+      parent = element;
+      while ( parent &&
+              parent != document.body &&
+              parent != document.documentElement ) {
+         if ( parent.scrollLeft  )
+            x -= parent.scrollLeft;
+         if ( parent.scrollTop )
+            y -= parent.scrollTop;
+         parent = parent.parentNode;
+      }
+
+      if ( accountForDocScroll ) {
+         x -= this.docScrollLeft();
+         y -= this.docScrollTop();
+      }
+
+      return { x:x, y:y };
+   },
+
+   docScrollLeft: function() {
+      if ( window.pageXOffset )
+         return window.pageXOffset;
+      else if ( document.documentElement && document.documentElement.scrollLeft )
+         return document.documentElement.scrollLeft;
+      else if ( document.body )
+         return document.body.scrollLeft;
+      else
+         return 0;
+   },
+
+   docScrollTop: function() {
+      if ( window.pageYOffset )
+         return window.pageYOffset;
+      else if ( document.documentElement && document.documentElement.scrollTop )
+         return document.documentElement.scrollTop;
+      else if ( document.body )
+         return document.body.scrollTop;
+      else
+         return 0;
+   }
+});
+
+Rico.includeLoaded('ricoDragDrop.js');
diff --git a/NP_TrackBack/trunk/trackback/js/rico/ricoEffects.js b/NP_TrackBack/trunk/trackback/js/rico/ricoEffects.js
new file mode 100644 (file)
index 0000000..608bb60
--- /dev/null
@@ -0,0 +1,389 @@
+ /**
+   *  (c) 2005-2007 Richard Cowin (http://openrico.org)
+   *
+   *  Rico is 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
+   **/
+
+Rico.animate = function(effect){
+       new Rico.Effect.Animator().play(effect, arguments[1]);
+}
+
+Rico.Effect = {}
+Rico.Effect.easeIn = function(step){
+  return Math.sqrt(step)
+}
+Rico.Effect.easeOut = function(step){
+  return step*step
+}
+Rico.Stepping = {}
+Rico.Stepping.easeIn = Rico.Effect.easeIn;
+Rico.Stepping.easeOut = Rico.Effect.easeOut;
+
+Rico.Effect.Animator = Class.create();
+Rico.Effect.Animator.prototype = {
+       initialize : function(effect) {
+               this.animateMethod = this.animate.bind(this);
+               this.options = arguments[1] || {};
+               this.stepsLeft = 0;
+               if (!effect) return;
+               this.reset(effect, arguments[1]);
+       },
+       reset: function(effect){
+               this.effect = effect;
+    if (arguments[1]) this.setOptions(arguments[1]);
+               this.stepsLeft = this.options.steps;
+               this.duration = this.options.duration;
+       },
+       setOptions: function(options){
+         this.options = Object.extend({
+                       steps: 10,
+                       duration: 200,
+                       rate: function(steps){ return steps;}
+    }, options|| {});
+       },
+       play: function(effect) {
+         this.setOptions(arguments[1])
+         if (effect)
+         if (effect.step)
+                 this.reset(effect, arguments[1]);
+               else{
+                 $H(effect).keys().each((function(e){
+                   var effectClass = {fadeOut:Rico.Effect.FadeOut}[e];
+                   this.reset(new effectClass(effect[e]));
+                 }).bind(this))                  
+               }
+               this.animate();
+       },
+       stop: function() {
+               this.stepsLeft = this.options.steps;
+       },
+       pause: function() {
+               this.interupt = true;
+       },
+       resume: function() {
+         this.interupt = false;
+         if (this.stepsLeft >0)
+           this.animate();
+       },
+       animate: function() {
+         if (this.interupt)
+           return;
+               if (this.stepsLeft <=0) {
+                       if (this.effect.finish)  this.effect.finish();
+                       if (this.options.onFinish) this.options.onFinish();
+                       return;
+               }
+               if (this.timer)
+                       clearTimeout(this.timer);
+               this.effect.step(this.options.rate(this.stepsLeft));
+               this.startNextStep();
+  },
+       startNextStep: function() {
+               var stepDuration = Math.round(this.duration/this.stepsLeft) ;
+    this.duration -= stepDuration;
+    this.stepsLeft--;
+    this.timer = setTimeout(this.animateMethod, stepDuration);
+       },
+  isPlaying: function(){
+    return this.stepsLeft != 0 && !this.interupt;
+  }
+}
+
+Rico.Effect.Group = Class.create();
+Rico.Effect.Group.prototype = {
+  initialize: function(effects){
+    this.effects = effects;
+  },
+  step: function(stepsToGo){ 
+    this.effects.each(function(e){e.step(stepsToGo)});
+  },
+  finish: function(){
+    this.effects.each(function(e){if (e.finish) e.finish()});
+  }
+}
+
+Rico.Effect.SizeAndPosition = Class.create();
+Rico.Effect.SizeAndPosition.prototype = {
+   initialize: function(element, x, y, w, h) {
+      this.element = $(element);
+      this.x = x || this.element.offsetLeft;
+      this.y = y || this.element.offsetTop;
+      this.w = w || this.element.offsetWidth;
+      this.h = h || this.element.offsetHeight;
+   },
+   step: function(stepsToGo) {  
+                       var left = this.element.offsetLeft + ((this.x - this.element.offsetLeft)/stepsToGo) + "px"
+                       var top = this.element.offsetTop + ((this.y - this.element.offsetTop)/stepsToGo) + "px"
+                       var width = this.element.offsetWidth + ((this.w - this.element.offsetWidth)/stepsToGo) + "px"
+                       var height = this.element.offsetHeight + ((this.h - this.element.offsetHeight)/stepsToGo) + "px"
+      var style = this.element.style;
+                       style.left = left;
+                       style.top = top;
+                       style.width = width;
+                       style.height = height;
+   }
+}
+
+Rico.AccordionEffect = Class.create();
+Rico.AccordionEffect.prototype = {
+  initialize: function(toClose, toOpen, height) {
+    this.toClose   = toClose;
+    this.toOpen    = toOpen;
+/*    if (!navigator.appVersion.match(/\bMSIE\b/)) {*/
+      Element.makeClipping(toOpen);
+      Element.makeClipping(toClose);
+/*    }*/
+    Rico.Controls.disableNativeControls(toClose);
+    Element.show(toOpen);
+    this.toOpen.style.height = "0px";
+    this.endHeight = height;
+  },
+  step: function(framesLeft) {
+     var cHeight = Math.max(1,this.toClose.offsetHeight - parseInt((parseInt(this.toClose.offsetHeight))/framesLeft));
+     var closeHeight = cHeight + "px";
+     var openHeight = (this.endHeight - cHeight) + "px"
+     this.toClose.style.height = closeHeight;
+     this.toOpen.style.height = openHeight;
+  },
+  finish: function(){
+    Element.hide(this.toClose)
+    this.toOpen.style.height = this.endHeight + "px";
+    this.toClose.style.height = "0px";
+/*    if (!navigator.appVersion.match(/\bMSIE\b/)) {*/
+      Element.undoClipping(this.toOpen);
+      Element.undoClipping(this.toClose);
+/*    }*/
+
+               Rico.Controls.enableNativeControls(this.toOpen);
+  }
+};
+
+Rico.Effect.SizeFromBottom = Class.create()
+Rico.Effect.SizeFromBottom.prototype = {
+  initialize: function(element, y, h) {
+    this.element = $(element);
+    this.y = y || this.element.offsetTop;
+    this.h = h || this.element.offsetHeight;
+    this.options  = arguments[3] || {};
+  },
+  step: function(framesToGo) {  
+               var top = this.element.offsetTop + ((this.y - this.element.offsetTop)/framesToGo) + "px"
+               var height = this.element.offsetHeight + ((this.h - this.element.offsetHeight)/framesToGo) + "px"
+    var style = this.element.style;
+               style.height = height;     
+               style.top = top;
+  }
+}
+
+Rico.Effect.Position = Class.create();
+Rico.Effect.Position.prototype = {
+  initialize: function(element, x, y) {
+    this.element = $(element);
+    this.x = x || this.element.offsetLeft;
+    this.destTop = y || this.element.offsetTop;
+  },
+  step: function(stepsToGo) {  
+       var left = this.element.offsetLeft + ((this.x - this.element.offsetLeft)/stepsToGo) + "px"
+       var top = this.element.offsetTop + ((this.destTop - this.element.offsetTop)/stepsToGo) + "px"
+    var style = this.element.style;
+       style.left = left;
+       style.top = top;
+  }
+}
+
+Rico.Effect.FadeTo = Class.create()
+Rico.Effect.FadeTo.prototype = {
+  initialize: function(element, value){
+    this.element = element;
+    this.opacity = Element.getStyle(this.element, 'opacity') || 1.0;
+    this.target = Math.min(value, 1.0);
+  },
+  step: function(framesLeft) {
+    var curOpacity = Element.getStyle(this.element, 'opacity');
+    var newOpacity = curOpacity + (this.target - curOpacity)/framesLeft
+    Rico.Effect.setOpacity(this.element, Math.min(Math.max(0,newOpacity),1.0));
+  }
+}
+
+Rico.Effect.FadeOut = Class.create()
+Rico.Effect.FadeOut.prototype = {
+  initialize: function(element){
+    this.effect = new Rico.Effect.FadeTo(element, 0.0)
+  },
+  step: function(framesLeft) {
+    this.effect.step(framesLeft);
+  }
+}
+
+Rico.Effect.FadeIn = Class.create()
+Rico.Effect.FadeIn.prototype = {
+  initialize: function(element){
+    var options = arguments[1] || {}
+    var startValue = options.startValue || 0
+    Element.setStyle(element, 'opacity', startValue);
+    this.effect = new Rico.Effect.FadeTo(element, 1.0)
+  },
+  step: function(framesLeft) {
+    this.effect.step(framesLeft);
+  }
+}
+
+Rico.Effect.setOpacity= function(element, value) {
+   element.style.filter = "alpha(opacity:"+Math.round(value*100)+")";
+   element.style.opacity = value; 
+}
+
+Rico.Effect.SizeFromTop = Class.create()
+Rico.Effect.SizeFromTop.prototype = {
+  initialize: function(element, scrollElement, y, h) {
+     this.element = $(element);
+     this.h = h || this.element.offsetHeight;
+       //       element.style.top = y;
+     this.scrollElement = scrollElement;
+     this.options  = arguments[4] || {};
+     this.baseHeight = this.options.baseHeight ||  Math.max(this.h, this.element.offsetHeight)
+  },
+  step: function(framesToGo) {  
+    var rawHeight = this.element.offsetHeight + ((this.h - this.element.offsetHeight)/framesToGo);
+               var height = rawHeight + "px"
+               var scroll = (rawHeight - this.baseHeight) + "px";
+               this.scrollElement.style.top = scroll;
+               this.element.style.height = height;     
+  }
+}
+
+
+Rico.Effect.Height = Class.create()
+Rico.Effect.Height.prototype = {
+  initialize: function(element, endHeight) {
+    this.element = element
+               this.endHeight = endHeight
+  },
+  step: function(stepsLeft) {
+    if (this.element.constructor != Array){
+      var height = this.element.offsetHeight + ((this.endHeight - this.element.offsetHeight)/stepsLeft) + "px"
+      this.element.style.height = height;
+    } else {
+      var height = this.element[0].offsetHeight + ((this.endHeight - this.element[0].offsetHeight)/stepsLeft) + "px"
+      this.element.each(function(e){e.style.height = height})
+    }
+  }
+}
+
+Rico.Effect.SizeWidth = Class.create();
+Rico.Effect.SizeWidth.prototype = {
+    initialize: function(element, endWidth) {
+      this.element = element
+                       this.endWidth = endWidth
+    },
+    step: function(stepsLeft) {
+       delta = Math.abs(this.endWidth - parseInt(this.element.offsetWidth))/(stepsLeft);
+       this.element.style.width = (this.element.offsetWidth - delta) + "px";
+    }
+}
+
+//these are to support non Safari browsers and keep controls from bleeding through on absolute positioned element.
+Rico.Controls = {
+       editors: [],
+       scrollSelectors: [],
+       
+       disableNativeControls: function(element) {
+               Rico.Controls.defaultDisabler.disableNative(element);
+  },
+       enableNativeControls: function(element){
+               Rico.Controls.defaultDisabler.enableNative(element);
+       },
+       prepareForSizing: function(element){
+    Element.makeClipping(element)
+    Rico.Controls.disableNativeControls(element)
+  },
+  resetSizing: function(element){
+    Element.undoClipping(element)
+    Rico.Controls.enableNativeControls(element)
+       },
+       registerScrollSelectors: function(selectorSet) {
+         selectorSet.each(function(s){Rico.Controls.scrollSelectors.push(Rico.selector(s))});
+       }
+}
+
+Rico.Controls.Disabler = Class.create();
+Rico.Controls.Disabler.prototype = {
+       initialize: function(){
+               this.options = Object.extend({
+                       excludeSet: [],
+                       hidables: Rico.Controls.editors
+    }, arguments[0] || {});
+       },
+  disableNative: function(element) {
+    if (!(/Konqueror|Safari|KHTML/.test(navigator.userAgent))){
+                       if (!navigator.appVersion.match(/\bMSIE\b/))
+                               this.blockControls(element).each(function(e){Element.makeClipping(e)});
+                       else
+                         this.hidableControls(element).each(function(e){e.disable()});
+    }
+  },
+  enableNative: function(element){
+    if (!(/Konqueror|Safari|KHTML/.test(navigator.userAgent))){
+                       if (!navigator.appVersion.match(/\bMSIE\b/))
+                               this.blockControls(element).each(function(e){Element.undoClipping(e)});
+                       else
+                         this.hidableControls(element).each(function(e){e.enable()});
+    }
+  },
+       blockControls: function(element){
+         try{
+               var includes = [];
+               if (this.options.includeSet)
+                       includes = this.options.includeSet;
+               else{
+                 var selectors = this.options.includeSelectors || Rico.Controls.scrollSelectors;
+                       includes = selectors.map(function(s){return s.findAll(element)}).flatten();
+    }
+               return includes.select(function(e){return (Element.getStyle(e, 'display') != 'none') && !this.options.excludeSet.include(e)}.bind(this));
+  }catch(e) { return []}
+       },
+       hidableControls: function(element){
+               if (element)
+                       return this.options.hidables.select(function(e){return Element.childOf(e, element)});
+               else
+                       return this.options.hidables;
+       }
+}      
+                    
+Rico.Controls.defaultDisabler = new Rico.Controls.Disabler();
+Rico.Controls.blankDisabler = new Rico.Controls.Disabler({includeSet:[],hidables:[]});
+                 
+Rico.Controls.HidableInput = Class.create(); 
+Rico.Controls.HidableInput.prototype = {
+       initialize: function(field, view){      
+               this.field = field;
+               this.view = view;
+               this.enable();
+               Rico.Controls.editors.push(this);
+       },
+       enable: function(){
+               Element.hide(this.view);
+               Element.show(this.field);
+       },
+       disable: function(){
+               this.view.value = $F(this.field);
+               if (this.field.offsetWidth > 1) {
+           this.view.style.width =  parseInt(this.field.offsetWidth)  + "px";
+                 Element.hide(this.field);
+                 Element.show(this.view);
+         }
+       }
+}
+
+
+
+Element.forceRefresh = function(item) {
+  try {
+    var n = document.createTextNode(' ')
+    item.appendChild(n); item.removeChild(n);
+  } catch(e) { }
+};
+
+Rico.includeLoaded('ricoEffects.js');
diff --git a/NP_TrackBack/trunk/trackback/js/rico/ricoGridCommon.js b/NP_TrackBack/trunk/trackback/js/rico/ricoGridCommon.js
new file mode 100644 (file)
index 0000000..01f7392
--- /dev/null
@@ -0,0 +1,685 @@
+/**
+  *  (c) 2005-2007 Richard Cowin (http://openrico.org)
+  *  (c) 2005-2007 Matt Brown (http://dowdybrown.com)
+  *
+  *  Rico is 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
+  **/
+
+
+if(typeof Rico=='undefined') throw("GridCommon requires the Rico JavaScript framework");
+if(typeof RicoUtil=='undefined') throw("GridCommon requires the RicoUtil Library");
+
+
+/**
+ * Define methods that are common to both SimpleGrid and LiveGrid
+ */
+Rico.GridCommon = function() {};
+
+Rico.GridCommon.prototype = {
+
+  baseInit: function() {
+    this.options = {
+      resizeBackground : 'resize.gif',
+      saveColumnInfo   : {width:true, filter:false, sort:false},  // save info in cookies?
+      allowColResize   : true,      // allow user to resize columns
+      windowResize     : true,      // Resize grid on window.resize event? Set to false when embedded in an accordian.
+      click            : null,
+      dblclick         : null,
+      contextmenu      : null,
+      useUnformattedColWidth : true,
+      menuEvent        : 'dblclick',  // event that triggers menus - click, dblclick, contextmenu, or none (no menus)
+      defaultWidth     : 100,   // in the absence of any other width info, columns will be this many pixels wide
+      scrollBarWidth   : 19,    // this is the value used in positioning calculations, it does not actually change the width of the scrollbar
+      minScrollWidth   : 100,   // min scroll area width when width of frozen columns exceeds window width
+      columnSpecs      : []
+    }
+    this.colWidths = new Array();
+    this.hdrCells=new Array();
+    this.headerColCnt=0;
+    this.headerRowIdx=0;
+    this.tabs=new Array(2);
+    this.thead=new Array(2);
+    this.tbody=new Array(2);
+  },
+
+  attachMenuEvents: function() {
+    if (!this.options.menuEvent || this.options.menuEvent=='none') return;
+    this.hideScroll=navigator.userAgent.match(/Macintosh\b.*\b(Firefox|Camino)\b/i) || Prototype.Browser.Opera;
+    this.options[this.options.menuEvent]=this.handleMenuClick.bindAsEventListener(this);
+    if (this.highlightDiv) {
+      switch (this.options.highlightElem) {
+        case 'cursorRow':
+          this.attachMenu(this.highlightDiv);
+          break;
+        case 'cursorCell':
+          for (var i=0; i<2; i++)
+            this.attachMenu(this.highlightDiv[i]);
+          break;
+      }
+    }
+    for (var i=0; i<2; i++)
+      this.attachMenu(this.tbody[i]);
+  },
+
+  attachMenu: function(elem) {
+    if (this.options.click)
+      Event.observe(elem, 'click', this.options.click, false);
+    if (this.options.dblclick) {
+      if (Prototype.Browser.WebKit || Prototype.Browser.Opera)
+        Event.observe(elem, 'click', this.handleDblClick.bindAsEventListener(this), false);
+      else
+        Event.observe(elem, 'dblclick', this.options.dblclick, false);
+    }
+    if (this.options.contextmenu) {
+      if (Prototype.Browser.Opera)
+        Event.observe(elem, 'click', this.handleContextMenu.bindAsEventListener(this), false);
+      else
+        Event.observe(elem, 'contextmenu', this.options.contextmenu, false);
+    }
+  },
+
+  // implement double-click for browsers that don't support a double-click event (e.g. Safari)
+  handleDblClick: function(e) {
+    var elem=Event.element(e);
+    if (this.dblClickElem == elem) {
+      this.options.dblclick(e);
+    } else {
+      this.dblClickElem = elem;
+      this.safariTimer=setTimeout(this.clearDblClick.bind(this),300);
+    }
+  },
+
+  clearDblClick: function() {
+    this.dblClickElem=null;
+  },
+
+  // implement right-click for browsers that don't support contextmenu event (e.g. Opera)
+  // use control-click instead
+  handleContextMenu: function(e) {
+    if( typeof( e.which ) == 'number' )
+      var b = e.which; //Netscape compatible
+    else if( typeof( e.button ) == 'number' )
+      var b = e.button; //DOM
+    else
+      return;
+    if (b==1 && e.ctrlKey)
+      this.options.contextmenu(e);
+  },
+
+  cancelMenu: function() {
+    if (this.menu && this.menu.isVisible()) this.menu.cancelmenu();
+  },
+
+  // gather info from original headings
+  getColumnInfo: function(hdrSrc) {
+    Rico.writeDebugMsg("getColumnInfo start");
+    //alert(hdrSrc.tagName+' '+hdrSrc.id+' len='+hdrSrc.length);
+    if (hdrSrc.length == 0) return;
+    this.headerRowCnt=hdrSrc.length;
+    var colcnt;
+    for (r=0; r<this.headerRowCnt; r++) {
+      var headerRow = hdrSrc[r];
+      var headerCells=headerRow.cells;
+      if (r >= this.hdrCells.length) this.hdrCells[r]=new Array();
+      for (c=0; c<headerCells.length; c++) {
+        var obj={};
+        obj.cell=headerCells[c];
+        obj.colSpan=headerCells[c].colSpan || 1;  // Safari & Konqueror return default colspan of 0
+        if (this.options.useUnformattedColWidth) obj.initWidth=headerCells[c].offsetWidth
+        this.hdrCells[r].push(obj);
+      }
+      if (headerRow.id.slice(-5)=='_main') {
+        colcnt=this.hdrCells[r].length;
+        this.headerRowIdx=r;
+      }
+    }
+    Rico.writeDebugMsg("getColumnInfo end");
+    if (!colcnt) {
+      this.headerRowIdx=this.headerRowCnt-1;
+      colcnt=this.hdrCells[this.headerRowIdx].length
+    }
+    return colcnt;
+  },
+
+  // create column array
+  createColumnArray: function() {
+    this.direction=Element.getStyle(this.outerDiv,'direction').toLowerCase();  // ltr or rtl
+    this.align=this.direction=='rtl' ? ['right','left'] : ['left','right'];
+    //alert(this.direction+' : '+this.align[0]);
+    this.columns = new Array();
+    for (var c=0 ; c < this.headerColCnt; c++) {
+      Rico.writeDebugMsg("createColumnArray: c="+c);
+      var tabidx=c<this.options.frozenColumns ? 0 : 1;
+      this.columns.push(new Rico.TableColumn(this, c, this.hdrCells[this.headerRowIdx][c], tabidx));
+    }
+    this.getCookie();
+  },
+
+  // create div structure
+  createDivs: function() {
+    Rico.writeDebugMsg("createDivs start");
+    this.outerDiv   = this.createDiv("outer");
+    this.scrollDiv  = this.createDiv("scroll",this.outerDiv);
+    this.frozenTabs = this.createDiv("frozenTabs",this.outerDiv);
+    this.innerDiv   = this.createDiv("inner",this.outerDiv);
+    this.resizeDiv  = this.createDiv("resize",this.outerDiv);
+    this.resizeDiv.style.display="none";
+    this.exportDiv  = this.createDiv("export",this.outerDiv);
+    this.exportDiv.style.display="none";
+    //this.frozenTabs.style[this.align[0]]='0px';
+    //this.innerDiv.style[this.align[0]]='0px';
+    Rico.writeDebugMsg("createDivs end");
+  },
+
+  createDiv: function(elemName,elemParent) {
+    var id=this.tableId+"_"+elemName+"Div";
+    newdiv=$(id);
+    if (!newdiv) {
+      var newdiv = document.createElement("div");
+      newdiv.id = id;
+      if (elemParent) elemParent.appendChild(newdiv);
+    }
+    newdiv.className = "ricoLG_"+elemName+"Div";
+    return newdiv;
+  },
+
+  baseSizeDivs: function() {
+    this.setOtherHdrCellWidths();
+    this.tabs[0].style.display=this.options.frozenColumns ? '' : 'none';
+    this.hdrHt=Math.max(RicoUtil.nan2zero(this.thead[0].offsetHeight),this.thead[1].offsetHeight);
+    this.dataHt=Math.max(RicoUtil.nan2zero(this.tbody[0].offsetHeight),this.tbody[1].offsetHeight);
+    this.frzWi=this.borderWidth(this.tabs[0]);
+    var borderWi=this.borderWidth(this.columns[0].dataCell);
+    Rico.writeDebugMsg('baseSizeDivs '+this.tableId+': hdrHt='+this.hdrHt+' dataHt='+this.dataHt);
+    //alert(this.tableId+' frzWi='+this.frzWi+' borderWi='+borderWi);
+    for (var i=0; i<this.options.frozenColumns; i++)
+      if (this.columns[i].visible) this.frzWi+=parseInt(this.columns[i].colWidth)+borderWi;
+    this.scrTabWi=this.borderWidth(this.tabs[1]);
+    for (var i=this.options.frozenColumns; i<this.columns.length; i++)
+      if (this.columns[i].visible) this.scrTabWi+=parseInt(this.columns[i].colWidth)+borderWi;
+    this.scrWi=this.scrTabWi+this.options.scrollBarWidth;
+    var wiLimit=RicoUtil.windowWidth()-this.options.scrollBarWidth-8;
+    if (this.outerDiv.parentNode.clientWidth > 0)
+      wiLimit=Math.min(this.outerDiv.parentNode.clientWidth, wiLimit);
+    var overage=this.frzWi+this.scrWi-wiLimit;
+    Rico.writeDebugMsg('baseSizeDivs '+this.tableId+': scrWi='+this.scrWi+' wiLimit='+wiLimit+' overage='+overage+' clientWidth='+this.outerDiv.parentNode.clientWidth);
+    if (overage > 0 && this.options.frozenColumns < this.columns.length)
+      this.scrWi=Math.max(this.scrWi-overage, this.options.minScrollWidth);
+    this.scrollDiv.style.width=this.scrWi+'px';
+    this.scrollDiv.style.top=this.hdrHt+'px';
+    this.frozenTabs.style.width=this.scrollDiv.style[this.align[0]]=this.innerDiv.style[this.align[0]]=this.frzWi+'px';
+    this.outerDiv.style.width=(this.frzWi+this.scrWi)+'px';
+  },
+
+  borderWidth: function(elem) {
+    return RicoUtil.nan2zero(Element.getStyle(elem,'border-left-width')) + RicoUtil.nan2zero(Element.getStyle(elem,'border-right-width'));
+  },
+
+  setOtherHdrCellWidths: function() {
+    for (var r=0; r<this.hdrCells.length; r++) {
+      if (r==this.headerRowIdx) continue;
+      Rico.writeDebugMsg('setOtherHdrCellWidths: r='+r);
+      var c=i=0;
+      while (i<this.headerColCnt && c<this.hdrCells[r].length) {
+        var hdrcell=this.hdrCells[r][c];
+        var cell=hdrcell.cell;
+        var origSpan=newSpan=hdrcell.colSpan;
+        for (var w=j=0; j<origSpan; j++, i++) {
+          if (this.columns[i].hdrCell.style.display=='none')
+            newSpan--;
+          else if (this.columns[i].hdrColDiv.style.display!='none')
+            w+=parseInt(this.columns[i].colWidth);
+        }
+        if (!hdrcell.hdrColDiv || !hdrcell.hdrCellDiv) {
+          var divs=cell.getElementsByTagName('div');
+          hdrcell.hdrColDiv=(divs.length<1) ? RicoUtil.wrapChildren(cell,'ricoLG_col') : divs[0];
+          hdrcell.hdrCellDiv=(divs.length<2) ? RicoUtil.wrapChildren(hdrcell.hdrColDiv,'ricoLG_cell') : divs[1];
+        }
+        if (newSpan==0) {
+          cell.style.display='none';
+        } else if (w==0) {
+          hdrcell.hdrColDiv.style.display='none';
+          cell.colSpan=newSpan;
+        } else {
+          cell.style.display='';
+          hdrcell.hdrColDiv.style.display='';
+          cell.colSpan=newSpan;
+          hdrcell.hdrColDiv.style.width=w+'px';
+        }
+        c++;
+      }
+    }
+  },
+  
+  cell: function(r,c) {
+    return (0<=c && c<this.columns.length && r>=0) ? this.columns[c].cell(r) : null;
+  },
+
+  availHt: function() {
+    var divPos=Position.page(this.outerDiv);
+    return RicoUtil.windowHeight()-divPos[1]-2*this.options.scrollBarWidth-15;  // allow for scrollbar and some margin
+  },
+
+  handleScroll: function(e) {
+    var newTop=(this.hdrHt-this.scrollDiv.scrollTop)+'px';
+    this.tabs[0].style.top=newTop;
+    this.setHorizontalScroll();
+  },
+
+  setHorizontalScroll: function() {
+    var newLeft=(-this.scrollDiv.scrollLeft)+'px';
+    this.hdrTabs[1].style.left=newLeft;
+  },
+
+  pluginScroll: function() {
+     if (this.scrollPluggedIn) return;
+     Event.observe(this.scrollDiv,"scroll",this.scrollEventFunc, false);
+     this.scrollPluggedIn=true;
+  },
+
+  unplugScroll: function() {
+     Event.stopObserving(this.scrollDiv,"scroll", this.scrollEventFunc , false);
+     this.scrollPluggedIn=false;
+  },
+
+  printVisible: function(exportType) {
+    this.exportStart();
+    var limit=this.pageSize;
+    if (this.buffer && this.buffer.totalRows < limit) limit=this.buffer.totalRows;
+    for(var r=0; r < limit; r++) {
+      this.exportText+="<tr>";
+      for (var c=0; c<this.columns.length; c++) {
+        if (this.columns[c].visible)
+          this.exportText+="<td style='"+this.exportStyle(this.columns[c].cell(r))+"'>"+this.columns[c].getFormattedValue(r)+"</td>";
+      }
+      this.exportText+="</tr>";
+    }
+    this.exportFinish(exportType);
+  },
+
+  exportStart: function() {
+    this.exportText="<table border='1' cellspacing='0'><thead style='display: table-header-group;'>";
+
+    for (var r=0; r<this.hdrCells.length; r++) {
+      if (this.hdrCells[r].length==0 || Element.getStyle(this.hdrCells[r][0].cell.parentNode,'display')=='none') continue;
+      this.exportText+="<tr>";
+      for (var c=0,i=0; c<this.hdrCells[r].length; c++) {
+        var hdrcell=this.hdrCells[r][c];
+        var newSpan=hdrcell.colSpan;
+        for (var j=0; j<hdrcell.colSpan; j++, i++)
+          if (!this.columns[i].visible) newSpan--;
+        if (newSpan > 0) {
+          var divs=Element.getElementsByClassName(hdrcell.cell,'ricoLG_cell');
+          var cell=divs && divs.length>0 ? divs[0] : hdrcell.cell;
+          this.exportText+="<td style='"+this.exportStyle(cell)+"'";
+          if (hdrcell.colSpan > 1) this.exportText+=" colspan='"+newSpan+"'";
+          this.exportText+=">"+RicoUtil.getInnerText(cell)+"</td>";
+        }
+      }
+      this.exportText+="</tr>";
+    }
+
+    for (var c=0; c<this.columns.length; c++)
+    this.exportText+="</thead><tbody>";
+  },
+
+  exportFinish: function(exportType) {
+    if (this.hideMsg) this.hideMsg();
+    this.exportText+="</tbody></table>";
+    this.exportDiv.innerHTML=this.exportText;
+    this.exportText=undefined;
+    if (this.cancelMenu) this.cancelMenu();
+    window.open(Rico.htmDir+'export-'+(exportType || 'plain')+'.html?'+this.exportDiv.id,'',this.options.exportWindow);
+  },
+  
+  exportStyle: function(elem) {
+    var styleList=['background-color','color','text-align','font-weight']
+    for (var i=0,s=''; i < styleList.length; i++) {
+      var curstyle=Element.getStyle(elem,styleList[i]);
+      if (curstyle) s+=styleList[i]+':'+curstyle+';';
+    }
+    return s;
+  },
+
+  // Gets the value of the specified cookie
+  getCookie: function() {
+    var c=RicoUtil.getCookie(this.tableId);
+    if (!c) return;
+       var cookieVals=c.split(',');
+       for (var i=0; i<cookieVals.length; i++) {
+         var v=cookieVals[i].split(':');
+         if (v.length!=2) continue;
+         var colnum=parseInt(v[0].slice(1));
+         if (colnum < 0 || colnum >= this.columns.length) continue;
+         var col=this.columns[colnum];
+         switch (v[0].charAt(0)) {
+           case 'w':
+             col.setColWidth(v[1]);
+          col.customWidth=true;
+             break;
+           case 'h':
+             if (v[1].toLowerCase()=='true')
+               col.showColumn(true);
+             else
+               col.hideColumn(true);
+             break;
+           case 's':
+             col.setSorted(v[1]);
+             break;
+           case 'f':
+             var filterTemp=v[1].split('~');
+             col.filterOp=filterTemp.shift();
+          col.filterValues = [];
+          col.filterType = Rico.TableColumn.USERFILTER;
+          for (var j=0; j<filterTemp.length; j++)
+            col.filterValues.push(unescape(filterTemp[j]));
+             break;
+         }
+       }
+  },
+  
+  // Write information to cookie
+  setCookie: function() {
+       var cookieVals=[];
+       for (var i=0; i<this.columns.length; i++) {
+         var col=this.columns[i];
+         if (this.options.saveColumnInfo.width) {
+         if (col.customWidth) cookieVals.push('w'+i+':'+col.colWidth);
+         if (col.customVisible) cookieVals.push('h'+i+':'+col.visible);
+         }
+      if (this.options.saveColumnInfo.sort) {
+        if (col.currentSort != Rico.TableColumn.UNSORTED)
+          cookieVals.push('s'+i+':'+col.currentSort);
+      }
+      if (this.options.saveColumnInfo.filter && col.filterType == Rico.TableColumn.USERFILTER) {
+        var filterTemp=[col.filterOp];
+        for (var j=0; j<col.filterValues.length; j++)
+          filterTemp.push(escape(col.filterValues[j]));
+        cookieVals.push('f'+i+':'+filterTemp.join('~'));
+      }
+       }
+       if (cookieVals.length > 0)
+         RicoUtil.setCookie(this.tableId, cookieVals.join(','), this.options.cookieDays, this.options.cookiePath, this.options.cookieDomain);
+  }
+
+}
+
+Rico.TableColumn = Class.create();
+
+Rico.TableColumn.UNFILTERED   = 0;
+Rico.TableColumn.SYSTEMFILTER = 1;  /* system-generated filter, not shown to user */
+Rico.TableColumn.USERFILTER   = 2;
+
+Rico.TableColumn.UNSORTED   = 0;
+Rico.TableColumn.SORT_ASC   = "ASC";
+Rico.TableColumn.SORT_DESC  = "DESC";
+Rico.TableColumn.MINWIDTH   = 10; // min column width when user is resizing
+
+Rico.TableColumn.DOLLAR  = {type:'number', prefix:'$', decPlaces:2, ClassName:'alignright'};
+Rico.TableColumn.EURO    = {type:'number', prefix:'&euro;', decPlaces:2, ClassName:'alignright'};
+Rico.TableColumn.PERCENT = {type:'number', suffix:'%', decPlaces:2, multiplier:100, ClassName:'alignright'};
+Rico.TableColumn.QTY     = {type:'number', decPlaces:0, ClassName:'alignright'};
+Rico.TableColumn.DEFAULT = {type:"raw"};
+
+Rico.TableColumn.prototype = {
+
+  baseInit: function(liveGrid,colIdx,hdrInfo,tabIdx) {
+    Rico.writeDebugMsg("TableColumn.init index="+colIdx+" tabIdx="+tabIdx);
+    this.liveGrid  = liveGrid;
+    this.index     = colIdx;
+    this.hideWidth = Rico.isKonqueror || Prototype.Browser.WebKit || liveGrid.headerRowCnt>1 ? 5 : 2;  // column width used for "hidden" columns. Anything less than 5 causes problems with Konqueror. Best to keep this greater than padding used inside cell.
+    this.options   = liveGrid.options;
+    this.tabIdx    = tabIdx;
+    this.hdrCell   = hdrInfo.cell;
+    this.body = document.getElementsByTagName("body")[0];  // work around FireFox bug (document.body doesn't exist after XSLT)
+    this.displayName  = this.getDisplayName(this.hdrCell);
+    var divs=this.hdrCell.getElementsByTagName('div');
+    this.hdrColDiv=(divs.length<1) ? RicoUtil.wrapChildren(this.hdrCell,'ricoLG_col') : divs[0];
+    this.hdrCellDiv=(divs.length<2) ? RicoUtil.wrapChildren(this.hdrColDiv,'ricoLG_cell') : divs[1];
+    var sectionIndex= tabIdx==0 ? colIdx : colIdx-liveGrid.options.frozenColumns;
+    this.dataCell = liveGrid.tbody[tabIdx].rows[0].cells[sectionIndex];
+    var divs=this.dataCell.getElementsByTagName('div');
+    this.dataColDiv=(divs.length<1) ? RicoUtil.wrapChildren(this.dataCell,'ricoLG_col') : divs[0];
+
+    this.mouseDownHandler= this.handleMouseDown.bindAsEventListener(this);
+    this.mouseMoveHandler= this.handleMouseMove.bindAsEventListener(this);
+    this.mouseUpHandler  = this.handleMouseUp.bindAsEventListener(this);
+    this.mouseOutHandler = this.handleMouseOut.bindAsEventListener(this);
+
+    this.fieldName = 'col'+this.index;
+    var spec = liveGrid.options.columnSpecs[colIdx];
+    this.format=Object.extend( {}, Rico.TableColumn.DEFAULT);
+    switch (typeof spec) {
+      case 'object':
+        if (typeof spec.format=='string') Object.extend(this.format, Rico.TableColumn[spec.format.toUpperCase()]);
+        Object.extend(this.format, spec);
+        break;
+      case 'string':
+        if (spec.slice(0,4)=='spec') spec=spec.slice(4).toUpperCase();  // for backwards compatibility
+        this.format=typeof Rico.TableColumn[spec]=='object' ? Rico.TableColumn[spec] : Rico.TableColumn.DEFAULT;
+        break;
+    }
+    this.dataColDiv.className += (this.format.ClassName) ? ' '+this.format.ClassName : ' '+liveGrid.tableId+'_col'+colIdx;
+    this.visible=true;
+    if (typeof this.format.visible=='boolean') this.visible=this.format.visible;
+    if (typeof this.format.type!='string') this.format.type='raw';
+    Rico.writeDebugMsg("TableColumn.init index="+colIdx+" fieldName="+this.fieldName+' type='+this.format.type);
+    this.sortable     = typeof this.format.canSort=='boolean' ? this.format.canSort : liveGrid.options.canSortDefault;
+    this.currentSort  = Rico.TableColumn.UNSORTED;
+    this.filterable   = typeof this.format.canFilter=='boolean' ? this.format.canFilter : liveGrid.options.canFilterDefault;
+    this.filterType   = Rico.TableColumn.UNFILTERED;
+    this.hideable     = typeof this.format.canHide=='boolean' ? this.format.canHide : liveGrid.options.canHideDefault;
+    if (typeof this.isNullable!='boolean') this.isNullable = /number|date/.test(this.format.type);
+    this.isText       = /raw|text/.test(this.format.type);
+
+    var wi=(typeof(this.format.width)=='number') ? this.format.width : hdrInfo.initWidth;
+    wi=(typeof(wi)=='number') ? Math.max(wi,Rico.TableColumn.MINWIDTH) : liveGrid.options.defaultWidth;
+    this.setColWidth(wi);
+    if (!this.visible) this.setDisplayNone();
+    if (this.options.allowColResize && !this.format.noResize) this.insertResizer();
+  },
+
+  insertResizer: function() {
+    this.hdrCell.style.width='';
+    var resizer=this.hdrCellDiv.appendChild(document.createElement('div'));
+    resizer.className='ricoLG_Resize';
+    resizer.style[this.liveGrid.align[1]]='0px';
+    if (this.options.resizeBackground) {
+      var resizePath=Rico.imgDir+this.options.resizeBackground;
+      if (Prototype.Browser.IE) resizePath=location.protocol+resizePath;
+      resizer.style.backgroundImage='url('+resizePath+')';
+    }
+    Event.observe(resizer,"mousedown", this.mouseDownHandler, false);
+  },
+
+  // get the display name of a column
+  getDisplayName: function(el) {
+    var anchors=el.getElementsByTagName("A");
+    //Check the existance of A tags
+    if (anchors.length > 0)
+      return anchors[0].innerHTML;
+    else
+      return el.innerHTML.stripTags();
+  },
+  
+  _clear: function(gridCell) {
+    gridCell.innerHTML='&nbsp;';
+  },
+
+  clearCell: function(rowIndex) {
+    var gridCell=this.cell(rowIndex);
+    this._clear(gridCell,rowIndex);
+    if (!this.liveGrid.buffer) return;
+    var acceptAttr=this.liveGrid.buffer.options.acceptAttr;
+    for (var k=0; k<acceptAttr.length; k++) {
+      switch (acceptAttr[k]) {
+        case 'style': gridCell.style.cssText=''; break;
+        case 'class': gridCell.className=''; break;
+        default:      gridCell['_'+acceptAttr[k]]=''; break;
+      }
+    }
+  },
+
+  dataTable: function() {
+    return this.liveGrid.tabs[this.tabIdx];
+  },
+  
+  numRows: function() {
+    return this.dataColDiv.childNodes.length;
+  },
+
+  clearColumn: function() {
+    var childCnt=this.numRows();
+    for (var r=0; r<childCnt; r++)
+      this.clearCell(r);
+  },
+
+  cell: function(r) {
+    return this.dataColDiv.childNodes[r];
+  },
+  
+  getFormattedValue: function(r) {
+    return RicoUtil.getInnerText(this.cell(r));
+  },
+
+  setColWidth: function(wi) {
+    if (typeof wi=='number') {
+      wi=parseInt(wi);
+      if (wi < Rico.TableColumn.MINWIDTH) return;
+      wi=wi+'px';
+    }
+    Rico.writeDebugMsg('setColWidth '+this.index+': '+wi);
+    this.colWidth=wi;
+    this.hdrColDiv.style.width=wi;
+    this.dataColDiv.style.width=wi;
+  },
+
+  pluginMouseEvents: function() {
+    if (this.mousePluggedIn==true) return;
+    Event.observe(this.body,"mousemove", this.mouseMoveHandler, false);
+    Event.observe(this.body,"mouseup",   this.mouseUpHandler  , false);
+    Event.observe(this.body,"mouseout",  this.mouseOutHandler , false);
+    this.mousePluggedIn=true;
+  },
+
+  unplugMouseEvents: function() {
+    Event.stopObserving(this.body,"mousemove", this.mouseMoveHandler, false);
+    Event.stopObserving(this.body,"mouseup",   this.mouseUpHandler  , false);
+    Event.stopObserving(this.body,"mouseout",  this.mouseOutHandler , false);
+    this.mousePluggedIn=false;
+  },
+
+  handleMouseDown: function(e) {
+    this.resizeStart=e.clientX;
+    this.origWidth=parseInt(this.colWidth);
+    var p=Position.positionedOffset(this.hdrCell);
+    if (this.liveGrid.direction=='rtl') {
+      this.edge=p[0]+this.liveGrid.options.scrollBarWidth;
+      switch (this.tabIdx) {
+        case 0: this.edge+=this.liveGrid.innerDiv.offsetWidth; break;
+        case 1: this.edge-=this.liveGrid.scrollDiv.scrollLeft; break;
+      }
+    } else {
+      this.edge=p[0]+this.hdrCell.offsetWidth;
+      if (this.tabIdx>0) this.edge+=RicoUtil.nan2zero(this.liveGrid.tabs[0].offsetWidth)-this.liveGrid.scrollDiv.scrollLeft;
+    }
+    this.liveGrid.resizeDiv.style.left=this.edge+"px";
+    this.liveGrid.resizeDiv.style.display="";
+    this.liveGrid.outerDiv.style.cursor='e-resize';
+    this.tmpHighlight=this.liveGrid.highlightEnabled;
+    this.liveGrid.highlightEnabled=false;
+    this.pluginMouseEvents();
+    Event.stop(e);
+  },
+
+  handleMouseMove: function(e) {
+    var delta=e.clientX-this.resizeStart;
+    var newWidth=(this.liveGrid.direction=='rtl') ? this.origWidth-delta : this.origWidth+delta;
+    if (newWidth < Rico.TableColumn.MINWIDTH) return;
+    this.liveGrid.resizeDiv.style.left=(this.edge+delta)+"px";
+    this.colWidth=newWidth;
+    Event.stop(e);
+  },
+
+  handleMouseUp: function(e) {
+    this.unplugMouseEvents();
+    Rico.writeDebugMsg('handleMouseUp '+this.liveGrid.tableId);
+    this.liveGrid.outerDiv.style.cursor='';
+    this.liveGrid.resizeDiv.style.display="none";
+    this.setColWidth(this.colWidth);
+    this.customWidth=true;
+    this.liveGrid.setCookie();
+    this.liveGrid.highlightEnabled=this.tmpHighlight;
+    this.liveGrid.sizeDivs();
+    Event.stop(e);
+  },
+
+  handleMouseOut: function(e) {
+    var reltg = (e.relatedTarget) ? e.relatedTarget : e.toElement;
+    while (reltg != null && reltg.nodeName.toLowerCase() != 'body')
+      reltg=reltg.parentNode;
+    if (reltg!=null && reltg.nodeName.toLowerCase() == 'body') return true;
+    this.handleMouseUp(e);
+    return true;
+  },
+
+  setDisplayNone: function() {
+    this.hdrCell.style.display='none';
+    this.hdrColDiv.style.display='none';
+    this.dataCell.style.display='none';
+    this.dataColDiv.style.display='none';
+  },
+
+  // recalcTableWidth defaults to true
+  hideColumn: function(noresize) {
+    Rico.writeDebugMsg('hideColumn '+this.liveGrid.tableId);
+    this.setDisplayNone();
+    this.liveGrid.cancelMenu();
+    this.visible=false;
+    this.customVisible=true;
+    if (noresize) return;
+    this.liveGrid.setCookie();
+    this.liveGrid.sizeDivs();
+  },
+
+  showColumn: function(noresize) {
+    Rico.writeDebugMsg('showColumn '+this.liveGrid.tableId);
+    this.hdrCell.style.display='';
+    this.hdrColDiv.style.display='';
+    this.dataCell.style.display='';
+    this.dataColDiv.style.display='';
+    this.liveGrid.cancelMenu();
+    this.visible=true;
+    this.customVisible=true;
+    if (noresize) return;
+    this.liveGrid.setCookie();
+    this.liveGrid.sizeDivs();
+  },
+
+  setImage: function() {
+    if ( this.currentSort == Rico.TableColumn.SORT_ASC ) {
+       this.imgSort.style.display='';
+       this.imgSort.src=Rico.imgDir+this.options.sortAscendImg;
+    } else if ( this.currentSort == Rico.TableColumn.SORT_DESC ) {
+       this.imgSort.style.display='';
+       this.imgSort.src=Rico.imgDir+this.options.sortDescendImg;
+    } else {
+       this.imgSort.style.display='none';
+    }
+    if (this.filterType == Rico.TableColumn.USERFILTER) {
+       this.imgFilter.style.display='';
+       this.imgFilter.title=this.getFilterText();
+    } else {
+       this.imgFilter.style.display='none';
+    }
+  },
+
+  canHideShow: function() {
+    return this.hideable;
+  }
+
+};
+
+Rico.includeLoaded('ricoGridCommon.js');
diff --git a/NP_TrackBack/trunk/trackback/js/rico/ricoLiveGrid.js b/NP_TrackBack/trunk/trackback/js/rico/ricoLiveGrid.js
new file mode 100644 (file)
index 0000000..bc2af62
--- /dev/null
@@ -0,0 +1,1806 @@
+/**
+  *  (c) 2005-2007 Richard Cowin (http://openrico.org)
+  *  (c) 2005-2007 Matt Brown (http://dowdybrown.com)
+  *
+  *  Rico is 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
+  **/
+
+
+if(typeof Rico=='undefined') throw("LiveGrid requires the Rico JavaScript framework");
+if(typeof RicoUtil=='undefined') throw("LiveGrid requires the RicoUtil Library");
+if(typeof RicoTranslate=='undefined') throw("LiveGrid requires the RicoTranslate Library");
+if(typeof Rico.TableColumn=='undefined') throw("LiveGrid requires ricoGridCommon.js");
+
+
+Rico.Buffer = {};
+
+/**
+ * Loads buffer with data that already exists in the document as an HTML table (no AJAX).
+ * Also serves as a base class for AJAX-enabled buffers.
+ */
+Rico.Buffer.Base = Class.create();
+
+Rico.Buffer.Base.prototype = {
+
+  initialize: function(dataTable, options) {
+    this.clear();
+    this.updateInProgress = false;
+    this.lastOffset = 0;
+    this.rcvdRowCount = false;  // true if an eof element was included in the last xml response
+    this.foundRowCount = false; // true if an xml response is ever received with eof true
+    this.totalRows = 0;
+    this.rowcntContent = "";
+    this.rcvdOffset = -1;
+    this.options = {
+      fixedHdrRows     : 0,
+      canFilter        : false, // does buffer object support filtering?
+      isEncoded        : true,  // is the data received via ajax html encoded?
+      acceptAttr       : []     // attributes that can be copied from original/ajax data (e.g. className, style, id)
+    }
+    Object.extend(this.options, options || {});
+    if (dataTable) {
+      this.loadRowsFromTable(dataTable);
+    } else {
+      this.clear();
+    }
+  },
+
+  registerGrid: function(liveGrid) {
+    this.liveGrid = liveGrid;
+  },
+
+  setTotalRows: function( newTotalRows ) {
+    if (this.totalRows == newTotalRows) return;
+    this.totalRows = newTotalRows;
+    if (this.liveGrid) {
+      Rico.writeDebugMsg("setTotalRows, newTotalRows="+newTotalRows);
+      if (this.liveGrid.sizeTo=='data') this.liveGrid.resizeWindow();
+      this.liveGrid.updateHeightDiv();
+    }
+  },
+
+  loadRowsFromTable: function(tableElement) {
+    this.rows = this.dom2jstable(tableElement,this.options.fixedHdrRows);
+    this.startPos = 0;
+    this.size = this.rows.length;
+    this.setTotalRows(this.size);
+    this.rowcntContent = this.size.toString();
+    this.rcvdRowCount = true;
+    this.foundRowCount = true;
+  },
+
+  dom2jstable: function(rowsElement,firstRow) {
+    var newRows = new Array();
+    var trs = rowsElement.getElementsByTagName("tr");
+    var acceptAttr=this.options.acceptAttr;
+    for ( var i=firstRow || 0; i < trs.length; i++ ) {
+      var row = new Array();
+      var cells = trs[i].getElementsByTagName("td");
+      for ( var j=0; j < cells.length ; j++ ) {
+        row[j]={};
+        row[j].content=RicoUtil.getContentAsString(cells[j],this.options.isEncoded);
+        for (var k=0; k<acceptAttr.length; k++) {
+          row[j]['_'+acceptAttr[k]]=cells[j].getAttribute(acceptAttr[k]);
+        }
+        if (Prototype.Browser.IE) row[j]._class=cells[j].getAttribute('className');
+      }
+      newRows.push( row );
+    }
+    return newRows;
+  },
+
+  _blankRow: function() {
+    var newRow=[];
+    for (var i=0; i<this.liveGrid.columns.length; i++) {
+      newRow[i]={};
+      newRow[i].content='';
+    }
+    return newRow;
+  },
+
+  insertRow: function(beforeRowIndex) {
+    this.rows.splice(beforeRowIndex,0,this._blankRow());
+  },
+
+  appendRows: function(cnt) {
+    for (var i=0; i<cnt; i++)
+      this.rows.push(this._blankRow());
+    this.size=this.rows.length;
+  },
+
+  sortBuffer: function(colnum,sortdir,coltype,getvalfunc) {
+    this.sortColumn=colnum;
+    this.getValFunc=getvalfunc;
+    var sortFunc;
+    switch (coltype) {
+      case 'number': sortFunc=this._sortNumeric.bind(this); break;
+      case 'control':sortFunc=this._sortControl.bind(this); break;
+      default:       sortFunc=this._sortAlpha.bind(this); break;
+    }
+    this.rows.sort(sortFunc);
+    if (sortdir=='DESC') this.rows.reverse();
+  },
+
+  _sortAlpha: function(a,b) {
+    var aa = this.sortColumn<a.length ? RicoUtil.getInnerText(a[this.sortColumn].content) : '';
+    var bb = this.sortColumn<b.length ? RicoUtil.getInnerText(b[this.sortColumn].content) : '';
+    if (aa==bb) return 0;
+    if (aa<bb) return -1;
+    return 1;
+  },
+
+  _sortNumeric: function(a,b) {
+    var aa = this.sortColumn<a.length ? parseFloat(RicoUtil.getInnerText(a[this.sortColumn].content)) : 0;
+    if (isNaN(aa)) aa = 0;
+    var bb = this.sortColumn<b.length ? parseFloat(RicoUtil.getInnerText(b[this.sortColumn].content)) : 0;
+    if (isNaN(bb)) bb = 0;
+    return aa-bb;
+  },
+
+  _sortControl: function(a,b) {
+    var aa = this.sortColumn<a.length ? RicoUtil.getInnerText(a[this.sortColumn].content) : '';
+    var bb = this.sortColumn<b.length ? RicoUtil.getInnerText(b[this.sortColumn].content) : '';
+    if (this.getValFunc) {
+      aa=this.getValFunc(aa);
+      bb=this.getValFunc(bb);
+    }
+    if (aa==bb) return 0;
+    if (aa<bb) return -1;
+    return 1;
+  },
+
+  clear: function() {
+    this.rows = new Array();
+    this.startPos = -1;
+    this.size = 0;
+    this.windowPos = 0;
+  },
+
+  isInRange: function(position) {
+    var lastRow=Math.min(this.totalRows, position + this.liveGrid.pageSize)
+    return (position >= this.startPos) && (lastRow <= this.endPos()); // && (this.size != 0);
+  },
+
+  endPos: function() {
+    return this.startPos + this.rows.length;
+  },
+
+  fetch: function(offset) {
+    this.liveGrid.refreshContents(offset);
+    return;
+  },
+
+  exportAllRows: function(populate,finish) {
+    populate(this.getRows(0,this.totalRows));
+    finish();
+  },
+
+  setWindow: function(start, count) {
+    this.windowStart = start - this.startPos;
+    this.windowEnd = Math.min(this.windowStart + count,this.size);
+    this.windowPos = start;
+  },
+
+  isVisible: function(bufRow) {
+    return bufRow < this.rows.length && bufRow >= this.windowStart && bufRow < this.windowEnd;
+  },
+
+  getWindowCell: function(windowRow,col) {
+    var bufrow=this.windowStart+windowRow;
+    return this.isVisible(bufrow) && col < this.rows[bufrow].length ? this.rows[bufrow][col] : null;
+  },
+
+  getWindowValue: function(windowRow,col) {
+    var cell=this.getWindowCell(windowRow,col);
+    return cell ? cell.content : null;
+  },
+
+  setWindowValue: function(windowRow,col,newval) {
+    var bufRow=this.windowStart+windowRow;
+    if (bufRow >= this.windowEnd) return false;
+    return this.setValue(bufRow,col,newval);
+  },
+
+  getCell: function(bufRow,col) {
+    return bufRow < this.size ? this.rows[bufRow][col] : null;
+  },
+
+  getValue: function(bufRow,col) {
+    var cell=this.getCell(bufRow,col);
+    return cell ? cell.content : null;
+  },
+
+  setValue: function(bufRow,col,newval,newstyle) {
+    if (bufRow>=this.size) return false;
+    if (!this.rows[bufRow][col]) this.rows[bufRow][col]={};
+    this.rows[bufRow][col].content=newval;
+    if (typeof newstyle=='string') this.rows[bufRow][col]._style=newstyle;
+    this.rows[bufRow][col].modified=true;
+    return true;
+  },
+
+  getRows: function(start, count) {
+    var begPos = start - this.startPos;
+    var endPos = Math.min(begPos + count,this.size);
+    var results = new Array();
+    for ( var i=begPos; i < endPos; i++ )
+      results.push(this.rows[i]);
+    return results
+  }
+
+};
+
+
+// Rico.LiveGrid -----------------------------------------------------
+
+Rico.LiveGrid = Class.create();
+
+Rico.LiveGrid.prototype = {
+
+  initialize: function( tableId, buffer, options ) {
+    Object.extend(this, new Rico.GridCommon);
+    Object.extend(this, new Rico.LiveGridMethods);
+    this.baseInit();
+    this.tableId = tableId;
+    this.buffer = buffer;
+    Rico.setDebugArea(tableId+"_debugmsgs");    // if used, this should be a textarea
+
+    Object.extend(this.options, {
+      visibleRows      : -1,    // -1 or 'window'=size grid to client window; -2 or 'data'=size grid to min(window,data); -3 or 'body'=size so body does not have a scrollbar
+      frozenColumns    : 0,
+      offset           : 0,     // first row to be displayed
+      prefetchBuffer   : true,  // load table on page load?
+      minPageRows      : 1,
+      maxPageRows      : 50,
+      canSortDefault   : true,  // can be overridden in the column specs
+      canFilterDefault : buffer.options.canFilter, // can be overridden in the column specs
+      canHideDefault   : true,  // can be overridden in the column specs
+      cookiePrefix     : 'liveGrid.'+tableId,
+
+      // highlight & selection parameters
+      highlightElem    : 'none',// what gets highlighted/selected (cursorRow, cursorCell, menuRow, menuCell, selection, or none)
+      highlightSection : 3,     // which section gets highlighted (frozen=1, scrolling=2, all=3, none=0)
+      highlightMethod  : 'class', // outline, class, both (outline is less CPU intensive on the client)
+      highlightClass   : 'ricoLG_selection',
+
+      // export/print parameters
+      maxPrint         : 1000,  // max # of rows that can be printed/exported, 0=disable print/export feature
+      exportWindow     : "height=300,width=500,scrollbars=1,menubar=1,resizable=1",
+
+      // heading parameters
+      headingSort      : 'link', // link: make headings a link that will sort column, hover: make headings a hoverset, none: events on headings are disabled
+      hdrIconsFirst    : true,   // true: put sort & filter icons before header text, false: after
+      sortAscendImg    : 'sort_asc.gif',
+      sortDescendImg   : 'sort_desc.gif',
+      filterImg        : 'filtercol.gif'
+    });
+    // other options:
+    //   sortCol: initial sort column
+
+    this.options.sortHandler = this.sortHandler.bind(this);
+    this.options.filterHandler = this.filterHandler.bind(this);
+    this.options.onRefreshComplete = this.bookmarkHandler.bind(this);
+    this.options.rowOverHandler = this.rowMouseOver.bindAsEventListener(this);
+    this.options.mouseDownHandler = this.selectMouseDown.bindAsEventListener(this);
+    this.options.mouseOverHandler = this.selectMouseOver.bindAsEventListener(this);
+    this.options.mouseUpHandler  = this.selectMouseUp.bindAsEventListener(this);
+    Object.extend(this.options, options || {});
+
+    switch (typeof this.options.visibleRows) {
+      case 'string':
+        this.sizeTo=this.options.visibleRows;
+        this.options.visibleRows=-1;
+        break;
+      case 'number':
+        switch (this.options.visibleRows) {
+          case -1: this.sizeTo='window'; break;
+          case -2: this.sizeTo='data'; break;
+          case -3: this.sizeTo='body'; break;
+        }
+        break;
+      default:
+        this.sizeTo='window';
+        this.options.visibleRows=-1;
+    }
+    this.highlightEnabled=this.options.highlightSection>0;
+    this.pageSize=0;
+    this.createTables();
+    if (this.headerColCnt==0) {
+      alert('ERROR: no columns found in "'+this.tableId+'"');
+      return;
+    }
+    this.createColumnArray();
+       if (this.options.headingSort=='hover')
+         this.createHoverSet();
+
+    this.bookmark=$(this.tableId+"_bookmark");
+    this.sizeDivs();
+    this.createDataCells(this.options.visibleRows);
+    if (this.pageSize == 0) return;
+    this.buffer.registerGrid(this);
+    if (this.buffer.setBufferSize) this.buffer.setBufferSize(this.pageSize);
+    this.scrollTimeout = null;
+    this.lastScrollPos = 0;
+    this.attachMenuEvents();
+
+    // preload the images...
+    new Image().src = Rico.imgDir+this.options.filterImg;
+    new Image().src = Rico.imgDir+this.options.sortAscendImg;
+    new Image().src = Rico.imgDir+this.options.sortDescendImg;
+    Rico.writeDebugMsg("images preloaded");
+
+    this.setSortUI( this.options.sortCol, this.options.sortDir );
+    this.setImages();
+    if (this.listInvisible().length==this.columns.length)
+      this.columns[0].showColumn();
+    this.sizeDivs();
+    this.scrollDiv.style.display="";
+    if (this.buffer.totalRows>0)
+      this.updateHeightDiv();
+    if (this.options.prefetchBuffer) {
+      if (this.bookmark) this.bookmark.innerHTML = RicoTranslate.getPhrase("Loading...");
+      if (this.options.canFilterDefault && this.options.getQueryParms)
+        this.checkForFilterParms();
+      this.buffer.fetch(this.options.offset);
+    }
+    this.scrollEventFunc=this.handleScroll.bindAsEventListener(this);
+    this.wheelEventFunc=this.handleWheel.bindAsEventListener(this);
+    this.wheelEvent=(Prototype.Browser.IE || Prototype.Browser.Opera || Prototype.Browser.WebKit) ? 'mousewheel' : 'DOMMouseScroll';
+    if (this.options.offset && this.options.offset < this.buffer.totalRows)
+      setTimeout(this.scrollToRow.bind(this,this.options.offset),50);  // Safari requires a delay
+    this.pluginScroll();
+    this.setHorizontalScroll();
+    if (this.options.windowResize)
+      setTimeout(this.pluginWindowResize.bind(this),100);
+  }
+};
+
+Rico.LiveGridMethods = function() {};
+
+Rico.LiveGridMethods.prototype = {
+
+  createHoverSet: function() {
+    var hdrs=[];
+    for( var c=0; c < this.headerColCnt; c++ )
+      hdrs.push(this.columns[c].hdrCellDiv);
+         this.hoverSet = new Rico.HoverSet(hdrs);
+  },
+
+  checkForFilterParms: function() {
+    var s=window.location.search;
+    if (s.charAt(0)=='?') s=s.substring(1);
+    var pairs = s.split('&');
+    for (var i=0; i<pairs.length; i++)
+      if (pairs[i].match(/^f\[\d+\]/)) {
+        this.buffer.options.requestParameters.push(pairs[i]);
+      }
+  },
+
+/**
+ * Create one table for frozen columns and one for scrolling columns.
+ * Also create div's to contain them.
+ */
+  createTables: function() {
+    var insertloc;
+    var result = -1;
+    var table = $(this.tableId);
+    if (!table) return result;
+    if (table.tagName.toLowerCase()=='table') {
+      var theads=table.getElementsByTagName("thead");
+      if (theads.length == 1) {
+        Rico.writeDebugMsg("createTables: using thead section, id="+this.tableId);
+        var hdrSrc=theads[0].rows;
+      } else {
+        Rico.writeDebugMsg("createTables: using tbody section, id="+this.tableId);
+        var hdrSrc=new Array(table.rows[0]);
+      }
+      insertloc=table;
+    } else if (this.options.columnSpecs.length > 0) {
+      insertloc=table;
+      Rico.writeDebugMsg("createTables: inserting at "+table.tagName+", id="+this.tableId);
+    } else {
+      alert("ERROR!\n\nUnable to initialize '"+this.tableId+"'\n\nLiveGrid terminated");
+      return result;
+    }
+
+    this.createDivs();
+    this.scrollTabs = this.createDiv("scrollTabs",this.innerDiv);
+    this.shadowDiv  = this.createDiv("shadow",this.scrollDiv);
+    this.shadowDiv.style.direction='ltr';  // avoid FF bug
+    this.messageDiv = this.createDiv("message",this.outerDiv);
+    this.messageDiv.style.display="none";
+    this.messageShadow=new Rico.Shadow(this.messageDiv);
+    this.scrollDiv.style.display="none";
+    this.scrollDiv.scrollTop=0;
+    if (this.options.highlightMethod!='class') {
+      this.highlightDiv=[];
+      switch (this.options.highlightElem) {
+        case 'menuRow':
+        case 'cursorRow':
+          this.highlightDiv[0] = this.createDiv("highlight",this.outerDiv);
+          this.highlightDiv[0].style.display="none";
+          break;
+        case 'menuCell':
+        case 'cursorCell':
+          for (var i=0; i<2; i++) {
+            this.highlightDiv[i] = this.createDiv("highlight",i==0 ? this.frozenTabs : this.scrollTabs);
+            this.highlightDiv[i].style.display="none";
+            this.highlightDiv[i].id+=i;
+          }
+          break;
+        case 'selection':
+          // create one div for each side of the rectangle
+          var parentDiv=this.options.highlightSection==1 ? this.frozenTabs : this.scrollTabs;
+          for (var i=0; i<4; i++) {
+            this.highlightDiv[i] = this.createDiv("highlight",parentDiv);
+            this.highlightDiv[i].style.display="none";
+            this.highlightDiv[i].id+=i;
+            this.highlightDiv[i].style[i % 2==0 ? 'height' : 'width']="0px";
+          }
+          break;
+      }
+    }
+
+    // create new tables
+    for (var i=0; i<2; i++) {
+      this.tabs[i] = document.createElement("table");
+      this.tabs[i].className = 'ricoLG_table';
+      this.tabs[i].border=0;
+      this.tabs[i].cellPadding=0;
+      this.tabs[i].cellSpacing=0;
+      this.tabs[i].id = this.tableId+"_tab"+i;
+      this.thead[i]=this.tabs[i].createTHead();
+      this.thead[i].className='ricoLG_top';
+      if (this.tabs[i].tBodies.length==0)
+        this.tbody[i]=this.tabs[i].appendChild(document.createElement("tbody"));
+      else
+        this.tbody[i]=this.tabs[i].tBodies[0];
+      this.tbody[i].className='ricoLG_bottom';
+      this.tbody[i].insertRow(-1);
+    }
+    this.frozenTabs.appendChild(this.tabs[0]);
+    this.scrollTabs.appendChild(this.tabs[1]);
+    insertloc.parentNode.insertBefore(this.outerDiv,insertloc);
+    if (hdrSrc)
+      this.loadHdrSrc(hdrSrc);
+    else
+      this.createHdr();
+    for( var c=0; c < this.headerColCnt; c++ )
+      this.tbody[c<this.options.frozenColumns ? 0 : 1].rows[0].insertCell(-1);
+    if (table) table.parentNode.removeChild(table);
+    Rico.writeDebugMsg('createTables end');
+  },
+
+  createDataCells: function(visibleRows) {
+    if (visibleRows < 0) {
+      this.appendBlankRow();
+      this.sizeDivs();
+      this.autoAppendRows(this.remainingHt());
+    } else {
+      for( var r=0; r < visibleRows; r++ )
+        this.appendBlankRow();
+    }
+    var s=this.options.highlightSection;
+    if (s & 1) this.attachHighlightEvents(this.tbody[0]);
+    if (s & 2) this.attachHighlightEvents(this.tbody[1]);
+    return;
+  },
+
+  createHdr: function() {
+    for (var i=0; i<2; i++) {
+      var start=(i==0) ? 0 : this.options.frozenColumns;
+      var limit=(i==0) ? this.options.frozenColumns : this.options.columnSpecs.length;
+      Rico.writeDebugMsg('createHdr: i='+i+' start='+start+' limit='+limit);
+      if (this.options.PanelNamesOnTabHdr && this.options.panels) {
+        // place panel names on first row of thead
+        var r = this.thead[i].insertRow(-1);
+        r.className='ricoLG_hdg';
+        var lastIdx=-1, span, newCell=null, spanIdx=0;
+        for( var c=start; c < limit; c++ ) {
+          if (lastIdx == this.options.columnSpecs[c].panelIdx) {
+            span++;
+          } else {
+            if (newCell) newCell.colSpan=span;
+            newCell = r.insertCell(-1);
+            span=1;
+            lastIdx=this.options.columnSpecs[c].panelIdx;
+            newCell.innerHTML=this.options.panels[lastIdx];
+          }
+        }
+        if (newCell) newCell.colSpan=span;
+      }
+      var mainRow = this.thead[i].insertRow(-1);
+      mainRow.id=this.tableId+'_tab'+i+'h_main';
+      mainRow.className='ricoLG_hdg';
+      for( var c=start; c < limit; c++ ) {
+        var newCell = mainRow.insertCell(-1);
+        newCell.innerHTML=this.options.columnSpecs[c].Hdg;
+      }
+      this.headerColCnt = this.getColumnInfo(this.thead[i].rows);
+    }
+  },
+
+  loadHdrSrc: function(hdrSrc) {
+    Rico.writeDebugMsg('loadHdrSrc start');
+    this.headerColCnt = this.getColumnInfo(hdrSrc);
+    for (var i=0; i<2; i++) {
+      for (var r=0; r<hdrSrc.length; r++) {
+        var newrow = this.thead[i].insertRow(-1);
+        newrow.className='ricoLG_hdg';
+      }
+    }
+    if (hdrSrc.length==1) {
+      var cells=hdrSrc[0].cells;
+      for (var c=0; cells.length > 0; c++)
+        this.thead[c<this.options.frozenColumns ? 0 : 1].rows[0].appendChild(cells[0]);
+    } else {
+      for (var r=0; r<hdrSrc.length; r++) {
+        var cells=hdrSrc[r].cells;
+        for (var c=0; cells.length > 0; c++) {
+          if (cells[0].className=='ricoFrozen') {
+            this.thead[0].rows[r].appendChild(cells[0]);
+            if (r==this.headerRowIdx) this.options.frozenColumns=c+1;
+          } else {
+            this.thead[1].rows[r].appendChild(cells[0]);
+          }
+        }
+      }
+    }
+    Rico.writeDebugMsg('loadHdrSrc end');
+  },
+
+  sizeDivs: function() {
+    Rico.writeDebugMsg('sizeDivs: '+this.tableId);
+    this.cancelMenu();
+    this.unhighlight();
+    this.baseSizeDivs();
+    if (this.pageSize == 0) return;
+    this.rowHeight = Math.round(this.dataHt/this.pageSize);
+    var scrHt=this.dataHt;
+    if (this.scrWi>0 || Prototype.Browser.IE || Prototype.Browser.WebKit)
+      scrHt+=this.options.scrollBarWidth;
+    this.scrollDiv.style.height=scrHt+'px';
+    this.innerDiv.style.width=(this.scrWi-this.options.scrollBarWidth+1)+'px';
+    this.resizeDiv.style.height=this.frozenTabs.style.height=this.innerDiv.style.height=(this.hdrHt+this.dataHt+1)+'px';
+    Rico.writeDebugMsg('sizeDivs scrHt='+scrHt+' innerHt='+this.innerDiv.style.height+' rowHt='+this.rowHeight+' pageSize='+this.pageSize);
+    pad=(this.scrWi-this.scrTabWi < this.options.scrollBarWidth) ? 2 : 0;
+    this.shadowDiv.style.width=(this.scrTabWi+pad)+'px';
+    this.outerDiv.style.height=(this.hdrHt+scrHt)+'px';
+    this.setHorizontalScroll();
+  },
+
+  setHorizontalScroll: function() {
+    var scrleft=this.scrollDiv.scrollLeft;
+    this.scrollTabs.style.left=(-scrleft)+'px';
+  },
+
+  remainingHt: function() {
+    var winHt=RicoUtil.windowHeight();
+    var margin=Prototype.Browser.IE ? 15 : 10;
+    switch (this.sizeTo) {
+      case 'window':
+      case 'data':
+        var divPos=Position.page(this.outerDiv);
+        var tabHt=Math.max(this.tabs[0].offsetHeight,this.tabs[1].offsetHeight);
+        Rico.writeDebugMsg("remainingHt, winHt="+winHt+' tabHt='+tabHt+' gridY='+divPos[1]);
+        return winHt-divPos[1]-tabHt-this.options.scrollBarWidth-margin;  // allow for scrollbar and some margin
+      case 'body':
+        //Rico.writeDebugMsg("remainingHt, document.height="+document.height);
+        //Rico.writeDebugMsg("remainingHt, body.offsetHeight="+document.body.offsetHeight);
+        //Rico.writeDebugMsg("remainingHt, body.scrollHeight="+document.body.scrollHeight);
+        //Rico.writeDebugMsg("remainingHt, documentElement.scrollHeight="+document.documentElement.scrollHeight);
+        var bodyHt=Prototype.Browser.IE ? document.body.scrollHeight : document.body.offsetHeight;
+        var remHt=winHt-bodyHt-margin;
+        if (!Prototype.Browser.WebKit) remHt-=this.options.scrollBarWidth;
+        Rico.writeDebugMsg("remainingHt, winHt="+winHt+' pageHt='+bodyHt+' remHt='+remHt);
+        return remHt;
+    }
+  },
+
+  adjustPageSize: function() {
+    var remHt=this.remainingHt();
+    Rico.writeDebugMsg('adjustPageSize remHt='+remHt+' lastRow='+this.lastRowPos);
+    if (remHt > this.rowHeight)
+      this.autoAppendRows(remHt);
+    else if (remHt < 0 || this.sizeTo=='data')
+      this.autoRemoveRows(-remHt);
+  },
+
+  pluginWindowResize: function() {
+    Event.observe(window, "resize", this.resizeWindow.bindAsEventListener(this), false);
+  },
+
+  resizeWindow: function() {
+    Rico.writeDebugMsg('resizeWindow '+this.tableId+' lastRow='+this.lastRowPos);
+    if (!this.sizeTo) {
+      this.sizeDivs();
+      return;
+    }
+    var oldSize=this.pageSize;
+    this.adjustPageSize();
+    if (this.pageSize > oldSize) {
+      this.isPartialBlank=true;
+      var adjStart=this.adjustRow(this.lastRowPos);
+      this.buffer.fetch(adjStart);
+    }
+    if (oldSize != this.pageSize)
+      setTimeout(this.finishResize.bind(this),50);
+    else
+      this.sizeDivs();
+    Rico.writeDebugMsg('resizeWindow complete. old size='+oldSize+' new size='+this.pageSize);
+  },
+
+  finishResize: function() {
+    this.sizeDivs();
+    this.updateHeightDiv();
+  },
+
+  topOfLastPage: function() {
+    return Math.max(this.buffer.totalRows-this.pageSize,0);
+  },
+
+  updateHeightDiv: function() {
+    var notdisp=this.topOfLastPage();
+    var ht = this.scrollDiv.clientHeight + this.rowHeight * notdisp;
+    //if (Prototype.Browser.Opera) ht+=this.options.scrollBarWidth-3;
+    Rico.writeDebugMsg("updateHeightDiv, ht="+ht+' scrollDiv.clientHeight='+this.scrollDiv.clientHeight+' rowsNotDisplayed='+notdisp);
+    this.shadowDiv.style.height=ht+'px';
+  },
+
+  autoRemoveRows: function(overage) {
+    var removeCnt=Math.ceil(overage / this.rowHeight);
+    if (this.sizeTo=='data')
+      removeCnt=Math.max(removeCnt,this.pageSize-this.buffer.totalRows);
+    Rico.writeDebugMsg("autoRemoveRows overage="+overage+" removeCnt="+removeCnt);
+    for (var i=0; i<removeCnt; i++)
+      this.removeRow();
+  },
+
+  removeRow: function() {
+    if (this.pageSize <= this.options.minPageRows) return;
+    this.pageSize--;
+    for( var c=0; c < this.headerColCnt; c++ ) {
+      var cell=this.columns[c].cell(this.pageSize);
+      this.columns[c].dataColDiv.removeChild(cell);
+    }
+  },
+
+  autoAppendRows: function(overage) {
+    var addCnt=Math.floor(overage / this.rowHeight);
+    Rico.writeDebugMsg("autoAppendRows overage="+overage+" cnt="+addCnt+" rowHt="+this.rowHeight);
+    for (var i=0; i<addCnt; i++) {
+      if (this.sizeTo=='data' && this.pageSize>=this.buffer.totalRows) break;
+      this.appendBlankRow();
+    }
+  },
+
+  // on older systems, this can be fairly slow
+  appendBlankRow: function() {
+    if (this.pageSize >= this.options.maxPageRows) return;
+    Rico.writeDebugMsg("appendBlankRow #"+this.pageSize);
+    var cls=this.defaultRowClass(this.pageSize);
+    for( var c=0; c < this.headerColCnt; c++ ) {
+      var newdiv = document.createElement("div");
+      newdiv.className = 'ricoLG_cell '+cls;
+      newdiv.id=this.tableId+'_'+this.pageSize+'_'+c;
+      this.columns[c].dataColDiv.appendChild(newdiv);
+      newdiv.innerHTML='&nbsp;';
+      if (this.columns[c]._create)
+        this.columns[c]._create(newdiv,this.pageSize);
+    }
+    this.pageSize++;
+  },
+
+  defaultRowClass: function(rownum) {
+    return (rownum % 2==0) ? 'ricoLG_evenRow' : 'ricoLG_oddRow';
+  },
+
+  handleMenuClick: function(e) {
+    //Event.stop(e);
+    if (!this.menu) return;
+    this.cancelMenu();
+    this.unhighlight(); // in case highlighting was invoked externally
+    var cell=Event.element(e);
+    if (cell.className=='ricoLG_highlightDiv') {
+      var idx=this.highlightIdx;
+    } else {
+      cell=RicoUtil.getParentByTagName(cell,'div','ricoLG_cell');
+      if (!cell) return;
+      var idx=this.winCellIndex(cell);
+      if ((this.options.highlightSection & (idx.tabIdx+1))==0) return;
+    }
+    this.highlight(idx);
+    this.highlightEnabled=false;
+    if (this.hideScroll) this.scrollDiv.style.overflow="hidden";
+    this.menuIdx=idx;
+    if (!this.menu.div) this.menu.createDiv();
+    this.menu.liveGrid=this;
+    if (this.menu.buildGridMenu) {
+      var showMenu=this.menu.buildGridMenu(idx.row, idx.column, idx.tabIdx);
+      if (!showMenu) return;
+    }
+    if (this.options.highlightElem=='selection' && !this.isSelected(idx.cell))
+      this.selectCell(idx.cell);
+    this.menu.showmenu(e,this.closeMenu.bind(this));
+  },
+
+  closeMenu: function() {
+    if (!this.menuIdx) return;
+    if (this.hideScroll) this.scrollDiv.style.overflow="";
+    this.unhighlight();
+    this.highlightEnabled=true;
+    this.menuIdx=null;
+  },
+
+/**
+ * @return index of cell within the window
+ */
+  winCellIndex: function(cell) {
+    var a=cell.id.split(/_/);
+    var l=a.length;
+    var r=parseInt(a[l-2]);
+    var c=parseInt(a[l-1]);
+    return {row:r, column:c, tabIdx:this.columns[c].tabIdx, cell:cell};
+  },
+
+/**
+ * @return index of cell within the buffer
+ */
+  bufCellIndex: function(cell) {
+    var idx=this.winCellIndex(cell);
+    idx.row+=this.buffer.windowPos;
+    if (idx.row >= this.buffer.size) idx.onBlankRow=true;
+    return idx;
+  },
+
+  attachHighlightEvents: function(tBody) {
+    switch (this.options.highlightElem) {
+      case 'selection':
+        Event.observe(tBody,"mousedown", this.options.mouseDownHandler, false);
+        tBody.ondrag = function () { return false; };
+        tBody.onselectstart = function () { return false; };
+        break;
+      case 'cursorRow':
+      case 'cursorCell':
+        Event.observe(tBody,"mouseover", this.options.rowOverHandler, false);
+        break;
+    }
+  },
+
+  getVisibleSelection: function() {
+    var cellList=[];
+    if (this.SelectIdxStart && this.SelectIdxEnd) {
+      var r1=Math.max(Math.min(this.SelectIdxEnd.row,this.SelectIdxStart.row),this.buffer.windowPos);
+      var r2=Math.min(Math.max(this.SelectIdxEnd.row,this.SelectIdxStart.row),this.buffer.windowEnd-1);
+      var c1=Math.min(this.SelectIdxEnd.column,this.SelectIdxStart.column);
+      var c2=Math.max(this.SelectIdxEnd.column,this.SelectIdxStart.column);
+      for (var r=r1; r<=r2; r++)
+        for (var c=c1; c<=c2; c++)
+          cellList.push({row:r-this.buffer.windowPos,column:c});
+    }
+    if (this.SelectCtrl) {
+      for (var i=0; i<this.SelectCtrl.length; i++) {
+        if (this.SelectCtrl[i].row>=this.buffer.windowPos && this.SelectCtrl[i].row<this.buffer.windowEnd)
+          cellList.push({row:this.SelectCtrl[i].row-this.buffer.windowPos,column:this.SelectCtrl[i].column});
+      }
+    }
+    return cellList;
+  },
+
+  updateSelectOutline: function() {
+    if (!this.SelectIdxStart || !this.SelectIdxEnd) return;
+    var r1=Math.max(Math.min(this.SelectIdxEnd.row,this.SelectIdxStart.row), this.buffer.windowStart);
+    var r2=Math.min(Math.max(this.SelectIdxEnd.row,this.SelectIdxStart.row), this.buffer.windowEnd-1);
+    if (r1 > r2) {
+      this.HideSelection();
+      return;
+    }
+    var c1=Math.min(this.SelectIdxEnd.column,this.SelectIdxStart.column);
+    var c2=Math.max(this.SelectIdxEnd.column,this.SelectIdxStart.column);
+    var top1=this.columns[c1].cell(r1-this.buffer.windowStart).offsetTop;
+    var cell2=this.columns[c1].cell(r2-this.buffer.windowStart);
+    var bottom2=cell2.offsetTop+cell2.offsetHeight;
+    var left1=this.columns[c1].dataCell.offsetLeft;
+    var left2=this.columns[c2].dataCell.offsetLeft;
+    var right2=left2+this.columns[c2].dataCell.offsetWidth;
+    //window.status='updateSelectOutline: '+r1+' '+r2+' top='+top1+' bot='+bottom2;
+    this.highlightDiv[0].style.top=this.highlightDiv[3].style.top=this.highlightDiv[1].style.top=(this.hdrHt+top1-1) + 'px';
+    this.highlightDiv[2].style.top=(this.hdrHt+bottom2-1)+'px';
+    this.highlightDiv[3].style.left=(left1-2)+'px';
+    this.highlightDiv[0].style.left=this.highlightDiv[2].style.left=(left1-1)+'px';
+    this.highlightDiv[1].style.left=(right2-1)+'px';
+    this.highlightDiv[0].style.width=this.highlightDiv[2].style.width=(right2-left1-1) + 'px';
+    this.highlightDiv[1].style.height=this.highlightDiv[3].style.height=(bottom2-top1) + 'px';
+    //this.highlightDiv[0].style.right=this.highlightDiv[2].style.right=this.highlightDiv[1].style.right=()+'px';
+    //this.highlightDiv[2].style.bottom=this.highlightDiv[3].style.bottom=this.highlightDiv[1].style.bottom=(this.hdrHt+bottom2) + 'px';
+    for (var i=0; i<4; i++)
+      this.highlightDiv[i].style.display='';
+  },
+
+  HideSelection: function(cellList) {
+    if (this.options.highlightMethod!='class') {
+      for (var i=0; i<4; i++)
+        this.highlightDiv[i].style.display='none';
+    }
+    if (this.options.highlightMethod!='outline') {
+      var cellList=this.getVisibleSelection();
+      for (var i=0; i<cellList.length; i++)
+        this.unhighlightCell(this.columns[cellList[i].column].cell(cellList[i].row));
+    }
+  },
+
+  ShowSelection: function() {
+    if (this.options.highlightMethod!='class')
+      this.updateSelectOutline();
+    if (this.options.highlightMethod!='outline') {
+      var cellList=this.getVisibleSelection();
+      for (var i=0; i<cellList.length; i++)
+        this.highlightCell(this.columns[cellList[i].column].cell(cellList[i].row));
+    }
+  },
+
+  ClearSelection: function() {
+    this.HideSelection();
+    this.SelectIdxStart=null;
+    this.SelectIdxEnd=null;
+    this.SelectCtrl=[];
+  },
+
+  selectCell: function(cell) {
+    this.ClearSelection();
+    this.SelectIdxStart=this.SelectIdxEnd=this.bufCellIndex(cell);
+    this.ShowSelection();
+  },
+
+  AdjustSelection: function(cell) {
+    var newIdx=this.bufCellIndex(cell);
+    if (this.SelectIdxStart.tabIdx != newIdx.tabIdx) return;
+    this.HideSelection();
+    this.SelectIdxEnd=newIdx;
+    this.ShowSelection();
+  },
+
+  RefreshSelection: function() {
+    var cellList=this.getVisibleSelection();
+    for (var i=0; i<cellList.length; i++)
+      this.columns[cellList[i].column].displayValue(cellList[i].row);
+  },
+
+  FillSelection: function(newVal,newStyle) {
+    if (this.SelectIdxStart && this.SelectIdxEnd) {
+      var r1=Math.min(this.SelectIdxEnd.row,this.SelectIdxStart.row);
+      var r2=Math.max(this.SelectIdxEnd.row,this.SelectIdxStart.row);
+      var c1=Math.min(this.SelectIdxEnd.column,this.SelectIdxStart.column);
+      var c2=Math.max(this.SelectIdxEnd.column,this.SelectIdxStart.column);
+      for (var r=r1; r<=r2; r++)
+        for (var c=c1; c<=c2; c++)
+          this.buffer.setValue(r,c,newVal,newStyle);
+    }
+    if (this.SelectCtrl) {
+      for (var i=0; i<this.SelectCtrl.length; i++)
+        this.buffer.setValue(this.SelectCtrl[i].row,this.SelectCtrl[i].column,newVal,newStyle);
+    }
+    this.RefreshSelection();
+  },
+
+  selectMouseDown: function(e) {
+    if (this.highlightEnabled==false) return true;
+    this.cancelMenu();
+    var cell=Event.element(e);
+    Event.stop(e);
+    if (!Event.isLeftClick(e)) return;
+    cell=RicoUtil.getParentByTagName(cell,'div','ricoLG_cell');
+    if (!cell) return;
+    var newIdx=this.bufCellIndex(cell);
+    if (newIdx.onBlankRow) return;
+    if (e.ctrlKey) {
+      if (!this.SelectIdxStart || this.options.highlightMethod!='class') return;
+      if (!this.isSelected(cell)) {
+        this.highlightCell(cell);
+        this.SelectCtrl.push(this.bufCellIndex(cell));
+      } else {
+        for (var i=0; i<this.SelectCtrl.length; i++) {
+          if (this.SelectCtrl[i].row==newIdx.row && this.SelectCtrl[i].column==newIdx.column) {
+            this.unhighlightCell(cell);
+            this.SelectCtrl.splice(i,1);
+            break;
+          }
+        }
+      }
+    } else if (e.shiftKey) {
+      if (!this.SelectIdxStart) return;
+      this.AdjustSelection(cell);
+    } else {
+      this.selectCell(cell);
+      this.pluginSelect();
+    }
+  },
+
+  pluginSelect: function() {
+    if (this.selectPluggedIn) return;
+    var tBody=this.tbody[this.SelectIdxStart.tabIdx];
+    Event.observe(tBody,"mouseover", this.options.mouseOverHandler, false);
+    Event.observe(this.outerDiv,"mouseup",  this.options.mouseUpHandler,  false);
+    if (this.options.highlightMethod!='class')
+    this.selectPluggedIn=true;
+  },
+
+  unplugSelect: function() {
+    var tBody=this.tbody[this.SelectIdxStart.tabIdx];
+    Event.stopObserving(tBody,"mouseover", this.options.mouseOverHandler , false);
+    Event.stopObserving(this.outerDiv,"mouseup", this.options.mouseUpHandler , false);
+    this.selectPluggedIn=false;
+  },
+
+  selectMouseUp: function(e) {
+    this.unplugSelect();
+    var cell=Event.element(e);
+    cell=RicoUtil.getParentByTagName(cell,'div','ricoLG_cell');
+    if (!cell) return;
+    if (this.SelectIdxStart && this.SelectIdxEnd)
+      this.AdjustSelection(cell);
+    else
+      this.ClearSelection();
+  },
+
+  selectMouseOver: function(e) {
+    var cell=Event.element(e);
+    cell=RicoUtil.getParentByTagName(cell,'div','ricoLG_cell');
+    if (!cell) return;
+    this.AdjustSelection(cell);
+    Event.stop(e);
+  },
+
+  isSelected: function(cell) {
+    return Element.hasClassName(cell,this.options.highlightClass);
+  },
+
+  highlightCell: function(cell) {
+    Element.addClassName(cell,this.options.highlightClass);
+  },
+
+  unhighlightCell: function(cell) {
+    if (cell==null) return;
+    Element.removeClassName(cell,this.options.highlightClass);
+  },
+
+  selectRow: function(r) {
+    for (var c=0; c<this.columns.length; c++)
+      this.highlightCell(this.columns[c].cell(r));
+  },
+
+  unselectRow: function(r) {
+    for (var c=0; c<this.columns.length; c++)
+      this.unhighlightCell(this.columns[c].cell(r));
+  },
+
+  rowMouseOver: function(e) {
+    if (!this.highlightEnabled) return;
+    var cell=Event.element(e);
+    cell=RicoUtil.getParentByTagName(cell,'div','ricoLG_cell');
+    if (!cell) return;
+    var newIdx=this.winCellIndex(cell);
+    if ((this.options.highlightSection & (newIdx.tabIdx+1))==0) return;
+    this.highlight(newIdx);
+  },
+
+  highlight: function(newIdx) {
+    if (this.options.highlightMethod!='outline') this.cursorSetClass(newIdx);
+    if (this.options.highlightMethod!='class') this.cursorOutline(newIdx);
+    this.highlightIdx=newIdx;
+  },
+
+  cursorSetClass: function(newIdx) {
+    switch (this.options.highlightElem) {
+      case 'menuCell':
+      case 'cursorCell':
+        if (this.highlightIdx) this.unhighlightCell(this.highlightIdx.cell);
+        this.highlightCell(newIdx.cell);
+        break;
+      case 'menuRow':
+      case 'cursorRow':
+        if (this.highlightIdx) this.unselectRow(this.highlightIdx.row);
+        var s1=this.options.highlightSection & 1;
+        var s2=this.options.highlightSection & 2;
+        var c0=s1 ? 0 : this.options.frozenColumns;
+        var c1=s2 ? this.columns.length : this.options.frozenColumns;
+        for (var c=c0; c<c1; c++)
+          this.highlightCell(this.columns[c].cell(newIdx.row));
+        break;
+      default: return;
+    }
+  },
+
+  cursorOutline: function(newIdx) {
+    switch (this.options.highlightElem) {
+      case 'menuCell':
+      case 'cursorCell':
+        var div=this.highlightDiv[newIdx.tabIdx];
+        div.style.left=(this.columns[newIdx.column].dataCell.offsetLeft-1)+'px';
+        div.style.width=this.columns[newIdx.column].colWidth;
+        this.highlightDiv[1-newIdx.tabIdx].style.display='none';
+        break;
+      case 'menuRow':
+      case 'cursorRow':
+        var div=this.highlightDiv[0];
+        var s1=this.options.highlightSection & 1;
+        var s2=this.options.highlightSection & 2;
+        div.style.left=s1 ? '0px' : this.frozenTabs.style.width;
+        div.style.width=((s1 ? this.frozenTabs.offsetWidth : 0) + (s2 ? this.innerDiv.offsetWidth : 0) - 4)+'px';
+        break;
+      default: return;
+    }
+    div.style.top=(this.hdrHt+newIdx.row*this.rowHeight-1)+'px';
+    div.style.height=(this.rowHeight-1)+'px';
+    div.style.display='';
+  },
+
+  unhighlight: function() {
+    switch (this.options.highlightElem) {
+      case 'menuCell':
+        this.highlightIdx=this.menuIdx;
+      case 'cursorCell':
+        if (this.highlightIdx) this.unhighlightCell(this.highlightIdx.cell);
+        if (!this.highlightDiv) return;
+        for (var i=0; i<2; i++)
+          this.highlightDiv[i].style.display='none';
+        break;
+      case 'menuRow':
+        this.highlightIdx=this.menuIdx;
+      case 'cursorRow':
+        if (this.highlightIdx) this.unselectRow(this.highlightIdx.row);
+        if (this.highlightDiv) this.highlightDiv[0].style.display='none';
+        break;
+    }
+  },
+
+  hideMsg: function() {
+    if (this.messageDiv.style.display=="none") return;
+    this.messageDiv.style.display="none";
+    this.messageShadow.hide();
+  },
+
+  showMsg: function(msg) {
+    this.messageDiv.innerHTML=RicoTranslate.getPhrase(msg);
+    this.messageDiv.style.display="";
+    var msgWidth=this.messageDiv.offsetWidth;
+    var msgHeight=this.messageDiv.offsetHeight;
+    var divwi=this.outerDiv.offsetWidth;
+    var divht=this.outerDiv.offsetHeight;
+    this.messageDiv.style.top=parseInt((divht-msgHeight)/2)+'px';
+    this.messageDiv.style.left=parseInt((divwi-msgWidth)/2)+'px';
+    this.messageShadow.show();
+    Rico.writeDebugMsg("showMsg: "+msg);
+  },
+
+  resetContents: function(resetHt) {
+    Rico.writeDebugMsg("resetContents("+resetHt+")");
+    this.buffer.clear();
+    this.clearRows();
+    if (typeof resetHt=='undefined' || resetHt==true) {
+      this.buffer.setTotalRows(0);
+    } else {
+      this.scrollToRow(0);
+    }
+    if (this.bookmark) this.bookmark.innerHTML="&nbsp;";
+  },
+
+  setImages: function() {
+    for (n=0; n<this.columns.length; n++)
+      this.columns[n].setImage();
+  },
+
+  // returns column index, or -1 if there are no sorted columns
+  findSortedColumn: function() {
+    for (var n=0; n<this.columns.length; n++)
+      if (this.columns[n].isSorted()) return n;
+    return -1;
+  },
+
+  findColumnName: function(name) {
+    for (var n=0; n<this.columns.length; n++)
+      if (this.columns[n].fieldName == name) return n;
+    return -1;
+  },
+
+/**
+ * Set initial sort
+ */
+  setSortUI: function( columnNameOrNum, sortDirection ) {
+    Rico.writeDebugMsg("setSortUI: "+columnNameOrNum+' '+sortDirection);
+    var colnum=this.findSortedColumn();
+    if (colnum >= 0) {
+      sortDirection=this.columns[colnum].getSortDirection();
+    } else {
+      if (typeof sortDirection!='string') {
+        sortDirection=Rico.TableColumn.SORT_ASC;
+      } else {
+        sortDirection=sortDirection.toUpperCase();
+        if (sortDirection != Rico.TableColumn.SORT_DESC) sortDirection=Rico.TableColumn.SORT_ASC;
+      }
+      switch (typeof columnNameOrNum) {
+        case 'string':
+          colnum=this.findColumnName(columnNameOrNum);
+          break;
+        case 'number':
+          colnum=columnNameOrNum;
+          break;
+      }
+    }
+    if (typeof(colnum)!='number' || colnum < 0) return;
+    this.clearSort();
+    this.columns[colnum].setSorted(sortDirection);
+    this.buffer.sortBuffer(colnum,sortDirection,this.columns[colnum].format.type,this.columns[colnum]._sortfunc);
+  },
+
+/**
+ * clear sort flag on all columns
+ */
+  clearSort: function() {
+    for (var x=0;x<this.columns.length;x++)
+      this.columns[x].setUnsorted();
+  },
+
+/**
+ * clear filters on all columns
+ */
+  clearFilters: function() {
+    for (var x=0;x<this.columns.length;x++)
+      this.columns[x].setUnfiltered(true);
+    if (this.options.filterHandler)
+      this.options.filterHandler();
+  },
+
+/**
+ * returns number of columns with a user filter set
+ */
+  filterCount: function() {
+    for (var x=0,cnt=0;x<this.columns.length;x++)
+      if (this.columns[x].isFiltered()) cnt++;
+    return cnt;
+  },
+
+  sortHandler: function() {
+    this.cancelMenu();
+    this.setImages();
+    var n=this.findSortedColumn();
+    if (n < 0) return;
+    Rico.writeDebugMsg("sortHandler: sorting column "+n);
+    this.buffer.sortBuffer(n,this.columns[n].getSortDirection(),this.columns[n].format.type,this.columns[n]._sortfunc);
+    this.clearRows();
+    this.scrollDiv.scrollTop = 0;
+    this.buffer.fetch(0);
+  },
+
+  filterHandler: function() {
+    Rico.writeDebugMsg("filterHandler");
+    this.cancelMenu();
+    this.ClearSelection();
+    this.setImages();
+    if (this.bookmark) this.bookmark.innerHTML="&nbsp;";
+    this.clearRows();
+    this.buffer.fetch(-1);
+  },
+
+  bookmarkHandler: function(firstrow,lastrow) {
+    if (isNaN(firstrow) || !this.bookmark) return;
+    var totrows=this.buffer.totalRows;
+    if (totrows < lastrow) lastrow=totrows;
+    if (totrows<=0) {
+      var newhtml = RicoTranslate.getPhrase("No matching records");
+    } else if (lastrow<0) {
+      var newhtml = RicoTranslate.getPhrase("No records");
+    } else {
+      var newhtml = RicoTranslate.getPhrase("Listing records")+" "+firstrow+" - "+lastrow;
+      var totphrase = this.buffer.foundRowCount ? "of" : "of about";
+      newhtml+=" "+RicoTranslate.getPhrase(totphrase)+" "+totrows;
+    }
+    this.bookmark.innerHTML = newhtml;
+  },
+
+/**
+ * @return array of column objects which have invisible status
+ */
+  listInvisible: function() {
+    var hiddenColumns=new Array();
+    for (var x=0;x<this.columns.length;x++)
+      if (this.columns[x].visible==false)
+        hiddenColumns.push(this.columns[x]);
+    return hiddenColumns;
+  },
+
+/**
+ * Show all columns
+ */
+  showAll: function() {
+    var invisible=this.listInvisible();
+    for (var x=0;x<invisible.length;x++)
+      invisible[x].showColumn();
+  },
+
+  clearRows: function() {
+    if (this.isBlank==true) return;
+    for (var c=0; c < this.columns.length; c++)
+      this.columns[c].clearColumn();
+    this.ClearSelection();
+    this.isBlank = true;
+  },
+
+  blankRow: function(r) {
+     for (var c=0; c < this.columns.length; c++)
+        this.columns[c].clearCell(r);
+  },
+
+  refreshContents: function(startPos) {
+    Rico.writeDebugMsg("refreshContents: startPos="+startPos+" lastRow="+this.lastRowPos+" PartBlank="+this.isPartialBlank+" pageSize="+this.pageSize);
+    this.hideMsg();
+    this.cancelMenu();
+    this.unhighlight(); // in case highlighting was manually invoked
+    this.highlightEnabled=this.options.highlightSection!='none';
+    if (startPos == this.lastRowPos && !this.isPartialBlank && !this.isBlank) return;
+    this.isBlank = false;
+    var viewPrecedesBuffer = this.buffer.startPos > startPos
+    var contentStartPos = viewPrecedesBuffer ? this.buffer.startPos: startPos;
+    var contentEndPos = Math.min(this.buffer.startPos + this.buffer.size, startPos + this.pageSize);
+    var onRefreshComplete = this.options.onRefreshComplete;
+
+    if ((startPos + this.pageSize < this.buffer.startPos)
+        || (this.buffer.startPos + this.buffer.size < startPos)
+        || (this.buffer.size == 0)) {
+      this.clearRows();
+      if (onRefreshComplete != null)
+          onRefreshComplete(contentStartPos+1,contentEndPos);
+      return;
+    }
+
+    Rico.writeDebugMsg('refreshContents: contentStartPos='+contentStartPos+' contentEndPos='+contentEndPos+' viewPrecedesBuffer='+viewPrecedesBuffer);
+    if (this.options.highlightElem=='selection') this.HideSelection();
+    var rowSize = contentEndPos - contentStartPos;
+    this.buffer.setWindow(contentStartPos, rowSize );
+    var blankSize = this.pageSize - rowSize;
+    var blankOffset = viewPrecedesBuffer ? 0: rowSize;
+    var contentOffset = viewPrecedesBuffer ? blankSize: 0;
+
+    for (var r=0; r < rowSize; r++) { //initialize what we have
+      for (var c=0; c < this.columns.length; c++)
+        this.columns[c].displayValue(r + contentOffset);
+    }
+    for (var i=0; i < blankSize; i++)     // blank out the rest
+      this.blankRow(i + blankOffset);
+    if (this.options.highlightElem=='selection') this.ShowSelection();
+    this.isPartialBlank = blankSize > 0;
+    this.lastRowPos = startPos;
+    Rico.writeDebugMsg("refreshContents complete, startPos="+startPos);
+    // Check if user has set a onRefreshComplete function
+    if (onRefreshComplete != null)
+      onRefreshComplete(contentStartPos+1,contentEndPos);
+  },
+
+  scrollToRow: function(rowOffset) {
+     var p=this.rowToPixel(rowOffset);
+     Rico.writeDebugMsg("scrollToRow, rowOffset="+rowOffset+" pixel="+p);
+     this.scrollDiv.scrollTop = p; // this causes a scroll event
+     if ( this.options.onscroll )
+        this.options.onscroll( this, rowOffset );
+  },
+
+  scrollUp: function() {
+     this.moveRelative(-1);
+  },
+
+  scrollDown: function() {
+     this.moveRelative(1);
+  },
+
+  pageUp: function() {
+     this.moveRelative(-this.pageSize);
+  },
+
+  pageDown: function() {
+     this.moveRelative(this.pageSize);
+  },
+
+  adjustRow: function(rowOffset) {
+     var notdisp=this.topOfLastPage();
+     if (notdisp == 0 || !rowOffset) return 0;
+     return Math.min(notdisp,rowOffset);
+  },
+
+  rowToPixel: function(rowOffset) {
+     return this.adjustRow(rowOffset) * this.rowHeight;
+  },
+
+/**
+ * @returns row to display at top of scroll div
+ */
+  pixeltorow: function(p) {
+     var notdisp=this.topOfLastPage();
+     if (notdisp == 0) return 0;
+     var prow=parseInt(p/this.rowHeight);
+     return Math.min(notdisp,prow);
+  },
+
+  moveRelative: function(relOffset) {
+     newoffset=Math.max(this.scrollDiv.scrollTop+relOffset*this.rowHeight,0);
+     newoffset=Math.min(newoffset,this.scrollDiv.scrollHeight);
+     //Rico.writeDebugMsg("moveRelative, newoffset="+newoffset);
+     this.scrollDiv.scrollTop=newoffset;
+  },
+
+  pluginScroll: function() {
+     if (this.scrollPluggedIn) return;
+     Rico.writeDebugMsg("pluginScroll: wheelEvent="+this.wheelEvent);
+     Event.observe(this.scrollDiv,"scroll",this.scrollEventFunc, false);
+     for (var t=0; t<2; t++)
+       Event.observe(this.tabs[t],this.wheelEvent,this.wheelEventFunc, false);
+     this.scrollPluggedIn=true;
+  },
+
+  unplugScroll: function() {
+     if (!this.scrollPluggedIn) return;
+     Rico.writeDebugMsg("unplugScroll");
+     Event.stopObserving(this.scrollDiv,"scroll", this.scrollEventFunc , false);
+     for (var t=0; t<2; t++)
+       Event.stopObserving(this.tabs[t],this.wheelEvent,this.wheelEventFunc, false);
+     this.scrollPluggedIn=false;
+  },
+
+  handleWheel: function(e) {
+    var delta = 0;
+    if (e.wheelDelta) {
+      if (Prototype.Browser.Opera)
+        delta = e.wheelDelta/120;
+      else if (Prototype.Browser.WebKit)
+        delta = -e.wheelDelta/12;
+      else
+        delta = -e.wheelDelta/120;
+    } else if (e.detail) {
+      delta = e.detail/3; /* Mozilla/Gecko */
+    }
+    if (delta) this.moveRelative(delta);
+    Event.stop(e);
+    return false;
+  },
+
+  handleScroll: function(e) {
+     if ( this.scrollTimeout )
+       clearTimeout( this.scrollTimeout );
+     this.setHorizontalScroll();
+     var scrtop=this.scrollDiv.scrollTop;
+     var vscrollDiff = this.lastScrollPos-scrtop;
+     if (vscrollDiff == 0.00) return;
+     var newrow=this.pixeltorow(scrtop);
+     if (newrow == this.lastRowPos && !this.isPartialBlank && !this.isBlank) return;
+     var stamp1 = new Date();
+     //Rico.writeDebugMsg("handleScroll, newrow="+newrow+" scrtop="+scrtop);
+     this.buffer.fetch(newrow);
+     if (this.options.onscroll) this.options.onscroll(this, newrow);
+     this.scrollTimeout = setTimeout(this.scrollIdle.bind(this), 1200 );
+     this.lastScrollPos = this.scrollDiv.scrollTop;
+     var stamp2 = new Date();
+     //Rico.writeDebugMsg("handleScroll, time="+(stamp2.getTime()-stamp1.getTime()));
+  },
+
+  scrollIdle: function() {
+     if ( this.options.onscrollidle )
+        this.options.onscrollidle();
+  },
+
+  printAll: function(exportType) {
+    this.exportStart();
+    this.buffer.exportAllRows(this.exportBuffer.bind(this),this.exportFinish.bind(this,exportType));
+  },
+
+/**
+ * Send all rows to print window
+ */
+  exportBuffer: function(rows) {
+    for(var r=0; r < rows.length; r++) {
+      this.exportText+="<tr>";
+      for (var c=0; c<this.columns.length; c++) {
+        if (this.columns[c].visible)
+          this.exportText+="<td>"+this.columns[c]._format(rows[r][c].content)+"</td>";
+      }
+      this.exportText+="</tr>";
+    }
+  }
+
+};
+
+
+Object.extend(Rico.TableColumn.prototype, {
+
+initialize: function(liveGrid,colIdx,hdrInfo,tabIdx) {
+  this.baseInit(liveGrid,colIdx,hdrInfo,tabIdx);
+  Rico.writeDebugMsg(" sortable="+this.sortable+" filterable="+this.filterable+" hideable="+this.hideable+" isNullable="+this.isNullable+' isText='+this.isText);
+  this.fixHeaders(this.liveGrid.tableId, this.options.hdrIconsFirst);
+  if (this.format.type=='control' && this.format.control) {
+    // copy all properties/methods that start with '_'
+    if (typeof this.format.control=='string')
+      this.format.control=eval(this.format.control);
+    for (var property in this.format.control)
+      if (property.charAt(0)=='_') {
+        Rico.writeDebugMsg("Copying control property "+property);
+        this[property] = this.format.control[property];
+      }
+  } else if (this['format_'+this.format.type]) {
+    this._format=this['format_'+this.format.type].bind(this);
+  }
+},
+
+sortAsc: function() {
+  this.setColumnSort(Rico.TableColumn.SORT_ASC);
+},
+
+sortDesc: function() {
+  this.setColumnSort(Rico.TableColumn.SORT_DESC);
+},
+
+setColumnSort: function(direction) {
+  this.liveGrid.clearSort();
+  this.setSorted(direction);
+  if (this.liveGrid.options.saveColumnInfo.sort)
+    this.liveGrid.setCookie();
+  if (this.options.sortHandler)
+    this.options.sortHandler();
+},
+
+isSortable: function() {
+  return this.sortable;
+},
+
+isSorted: function() {
+  return this.currentSort != Rico.TableColumn.UNSORTED;
+},
+
+getSortDirection: function() {
+  return this.currentSort;
+},
+
+toggleSort: function() {
+  if (this.liveGrid.buffer && this.liveGrid.buffer.totalRows==0) return;
+  if (this.currentSort == Rico.TableColumn.SORT_ASC)
+    this.sortDesc();
+  else
+    this.sortAsc();
+},
+
+setUnsorted: function() {
+  this.setSorted(Rico.TableColumn.UNSORTED);
+},
+
+/**
+ * direction must be one of Rico.TableColumn.UNSORTED, .SORT_ASC, or .SORT_DESC
+ */
+setSorted: function(direction) {
+  this.currentSort = direction;
+},
+
+canFilter: function() {
+  return this.filterable;
+},
+
+getFilterText: function() {
+  var vals=[];
+  for (var i=0; i<this.filterValues.length; i++) {
+    var v=this.filterValues[i];
+    if (v!=null && v.match(/<span\s+class=(['"]?)ricolookup\1>(.*)<\/span>/i))
+      vals.push(RegExp.leftContext);
+    else
+      vals.push(v);
+  }
+  switch (this.filterOp) {
+    case 'EQ':   return vals[0];
+    case 'NE':   return 'not: '+vals.join(', ');
+    case 'LE':   return '<= '+vals[0];
+    case 'GE':   return '>= '+vals[0];
+    case 'LIKE': return 'like: '+vals[0];
+    case 'NULL': return '<empty>';
+    case 'NOTNULL': return '<not empty>';
+  }
+  return '?';
+},
+
+getFilterQueryParm: function() {
+  if (this.filterType == Rico.TableColumn.UNFILTERED) return '';
+  var retval='&f['+this.index+'][op]='+this.filterOp;
+  retval+='&f['+this.index+'][len]='+this.filterValues.length
+  for (var i=0; i<this.filterValues.length; i++)
+    retval+='&f['+this.index+']['+i+']='+escape(this.filterValues[i]);
+  return retval;
+},
+
+setUnfiltered: function(skipHandler) {
+  this.filterType = Rico.TableColumn.UNFILTERED;
+  if (this.liveGrid.options.saveColumnInfo.filter)
+    this.liveGrid.setCookie();
+  if (this.removeFilterFunc)
+    this.removeFilterFunc();
+  if (this.options.filterHandler && !skipHandler)
+    this.options.filterHandler();
+},
+
+setFilterEQ: function() {
+  if (this.userFilter=='' && this.isNullable)
+    this.setUserFilter('NULL');
+  else
+    this.setUserFilter('EQ');
+},
+setFilterNE: function() {
+  if (this.userFilter=='' && this.isNullable)
+    this.setUserFilter('NOTNULL');
+  else
+    this.setUserFilter('NE');
+},
+addFilterNE: function() {
+  this.filterValues.push(this.userFilter);
+  if (this.liveGrid.options.saveColumnInfo.filter)
+    this.liveGrid.setCookie();
+  if (this.options.filterHandler)
+    this.options.filterHandler();
+},
+setFilterGE: function() { this.setUserFilter('GE'); },
+setFilterLE: function() { this.setUserFilter('LE'); },
+setFilterKW: function() {
+  var keyword=prompt(RicoTranslate.getPhrase("Enter keyword to search for")+RicoTranslate.getPhrase(" (use * as a wildcard):"),'');
+  if (keyword!='' && keyword!=null) {
+    if (keyword.indexOf('*')==-1) keyword='*'+keyword+'*';
+    this.setFilter('LIKE',keyword,Rico.TableColumn.USERFILTER);
+  } else {
+    this.liveGrid.cancelMenu();
+  }
+},
+
+setUserFilter: function(relop) {
+  this.setFilter(relop,this.userFilter,Rico.TableColumn.USERFILTER);
+},
+
+setSystemFilter: function(relop,filter) {
+  this.setFilter(relop,filter,Rico.TableColumn.SYSTEMFILTER);
+},
+
+setFilter: function(relop,filter,type,removeFilterFunc) {
+  this.filterValues = [filter];
+  this.filterType = type;
+  this.filterOp = relop;
+  if (type == Rico.TableColumn.USERFILTER && this.liveGrid.options.saveColumnInfo.filter)
+    this.liveGrid.setCookie();
+  this.removeFilterFunc=removeFilterFunc;
+  if (this.options.filterHandler)
+    this.options.filterHandler();
+},
+
+isFiltered: function() {
+  return this.filterType == Rico.TableColumn.USERFILTER;
+},
+
+format_text: function(v) {
+  if (typeof v!='string')
+    return '&nbsp;';
+  else
+    return v.stripTags();
+},
+
+format_showTags: function(v) {
+  if (typeof v!='string')
+    return '&nbsp;';
+  else
+    return v.replace(/&/g, '&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
+},
+
+format_number: function(v) {
+  if (typeof v=='undefined' || v=='' || v==null)
+    return '&nbsp;';
+  else
+    return v.formatNumber(this.format);
+},
+
+format_datetime: function(v) {
+  if (typeof v=='undefined' || v=='' || v==null)
+    return '&nbsp;';
+  else {
+    var d=new Date;
+    d.setISO8601(v);
+    return d.formatDate(this.format.dateFmt || 'translateDateTime');
+  }
+},
+
+format_date: function(v) {
+  if (typeof v=='undefined' || v==null || v=='')
+    return '&nbsp;';
+  else {
+    var d=new Date;
+    if (!d.setISO8601(v)) return v;
+    return d.formatDate(this.format.dateFmt || 'translateDate');
+  }
+},
+
+fixHeaders: function(prefix, iconsfirst) {
+  if (this.sortable) {
+    switch (this.options.headingSort) {
+      case 'link':
+        var a=RicoUtil.wrapChildren(this.hdrCellDiv,'ricoSort',undefined,'a')
+        a.href = "#";
+        a.onclick = this.toggleSort.bindAsEventListener(this);
+        break;
+      case 'hover':
+        this.hdrCellDiv.onclick = this.toggleSort.bindAsEventListener(this);
+        break;
+    }
+  }
+  this.imgFilter = document.createElement('img');
+  this.imgFilter.style.display='none';
+  this.imgFilter.src=Rico.imgDir+this.options.filterImg;
+  this.imgFilter.className='ricoLG_HdrIcon';
+  this.imgSort = document.createElement('img');
+  this.imgSort.style.display='none';
+  this.imgSort.className='ricoLG_HdrIcon';
+  if (iconsfirst) {
+    this.hdrCellDiv.insertBefore(this.imgSort,this.hdrCellDiv.firstChild);
+    this.hdrCellDiv.insertBefore(this.imgFilter,this.hdrCellDiv.firstChild);
+  } else {
+    this.hdrCellDiv.appendChild(this.imgFilter);
+    this.hdrCellDiv.appendChild(this.imgSort);
+  }
+},
+
+getValue: function(windowRow) {
+  return this.liveGrid.buffer.getWindowValue(windowRow,this.index);
+},
+
+getFormattedValue: function(windowRow) {
+  return this._format(this.getValue(windowRow));
+},
+
+getBufferCell: function(windowRow) {
+  return this.liveGrid.buffer.getWindowCell(windowRow,this.index);
+},
+
+setValue: function(windowRow,newval) {
+  this.liveGrid.buffer.setWindowValue(windowRow,this.index,newval);
+},
+
+_format: function(v) {
+  return v;
+},
+
+_display: function(v,gridCell) {
+  gridCell.innerHTML=this._format(v);
+},
+
+displayValue: function(windowRow) {
+  var bufCell=this.getBufferCell(windowRow);
+  if (!bufCell) {
+    this.clearCell(windowRow);
+    return;
+  }
+  var gridCell=this.cell(windowRow);
+  this._display(bufCell.content,gridCell,windowRow);
+  var acceptAttr=this.liveGrid.buffer.options.acceptAttr;
+  for (var k=0; k<acceptAttr.length; k++) {
+    var bufAttr=bufCell['_'+acceptAttr[k]] || '';
+    switch (acceptAttr[k]) {
+      case 'style': gridCell.style.cssText=bufAttr; break;
+      case 'class': gridCell.className=bufAttr; break;
+      default:      gridCell['_'+acceptAttr[k]]=bufAttr; break;
+    }
+  }
+}
+
+});
+
+Rico.TableColumn.checkbox = Class.create();
+
+Rico.TableColumn.checkbox.prototype = {
+
+  initialize: function(checkedValue, uncheckedValue, defaultValue, readOnly) {
+    this._checkedValue=checkedValue;
+    this._uncheckedValue=uncheckedValue;
+    this._defaultValue=defaultValue || false;
+    this._readOnly=readOnly || false;
+    this._checkboxes=[];
+  },
+
+  _create: function(gridCell,windowRow) {
+    this._checkboxes[windowRow]=RicoUtil.createFormField(gridCell,'input','checkbox',this.liveGrid.tableId+'_chkbox_'+this.index+'_'+windowRow);
+    this._clear(gridCell,windowRow);
+    if (this._readOnly)
+      this._checkboxes[windowRow].disabled=true;
+    else
+      Event.observe(this._checkboxes[windowRow], "click", this._onclick.bindAsEventListener(this), false);
+  },
+
+  _onclick: function(e) {
+    var elem=Event.element(e);
+    var windowRow=parseInt(elem.id.split(/_/).pop());
+    var newval=elem.checked ? this._checkedValue : this._uncheckedValue;
+    this.setValue(windowRow,newval);
+  },
+
+  _clear: function(gridCell,windowRow) {
+    this._checkboxes[windowRow].checked=this._defaultValue;
+  },
+
+  _display: function(v,gridCell,windowRow) {
+    this._checkboxes[windowRow].checked=(v==this._checkedValue);
+  }
+
+}
+
+Rico.TableColumn.link = Class.create();
+
+Rico.TableColumn.link.prototype = {
+
+  initialize: function(href,target) {
+    this._href=href;
+    this._target=target;
+    this._anchors=[];
+  },
+
+  _create: function(gridCell,windowRow) {
+    this._anchors[windowRow]=RicoUtil.createFormField(gridCell,'a',null,this.liveGrid.tableId+'_a_'+this.index+'_'+windowRow);
+    if (this._target) this._anchors[windowRow].target=this._target;
+    this._clear(gridCell,windowRow);
+  },
+
+  _clear: function(gridCell,windowRow) {
+    this._anchors[windowRow].href='';
+    this._anchors[windowRow].innerHTML='';
+  },
+
+  _display: function(v,gridCell,windowRow) {
+    this._anchors[windowRow].innerHTML=v;
+    var getWindowValue=this.liveGrid.buffer.getWindowValue.bind(this.liveGrid.buffer);
+    this._anchors[windowRow].href=this._href.replace(/\{\d+\}/g,
+      function ($1) {
+        var colIdx=parseInt($1.substr(1));
+        return getWindowValue(windowRow,colIdx);
+      }
+    );
+  }
+
+}
+
+Rico.TableColumn.lookup = Class.create();
+
+Rico.TableColumn.lookup.prototype = {
+
+  initialize: function(map, defaultCode, defaultDesc) {
+    this._map=map;
+    this._defaultCode=defaultCode || '';
+    this._defaultDesc=defaultDesc || '&nbsp;';
+    this._sortfunc=this._sortvalue.bind(this);
+    this._codes=[];
+    this._descriptions=[];
+  },
+
+  _create: function(gridCell,windowRow) {
+    this._descriptions[windowRow]=RicoUtil.createFormField(gridCell,'span',null,this.liveGrid.tableId+'_desc_'+this.index+'_'+windowRow);
+    this._codes[windowRow]=RicoUtil.createFormField(gridCell,'input','hidden',this.liveGrid.tableId+'_code_'+this.index+'_'+windowRow);
+    this._clear(gridCell,windowRow);
+  },
+
+  _clear: function(gridCell,windowRow) {
+    this._codes[windowRow].value=this._defaultCode;
+    this._descriptions[windowRow].innerHTML=this._defaultDesc;
+  },
+
+  _sortvalue: function(v) {
+    return this._getdesc(v).replace(/&amp;/g, '&').replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&nbsp;/g,' ');
+  },
+
+  _getdesc: function(v) {
+    var desc=this._map[v];
+    return (typeof desc=='string') ? desc : this._defaultDesc;
+  },
+
+  _display: function(v,gridCell,windowRow) {
+    this._codes[windowRow].value=v;
+    this._descriptions[windowRow].innerHTML=this._getdesc(v);
+  }
+
+}
+
+Rico.includeLoaded('ricoLiveGrid.js');
diff --git a/NP_TrackBack/trunk/trackback/js/rico/ricoLiveGridAjax.js b/NP_TrackBack/trunk/trackback/js/rico/ricoLiveGridAjax.js
new file mode 100644 (file)
index 0000000..ab193ff
--- /dev/null
@@ -0,0 +1,412 @@
+if(typeof Rico=='undefined') throw("LiveGridAjax requires the Rico JavaScript framework");
+if(typeof RicoUtil=='undefined') throw("LiveGridAjax requires the RicoUtil object");
+if(typeof Rico.Buffer=='undefined') throw("LiveGridAjax requires the Rico.Buffer object");
+
+
+/**
+ * Data source is a static XML file located on the server
+ */
+Rico.Buffer.AjaxXML = Class.create();
+
+Rico.Buffer.AjaxXML.prototype = {
+
+  initialize: function(url,options,ajaxOptions) {
+    Object.extend(this, new Rico.Buffer.Base());
+    Object.extend(this, new Rico.Buffer.AjaxXMLMethods);
+    this.dataSource=url;
+    this.options.bufferTimeout = 20000;  // time to wait for ajax response (milliseconds)
+    this.options.requestParameters = [];
+    Object.extend(this.options, options || {});
+    this.ajaxOptions = { parameters: null, method : 'get' };
+    Object.extend(this.ajaxOptions, ajaxOptions || {});
+    this.requestCount=0;
+    this.processingRequest=false;
+    this.pendingRequest=-1;
+  }
+}
+
+Rico.Buffer.AjaxXMLMethods = function() {};
+
+Rico.Buffer.AjaxXMLMethods.prototype = {
+
+  fetch: function(offset) {
+    if ( this.isInRange(offset) ) {
+      Rico.writeDebugMsg("AjaxXML fetch: in buffer");
+      this.liveGrid.refreshContents(offset);
+      return;
+    }
+    this.processingRequest=true
+    Rico.writeDebugMsg("AjaxXML fetch, offset="+offset);
+    this.liveGrid.showMsg("Waiting for data...");
+    this.timeoutHandler = setTimeout( this.handleTimedOut.bind(this), this.options.bufferTimeout);
+    this.sendAjaxRequest(offset,0,this.ajaxUpdate.bind(this,offset));
+  },
+
+/**
+ * Server did not respond in time... assume that there could have been
+ * an error, and allow requests to be processed again.
+ */
+  handleTimedOut: function() {
+    Rico.writeDebugMsg("Request Timed Out");
+    this.liveGrid.showMsg("Request for data timed out!");
+  },
+
+  formQueryHash: function(startPos,fetchSize) {
+    if (typeof fetchSize!='number') fetchSize=this.totalRows;
+    var queryHash= {
+      id: this.liveGrid.tableId,
+      page_size: fetchSize,
+      offset: startPos
+    };
+    if (!this.foundRowCount) queryHash['get_total']='true';
+    if (this.options.requestParameters) {
+      for ( var i=0; i < this.options.requestParameters.length; i++ ) {
+        var anArg = this.options.requestParameters[i];
+        if ( anArg.name != undefined && anArg.value != undefined ) {
+          queryHash[anArg.name]=anArg.value;
+        } else {
+          var ePos  = anArg.indexOf('=');
+          var argName  = anArg.substring( 0, ePos );
+          var argValue = anArg.substring( ePos + 1 );
+          queryHash[argName]=argValue;
+        }
+      }
+    }
+    
+    // sort
+    Object.extend(queryHash,this.sortParm);
+
+    // filters
+    for (n=0; n<this.liveGrid.columns.length; n++) {
+      var c=this.liveGrid.columns[n];
+      if (c.filterType == Rico.TableColumn.UNFILTERED) continue;
+      queryHash['f['+c.index+'][op]']=c.filterOp;
+      queryHash['f['+c.index+'][len]']=c.filterValues.length
+      for (var i=0; i<c.filterValues.length; i++)
+        queryHash['f['+c.index+']['+i+']']=c.filterValues[i];
+    }
+      
+    return $H(queryHash);
+  },
+
+  sendAjaxRequest: function(startPos,fetchSize,onComplete) {
+    this.ajaxOptions.parameters = this.formQueryHash(startPos,fetchSize);
+    this.ajaxOptions.onComplete = onComplete;
+    this.requestCount++;
+    Rico.writeDebugMsg('req '+this.requestCount+':'+this.ajaxOptions.parameters.inspect());
+    new Ajax.Request(this.dataSource, this.ajaxOptions);
+  },
+  
+  clearTimer: function() {
+    if(typeof this.timeoutHandler != "number") return;
+    window.clearTimeout(this.timeoutHandler);
+    delete this.timeoutHandler;
+  },
+
+  ajaxUpdate: function(startPos,request) {
+    this.clearTimer();
+    this.processingRequest=false;
+    if (request.status != 200) {
+      Rico.writeDebugMsg("ajaxUpdate: received http error="+request.status);
+      this.liveGrid.showMsg('Received HTTP error: '+request.status);
+      return;
+    }
+    var response = request.responseXML.getElementsByTagName("ajax-response");
+    if (response == null || response.length != 1) return;
+    this.updateBuffer(response[0],startPos);
+    this.CheckRowCount(response[0],startPos);
+    if (this.options.TimeOut && this.timerMsg)
+      this.restartSessionTimer();
+    if (this.options.onAjaxUpdate)
+      this.options.onAjaxUpdate();
+    if (this.pendingRequest>=0) {
+      var offset=this.pendingRequest;
+      Rico.writeDebugMsg("ajaxUpdate: found pending request for offset="+offset);
+      this.pendingRequest=-1;
+      this.fetch(offset);
+    }
+  },
+
+  CheckRowCount: function(ajaxResponse,offset) {
+    //try {
+      Rico.writeDebugMsg("CheckRowCount, size="+this.size+' rcv cnt type='+typeof(this.rowcntContent));
+      if (this.rcvdRowCount==true) {
+        Rico.writeDebugMsg("found row cnt: "+this.rowcntContent);
+        var eofrow=parseInt(this.rowcntContent);
+        var lastTotalRows=this.totalRows;
+        if (!isNaN(eofrow) && eofrow!=lastTotalRows) {
+          this.setTotalRows(eofrow);
+          var newpos=Math.min(this.liveGrid.topOfLastPage(),offset);
+          Rico.writeDebugMsg("CheckRowCount: new rowcnt="+eofrow+" newpos="+newpos);
+          if (lastTotalRows==0 && this.liveGrid.sizeTo=='data')
+            this.liveGrid.adjustPageSize();
+          //this.lastRowPos=-1;
+          this.liveGrid.scrollToRow(newpos);
+          if ( this.isInRange(newpos) ) {
+            this.liveGrid.refreshContents(newpos);
+          } else {
+            this.fetch(newpos);
+          }
+          return;
+        }
+      } else {
+        var lastbufrow=offset+this.rcvdRows;
+        if (lastbufrow>this.totalRows) {
+          var newcnt=lastbufrow;
+          Rico.writeDebugMsg("extending totrows to "+newcnt);
+          this.setTotalRows(newcnt);
+        }
+      }
+      var newpos=this.liveGrid.pixeltorow(this.liveGrid.scrollDiv.scrollTop);
+      Rico.writeDebugMsg("CheckRowCount: newpos="+newpos);
+      this.liveGrid.refreshContents(newpos);
+    //}
+    //catch(err) {
+    //  alert("Error in CheckRowCount:"+err.message);
+    //}
+  },
+
+  updateBuffer: function(ajaxResponse, start) {
+    Rico.writeDebugMsg("updateBuffer: "+start);
+    this.rcvdRows = 0;
+    var newRows = this.loadRows(ajaxResponse);
+    if (newRows==null) return;
+    this.rcvdRows = newRows.length;
+    Rico.writeDebugMsg("updateBuffer: # of rows="+this.rcvdRows);
+    if (this.rows.length == 0) { // initial load
+      this.rows = newRows;
+      this.startPos = start;
+    } else if (start > this.startPos) { //appending
+      if (this.startPos + this.rows.length < start) {
+        this.rows =  newRows;
+        this.startPos = start;//
+      } else {
+        this.rows = this.rows.concat( newRows.slice(0, newRows.length));
+        if (this.rows.length > this.maxBufferSize) {
+          var fullSize = this.rows.length;
+          this.rows = this.rows.slice(this.rows.length - this.maxBufferSize, this.rows.length)
+          this.startPos = this.startPos +  (fullSize - this.rows.length);
+        }
+      }
+    } else { //prepending
+      if (start + newRows.length < this.startPos) {
+        this.rows =  newRows;
+      } else {
+        this.rows = newRows.slice(0, this.startPos).concat(this.rows);
+        if (this.maxBufferSize && this.rows.length > this.maxBufferSize)
+          this.rows = this.rows.slice(0, this.maxBufferSize)
+      }
+      this.startPos =  start;
+    }
+    this.size = this.rows.length;
+  },
+
+  loadRows: function(ajaxResponse) {
+    Rico.writeDebugMsg("loadRows");
+    this.rcvdRowCount = false;
+    var debugtags = ajaxResponse.getElementsByTagName('debug');
+    for (var i=0; i<debugtags.length; i++)
+      Rico.writeDebugMsg("loadRows, debug msg "+i+": "+RicoUtil.getContentAsString(debugtags[i],this.options.isEncoded));
+    var error = ajaxResponse.getElementsByTagName('error');
+    if (error.length > 0) {
+      var msg=RicoUtil.getContentAsString(error[0],this.options.isEncoded);
+      alert("Data provider returned an error:\n"+msg);
+      Rico.writeDebugMsg("Data provider returned an error:\n"+msg);
+      return null;
+    }
+    var rowsElement = ajaxResponse.getElementsByTagName('rows')[0];
+    var rowcnttags = ajaxResponse.getElementsByTagName('rowcount');
+    if (rowcnttags && rowcnttags.length==1) {
+      this.rowcntContent = RicoUtil.getContentAsString(rowcnttags[0],this.options.isEncoded);
+      this.rcvdRowCount = true;
+      this.foundRowCount = true;
+      Rico.writeDebugMsg("loadRows, found RowCount="+this.rowcntContent);
+    }
+    this.updateUI = rowsElement.getAttribute("update_ui") == "true";
+    this.rcvdOffset = rowsElement.getAttribute("offset");
+    Rico.writeDebugMsg("loadRows, rcvdOffset="+this.rcvdOffset);
+    return this.dom2jstable(rowsElement);
+  }
+
+};
+
+
+
+Rico.Buffer.AjaxSQL = Class.create();
+
+Rico.Buffer.AjaxSQL.prototype = {
+
+  initialize: function(url,options,ajaxOptions) {
+    Object.extend(this, new Rico.Buffer.AjaxXML());
+    Object.extend(this, new Rico.Buffer.AjaxSQLMethods());
+    this.dataSource=url;
+    this.options.canFilter=true;
+    this.options.largeBufferSize  = 7.0;   // 7 pages
+    this.options.nearLimitFactor  = 1.0;   // 1 page
+    Object.extend(this.options, options || {});
+    Object.extend(this.ajaxOptions, ajaxOptions || {});
+    this.sortParm={};
+  }
+}
+  
+Rico.Buffer.AjaxSQLMethods = function() {};
+
+Rico.Buffer.AjaxSQLMethods.prototype = {
+
+  registerGrid: function(liveGrid) {
+    this.liveGrid = liveGrid;
+    this.sessionExpired=false;
+    this.timerMsg=$(liveGrid.tableId+'_timer');
+    if (this.options.TimeOut && this.timerMsg) {
+      if (!this.timerMsg.title) this.timerMsg.title=RicoTranslate.getPhrase("minutes before your session expires")
+      this.restartSessionTimer();
+    }
+  },
+  
+  setBufferSize: function(pageSize) {
+    this.maxFetchSize = Math.max(50,parseInt(this.options.largeBufferSize * pageSize));
+    this.nearLimit = parseInt(this.options.nearLimitFactor * pageSize);
+    this.maxBufferSize = this.maxFetchSize * 3;
+  },
+
+  restartSessionTimer: function() {
+    if (this.sessionExpired==true) return;
+    this.timeRemaining=this.options.TimeOut+1;
+    if (this.sessionTimer) clearTimeout(this.sessionTimer);
+    this.updateSessionTimer();
+  },
+  
+  updateSessionTimer: function() {
+    if (--this.timeRemaining<=0) {
+      this.displaySessionTimer(RicoTranslate.getPhrase("EXPIRED"));
+      this.timerMsg.style.backgroundColor="red";
+      this.sessionExpired=true;
+    } else {
+      this.displaySessionTimer(this.timeRemaining);
+      this.sessionTimer=setTimeout(this.updateSessionTimer.bind(this),60000);
+    }
+  },
+  
+  displaySessionTimer: function(msg) {
+    this.timerMsg.innerHTML='&nbsp;'+msg+'&nbsp;';
+  },
+  
+  refresh: function() {
+    this.fetch(this.lastOffset);
+  },
+  
+  /**
+   * Fetch data from database.
+   * @param offset position (row) within the dataset (-1=clear existing buffer before issuing request)
+   */
+  fetch: function(offset) {
+    Rico.writeDebugMsg("AjaxSQL fetch, offset="+offset+' lastOffset='+this.lastOffset);
+    if (this.processingRequest) {
+      Rico.writeDebugMsg("AjaxSQL fetch: queue request");
+      this.pendingRequest=offset;
+      return;
+    }
+    if (offset < 0) {
+      this.clear();
+      this.setTotalRows(0);
+      this.foundRowCount = false;
+      offset=0;
+    }
+    var lastOffset = this.lastOffset;
+    this.lastOffset = offset;
+    var inRange=this.isInRange(offset);
+    if (inRange) {
+      Rico.writeDebugMsg("AjaxSQL fetch: in buffer");
+      this.liveGrid.refreshContents(offset);
+      if (offset > lastOffset) {
+        if (offset+this.liveGrid.pageSize < this.endPos()-this.nearLimit) return;
+        if (this.endPos()==this.totalRows && this.foundRowCount) return;
+      } else if (offset < lastOffset) {
+        if (offset > this.startPos+this.nearLimit) return;
+        if (this.startPos==0) return;
+      } else return;
+    }
+    if (offset >= this.totalRows && this.foundRowCount) return;
+    
+    this.processingRequest=true
+    Rico.writeDebugMsg("AjaxSQL fetch, processing offset="+offset);
+    var bufferStartPos = this.getFetchOffset(offset);
+    var fetchSize = this.getFetchSize(bufferStartPos);
+    var partialLoaded = false;
+
+    if (!inRange) this.liveGrid.showMsg("Waiting for data...");
+    this.timeoutHandler = setTimeout( this.handleTimedOut.bind(this), this.options.bufferTimeout);
+    this.sendAjaxRequest(bufferStartPos,fetchSize,this.ajaxUpdate.bind(this,bufferStartPos));
+  },
+
+  getFetchSize: function(adjustedOffset) {
+    var adjustedSize = 0;
+    if (adjustedOffset >= this.startPos) { //appending
+      var endFetchOffset = this.maxFetchSize + adjustedOffset;
+      adjustedSize = endFetchOffset - adjustedOffset;
+      if(adjustedOffset == 0 && adjustedSize < this.maxFetchSize)
+        adjustedSize = this.maxFetchSize;
+      Rico.writeDebugMsg("getFetchSize/append, adjustedSize="+adjustedSize+" adjustedOffset="+adjustedOffset+' endFetchOffset='+endFetchOffset);
+    } else { //prepending
+      adjustedSize = Math.min(this.startPos - adjustedOffset,this.maxFetchSize);
+    }
+    return adjustedSize;
+  },
+
+  getFetchOffset: function(offset) {
+    var adjustedOffset = offset;
+    if (offset > this.startPos)
+      adjustedOffset = Math.max(offset, this.endPos());  //appending
+    else if (offset + this.maxFetchSize >= this.startPos)
+      adjustedOffset = Math.max(this.startPos - this.maxFetchSize, 0);  //prepending
+    return adjustedOffset;
+  },
+
+  sortBuffer: function(colnum,sortdir,coltype) {
+    this.sortParm={};
+    if (this.options.sortParmFmt && this.options.sortParmFmt=='displayName') {
+      this.sortParm['sort_col']=this.liveGrid.columns[colnum].displayName.toLowerCase();
+      this.sortParm['sort_dir']=sortdir;
+    }else{
+     this.sortParm['s'+colnum]=sortdir;
+    }
+    this.clear();
+  },
+  
+  exportAllRows: function(populate,finish) {
+    this.exportPopulate=populate;
+    this.exportFinish=finish;
+    this.liveGrid.showMsg("Waiting for data...");
+    this.sendExportRequest(0);
+  },
+  
+/**
+ * Make ajax request for print window data
+ */
+  sendExportRequest: function(offset) {
+    this.timeoutHandler = setTimeout(this.exportTimedOut.bind(this), this.options.bufferTimeout);
+    this.sendAjaxRequest(offset,200,this.exportAppend.bind(this,offset));
+  },
+
+  exportTimedOut: function() {
+    Rico.writeDebugMsg("Print Request Timed Out");
+    this.liveGrid.showMsg("Request for data timed out!");
+    this.exportFinish();
+  },
+
+  exportAppend: function(startPos,request) {
+    this.clearTimer();
+    var response = request.responseXML.getElementsByTagName("ajax-response");
+    if (response == null || response.length != 1) return;
+    var rowsElement = response[0].getElementsByTagName('rows')[0];
+    var rows=this.dom2jstable(rowsElement);
+    this.exportPopulate(rows);
+    if (rows.length==0)
+      this.exportFinish();
+    else
+      this.sendExportRequest(startPos+rows.length);
+  }
+
+};
+
+Rico.includeLoaded('ricoLiveGridAjax.js');
diff --git a/NP_TrackBack/trunk/trackback/js/rico/ricoLiveGridForms.js b/NP_TrackBack/trunk/trackback/js/rico/ricoLiveGridForms.js
new file mode 100644 (file)
index 0000000..3e60c45
--- /dev/null
@@ -0,0 +1,830 @@
+if(typeof Rico=='undefined') throw("LiveGridForms requires the Rico JavaScript framework");\r
+if(typeof RicoUtil=='undefined') throw("LiveGridForms requires the RicoUtil object");\r
+if(typeof RicoTranslate=='undefined') throw("LiveGridForms requires the RicoTranslate object");\r
+\r
+\r
+Rico.TableEdit = Class.create();\r
+\r
+Rico.TableEdit.prototype = {\r
+\r
+  initialize: function(liveGrid) {\r
+    Rico.writeDebugMsg('Rico.TableEdit initialize: '+liveGrid.tableId);\r
+    this.grid=liveGrid;\r
+    this.options = {\r
+      maxDisplayLen    : 20,    // max displayed text field length\r
+      panelHeight      : 200,   // size of tabbed panels\r
+      panelWidth       : 500,\r
+      hoverClass       : 'tabHover',\r
+      selectedClass    : 'tabSelected',\r
+      compact          : false,    // compact corners\r
+      RecordName       : 'record',\r
+      readOnlyColor    : '#AAA',   // read-only fields displayed using this color\r
+      showSaveMsg      : 'errors'  // disposition of database update responses (full - show full response, errors - show full response for errors and short response otherwise)
+    }
+    Object.extend(this.options, liveGrid.options);
+    this.menu=liveGrid.menu;\r
+    this.menu.options.dataMenuHandler=this.editMenu.bind(this);\r
+    this.menu.ignoreClicks();\r
+    RicoEditControls.atLoad();\r
+    this.createEditDiv();\r
+    this.saveMsg=$(liveGrid.tableId+'_savemsg');\r
+    Event.observe(document,"click", this.clearSaveMsg.bindAsEventListener(this), false);\r
+    this.TEerror=false;\r
+    this.extraMenuItems=new Array();\r
+    this.responseHandler=this.processResponse.bind(this);\r
+  },\r
+  \r
+  createEditDiv: function() {\r
+\r
+    // create editDiv (form)\r
+    \r
+    this.requestCount=1;\r
+    this.editDiv = this.grid.createDiv('edit',document.body);\r
+    this.editDiv.style.display='none';\r
+    if (this.options.canEdit || this.options.canAdd) {\r
+      this.startForm();\r
+      this.createForm(this.form);\r
+    } else {\r
+      var button=this.createButton("Close");\r
+      Event.observe(button,"click", this.cancelEdit.bindAsEventListener(this), false);
+      this.createForm(this.editDiv);\r
+    }\r
+    this.editDivCreated=true;\r
+    this.formPopup=new Rico.Popup({ignoreClicks:true},this.editDiv);\r
+\r
+    // create responseDialog\r
+    \r
+    this.responseDialog = this.grid.createDiv('editResponse',document.body);\r
+    this.responseDialog.style.display='none';\r
+    \r
+    var button = document.createElement('button');\r
+       button.appendChild(document.createTextNode('OK'));\r
+    button.onclick=this.ackResponse.bindAsEventListener(this);
+    this.responseDialog.appendChild(button);
+\r
+    this.responseDiv = this.grid.createDiv('editResponseText',this.responseDialog);\r
+\r
+    if (this.panelGroup) {\r
+      Rico.writeDebugMsg("createEditDiv complete, requestCount="+this.requestCount);
+      setTimeout(this.initPanelGroup.bind(this),50);\r
+    }\r
+  },\r
+  \r
+  initPanelGroup: function() {\r
+    this.requestCount--;\r
+    Rico.writeDebugMsg("initPanelGroup: "+this.requestCount);
+    if (this.requestCount>0) return;\r
+    var wi=parseInt(this.options.panelWidth);\r
+    this.form.style.width=(wi+10)+'px';
+    if (Prototype.Browser.WebKit) this.editDiv.style.display='block';  // this causes display to flash briefly\r
+    this.options.bgColor = Rico.Color.createColorFromBackground(this.form);\r
+    this.editDiv.style.display='none';\r
+    this.options.panelHdrWidth=(Math.floor(wi / this.options.panels.length)-4)+'px';\r
+    this.Accordion=new Rico.TabbedPanel(this.panelHdr.findAll(this.notEmpty), this.panelContent.findAll(this.notEmpty), this.options);\r
+  },\r
+  \r
+  notEmpty: function(v) {\r
+    return typeof(v)!='undefined';\r
+  },\r
+  \r
+  startForm: function() {\r
+    this.form = document.createElement('form');\r
+    this.form.onsubmit=function() {return false;};
+    this.editDiv.appendChild(this.form);\r
+\r
+    var tab = document.createElement('table');\r
+    var row = tab.insertRow(-1);
+    var cell = row.insertCell(-1);
+    var button=cell.appendChild(this.createButton("Save \t"+this.options.RecordName));\r
+    Event.observe(button,"click", this.TESubmit.bindAsEventListener(this), false);
+    var cell = row.insertCell(-1);
+    var button=cell.appendChild(this.createButton("Cancel"));\r
+    Event.observe(button,"click", this.cancelEdit.bindAsEventListener(this), false);
+    this.form.appendChild(tab);
+\r
+    // hidden fields\r
+    this.hiddenFields = document.createElement('div');
+    this.hiddenFields.style.display='none';\r
+    this.action = this.appendHiddenField(this.grid.tableId+'__action','');\r
+    for (var i=0; i<this.grid.columns.length; i++) {\r
+      var fldSpec=this.grid.columns[i].format;\r
+      if (fldSpec && fldSpec.FormView && fldSpec.FormView=="hidden")\r
+        this.appendHiddenField(fldSpec.FieldName,fldSpec.ColData);\r
+    }\r
+    this.form.appendChild(this.hiddenFields);
+  },\r
+  \r
+  createButton: function(buttonLabel) {\r
+    var button = document.createElement('button');\r
+    buttonLabel=RicoTranslate.getPhrase(buttonLabel);\r
+       button.innerHTML="<span style='text-decoration:underline;'>"+buttonLabel.charAt(0)+"</span>"+buttonLabel.substr(1);\r
+    button.accessKey=buttonLabel.charAt(0);\r
+    return button;
+  },\r
+  \r
+  createPanel: function(i) {\r
+    var hasFields=false;\r
+    for (var j=0; j<this.grid.columns.length; j++) {\r
+      var fldSpec=this.grid.columns[j].format;\r
+      if (!fldSpec) continue;\r
+      if (!fldSpec.EntryType) continue\r
+      if (fldSpec.EntryType=='H') continue;\r
+      var panelIdx=fldSpec.panelIdx || 0;\r
+      if (panelIdx==i) {\r
+        hasFields=true;\r
+        break;\r
+      }\r
+    }\r
+    if (!hasFields) return false;\r
+    this.panelHdr[i] = document.createElement('div');\r
+    this.panelHdr[i].className='tabHeader';\r
+    this.panelHdr[i].innerHTML=this.options.panels[i];
+    this.panelHdrs.appendChild(this.panelHdr[i]);
+    this.panelContent[i] = document.createElement('div');
+    this.panelContent[i].className='tabContent';\r
+    this.panelContents.appendChild(this.panelContent[i]);\r
+    return true;\r
+  },\r
+  \r
+  createForm: function(parentDiv) {\r
+    var tables=[];\r
+    this.panelHdr=[];\r
+    this.panelContent=[];\r
+    if (this.options.panels) {\r
+      this.panelGroup = document.createElement('div');\r
+      this.panelGroup.className='tabPanelGroup';
+      this.panelHdrs = document.createElement('div');\r
+      this.panelGroup.appendChild(this.panelHdrs);\r
+      this.panelContents = document.createElement('div');\r
+      this.panelContents.className='tabContentContainer';
+      this.panelGroup.appendChild(this.panelContents);\r
+      parentDiv.appendChild(this.panelGroup);\r
+      if (this.grid.direction=='rtl') {\r
+        for (var i=this.options.panels.length-1; i>=0; i--)\r
+          if (this.createPanel(i))\r
+            tables[i]=this.createFormTable(this.panelContent[i],'tabContent');\r
+      } else {\r
+        for (var i=0; i<this.options.panels.length; i++)\r
+          if (this.createPanel(i))\r
+            tables[i]=this.createFormTable(this.panelContent[i],'tabContent');\r
+      }\r
+      parentDiv.appendChild(this.panelGroup);\r
+    } else {\r
+      var div=document.createElement('div');
+      div.className='noTabContent';\r
+      tables[0]=this.createFormTable(div);\r
+      parentDiv.appendChild(div);\r
+    }\r
+    for (var i=0; i<this.grid.columns.length; i++) {\r
+      var fldSpec=this.grid.columns[i].format;\r
+      if (!fldSpec) continue;\r
+      var panelIdx=fldSpec.panelIdx || 0;\r
+      if (tables[panelIdx]) this.appendFormField(this.grid.columns[i],tables[panelIdx]);\r
+    }\r
+  },\r
+  \r
+  createFormTable: function(div) {\r
+    var tab=document.createElement('table');\r
+    tab.border=0;
+    div.appendChild(tab);
+    return tab;
+  },\r
+  \r
+  appendHiddenField: function(name,value) {\r
+    var field=RicoUtil.createFormField(this.hiddenFields,'input','hidden',name,name);\r
+    field.value=value;
+    return field;
+  },\r
+  \r
+  appendFormField: function(column, table) {\r
+    if (!column.format.EntryType) return;\r
+    if (column.format.EntryType=="H") return;\r
+    if (column.format.FormView) return;\r
+    Rico.writeDebugMsg('appendFormField: '+column.format.Hdg+' - '+column.format.EntryType);\r
+    var row = table.insertRow(-1);\r
+    var hdr = row.insertCell(-1);
+    column.formLabel=hdr;
+    if (hdr.noWrap) hdr.noWrap=true;
+    var entry = row.insertCell(-1);\r
+    if (entry.noWrap) entry.noWrap=true;
+    hdr.innerHTML=column.format.Hdg;\r
+    hdr.className='ricoEditLabel';\r
+    if (column.format.Help) {\r
+      hdr.title=column.format.Help;\r
+      hdr.className='ricoEditLabelWithHelp';\r
+    }\r
+    var field, name=column.format.FieldName;\r
+    switch (column.format.EntryType) {\r
+      case 'TA','tinyMCE':\r
+        field=RicoUtil.createFormField(entry,'textarea',null,name);\r
+        field.cols=column.format.TxtAreaCols;\r
+        field.rows=column.format.TxtAreaRows;\r
+        field.innerHTML=column.format.ColData;\r
+        hdr.style.verticalAlign='top';\r
+        break;\r
+      case 'R':\r
+      case 'RL':\r
+        field=RicoUtil.createFormField(entry,'div',null,name);\r
+        if (column.format.isNullable)\r
+          this.addSelectOption(field,this.options.TableSelectNone,"(none)");\r
+        this.selectValuesRequest(field,column.format);\r
+        break;\r
+      case 'N':\r
+        field=RicoUtil.createFormField(entry,'select',null,name);\r
+        if (column.format.isNullable)\r
+          this.addSelectOption(field,this.options.TableSelectNone,"(none)");\r
+        field.onchange=this.checkSelectNew.bindAsEventListener(this);\r
+        this.selectValuesRequest(field,column.format);\r
+        field=document.createElement('span');\r
+        field.className='ricoEditLabel';\r
+        field.id='labelnew__'+column.format.FieldName;\r
+        field.innerHTML='&nbsp;&nbsp;&nbsp;New&nbsp;value:';\r
+        entry.appendChild(field);\r
+        name='textnew__'+column.format.FieldName;\r
+        field=RicoUtil.createFormField(entry,'input','text',name,name);\r
+        break;\r
+      case 'S':\r
+      case 'SL':\r
+        field=RicoUtil.createFormField(entry,'select',null,name);\r
+        if (column.format.isNullable)\r
+          this.addSelectOption(field,this.options.TableSelectNone,"(none)");\r
+        this.selectValuesRequest(field,column.format);\r
+        break;\r
+      default:\r
+        field=RicoUtil.createFormField(entry,'input','text',name,name);\r
+        if (column.format.Length) {\r
+          field.maxLength=column.format.Length;\r
+          field.size=Math.min(column.format.Length, this.options.maxDisplayLen);\r
+        }\r
+        field.value=column.format.ColData;\r
+        break;\r
+    }\r
+    if (field) {\r
+      if (column.format.SelectCtl)\r
+        RicoEditControls.applyTo(column,field);\r
+    }\r
+  },\r
+  \r
+  checkSelectNew: function(e) {\r
+    this.updateSelectNew(Event.element(e));\r
+  },\r
+  \r
+  updateSelectNew: function(SelObj) {\r
+    var vis=(SelObj.value==this.options.TableSelectNew) ? "" : "hidden";\r
+    $("labelnew__" + SelObj.id).style.visibility=vis\r
+    $("textnew__" + SelObj.id).style.visibility=vis\r
+  },\r
+\r
+  selectValuesRequest: function(elem,fldSpec) {\r
+    if (fldSpec.SelectValues) {\r
+      var valueList=fldSpec.SelectValues.split(',');\r
+      for (var i=0; i<valueList.length; i++)\r
+        this.addSelectOption(elem,valueList[i],valueList[i],i);\r
+    } else {\r
+      this.requestCount++;\r
+      var options={};\r
+      Object.extend(options, this.grid.buffer.ajaxOptions);\r
+      options.parameters = 'id='+fldSpec.FieldName+'&offset=0&page_size=-1';
+      options.onComplete = this.selectValuesUpdate.bind(this);
+      new Ajax.Request(this.grid.buffer.dataSource, options);
+      Rico.writeDebugMsg("selectValuesRequest: "+options.parameters);
+    }\r
+  },\r
+  \r
+  selectValuesUpdate: function(request) {\r
+    var response = request.responseXML.getElementsByTagName("ajax-response");
+    Rico.writeDebugMsg("selectValuesUpdate: "+request.status);
+    if (response == null || response.length != 1) return;
+    response=response[0];
+    var error = response.getElementsByTagName('error');
+    if (error.length > 0) {
+      Rico.writeDebugMsg("Data provider returned an error:\n"+RicoUtil.getContentAsString(error[0],this.grid.buffer.isEncoded));
+      alert(RicoTranslate.getPhrase("The request returned an error")+":\n"+RicoUtil.getContentAsString(error[0],this.grid.buffer.isEncoded));
+      return null;
+    }\r
+    response=response.getElementsByTagName('response')[0];\r
+    var id = response.getAttribute("id").slice(0,-8);
+    var rowsElement = response.getElementsByTagName('rows')[0];\r
+    var rows = this.grid.buffer.dom2jstable(rowsElement);\r
+    var elem=$(id);\r
+    //alert('selectValuesUpdate:'+id+' '+elem.tagName);
+    Rico.writeDebugMsg("selectValuesUpdate: id="+id+' rows='+rows.length);
+    for (var i=0; i<rows.length; i++) {\r
+      if (rows[i].length>0) {\r
+        var c0=rows[i][0].content;\r
+        var c1=(rows[i].length>1) ? rows[i][1].content : c0;\r
+        this.addSelectOption(elem,c0,c1,i);\r
+      }\r
+    }
+    if ($('textnew__'+id))\r
+      this.addSelectOption(elem,this.options.TableSelectNew,"(new value)");\r
+    if (this.panelGroup)\r
+      setTimeout(this.initPanelGroup.bind(this),50);\r
+  },\r
+  \r
+  addSelectOption: function(elem,value,text,idx) {\r
+    switch (elem.tagName.toLowerCase()) {\r
+      case 'div':\r
+        var opt=RicoUtil.createFormField(elem,'input','radio',elem.id+'_'+idx,elem.id);\r
+        opt.value=value;\r
+        var lbl=document.createElement('label');\r
+        lbl.innerHTML=text;\r
+        lbl.htmlFor=opt.id;\r
+        elem.appendChild(lbl);\r
+        break;\r
+      case 'select':\r
+        var opt=document.createElement('option');\r
+        opt.value=value;\r
+        opt.text=text;\r
+        //elem.options.add(opt);\r
+        if (Prototype.Browser.IE)\r
+          elem.add(opt);\r
+        else\r
+          elem.add(opt,null);\r
+        break;\r
+    }\r
+  },\r
+  \r
+  clearSaveMsg: function() {\r
+    if (this.saveMsg) this.saveMsg.innerHTML="";\r
+  },\r
+  \r
+  addMenuItem: function(menuText,menuAction,enabled) {\r
+    this.extraMenuItems.push({menuText:menuText,menuAction:menuAction,enabled:enabled});\r
+  },\r
+\r
+  editMenu: function(grid,r,c,onBlankRow) {\r
+    this.clearSaveMsg();\r
+    if (this.grid.buffer.sessionExpired==true || this.grid.buffer.startPos<0) return;\r
+    this.rowIdx=r;\r
+    var elemTitle=$('pageTitle');\r
+    var pageTitle=elemTitle ? elemTitle.innerHTML : document.title;\r
+    this.menu.addMenuHeading(pageTitle);\r
+    for (var i=0; i<this.extraMenuItems.length; i++) {\r
+      this.menu.addMenuItem(this.extraMenuItems[i].menuText,this.extraMenuItems[i].menuAction,this.extraMenuItems[i].enabled);\r
+    }\r
+    if (onBlankRow==false) {\r
+      this.menu.addMenuItem("Edit\t this "+this.options.RecordName,this.editRecord.bindAsEventListener(this),this.options.canEdit);\r
+      this.menu.addMenuItem("Delete\t this "+this.options.RecordName,this.deleteRecord.bindAsEventListener(this),this.options.canDelete);\r
+    }\r
+    this.menu.addMenuItem("Add\t new "+this.options.RecordName,this.addRecord.bindAsEventListener(this),this.options.canAdd);\r
+    return true;\r
+  },\r
+\r
+  cancelEdit: function(e) {\r
+    Event.stop(e);\r
+    for (var i=0; i<this.grid.columns.length; i++)\r
+      if (this.grid.columns[i].format && this.grid.columns[i].format.SelectCtl)\r
+        RicoEditControls.close(this.grid.columns[i].format.SelectCtl);\r
+    this.makeFormInvisible();\r
+    this.grid.highlightEnabled=true;\r
+    this.menu.cancelmenu();\r
+    return false;\r
+  },\r
+\r
+  setField: function(fldSpec,fldvalue) {\r
+    var e=$(fldSpec.FieldName);\r
+    if (!e) return;\r
+    //alert('setField: '+fldSpec.FieldName+'='+fldvalue);\r
+    switch (e.tagName.toUpperCase()) {\r
+      case 'DIV':\r
+        var elems=e.getElementsByTagName('INPUT');\r
+        var fldcode=this.getLookupValue(fldvalue)[0];\r
+        for (var i=0; i<elems.length; i++)\r
+          elems[i].checked=(elems[i].value==fldcode);\r
+        break;\r
+      case 'INPUT':\r
+        if (fldSpec.SelectCtl)\r
+          fldvalue=this.getLookupValue(fldvalue)[0];\r
+        switch (e.type.toUpperCase()) {\r
+          case 'HIDDEN':\r
+          case 'TEXT':\r
+            e.value=fldvalue;\r
+            break;\r
+        }\r
+        break;\r
+      case 'SELECT':\r
+        var opts=e.options;\r
+        var fldcode=this.getLookupValue(fldvalue)[0];\r
+        //alert('setField SELECT: id='+e.id+'\nvalue='+fldcode+'\nopt cnt='+opts.length)\r
+        for (var i=0; i<opts.length; i++) {\r
+          if (opts[i].value==fldcode) {\r
+            e.selectedIndex=i;\r
+            break;\r
+          }\r
+        }\r
+        if (fldSpec.EntryType=='N') {\r
+          var txt=$('textnew__'+e.id);\r
+          if (!txt) alert('Warning: unable to find id "textnew__'+e.id+'"');\r
+          txt.value=fldvalue;\r
+          if (e.selectedIndex!=i) e.selectedIndex=opts.length-1;\r
+          this.updateSelectNew(e);\r
+        }\r
+        return;\r
+      case 'TEXTAREA':\r
+        e.value=fldvalue;\r
+        if (fldSpec.EntryType=='tinyMCE' && typeof(tinyMCE)!='undefined' && this.initialized)\r
+          tinyMCE.updateContent(e.id);\r
+        return;\r
+    }\r
+  },\r
+  \r
+  getLookupValue: function(value) {\r
+    if (typeof value!='string')\r
+      return ['',''];\r
+    else if (value.match(/<span\s+class=(['"]?)ricolookup\1>(.*)<\/span>/i))\r
+      return [RegExp.$2,RegExp.leftContext];\r
+    else\r
+      return [value,value];\r
+  },\r
+  \r
+  // use with care: Prototype 1.5 does not include disabled fields in the post-back\r
+  setReadOnly: function(addFlag) {\r
+    for (var i=0; i<this.grid.columns.length; i++) {\r
+      var fldSpec=this.grid.columns[i].format;\r
+      if (!fldSpec) continue;\r
+      var e=$(fldSpec.FieldName);\r
+      if (!e) continue;\r
+      var ro=!fldSpec.Writeable || fldSpec.ReadOnly || (fldSpec.InsertOnly && !addFlag) || (fldSpec.UpdateOnly && addFlag);\r
+      var color=ro ? this.options.readOnlyColor : '';\r
+      switch (e.tagName.toUpperCase()) {\r
+        case 'DIV':\r
+          var elems=e.getElementsByTagName('INPUT');\r
+          for (var j=0; j<elems.length; j++)\r
+            elems[j].disabled=ro;\r
+          break;\r
+        case 'SELECT':\r
+          if (fldSpec.EntryType=='N') {\r
+            var txt=$('textnew__'+e.id);\r
+            txt.disabled=ro;\r
+          }\r
+          e.disabled=ro;\r
+          break;\r
+        case 'TEXTAREA':\r
+        case 'INPUT':\r
+          e.readOnly=ro;\r
+          e.style.color=color;\r
+          if (fldSpec.selectIcon) fldSpec.selectIcon.style.display=ro ? 'none' : '';\r
+          break;\r
+      }\r
+    }\r
+  },\r
+  \r
+  hideResponse: function(msg) {\r
+    this.responseDiv.innerHTML=msg;\r
+    this.responseDialog.style.display='none';\r
+  },\r
+  \r
+  showResponse: function() {\r
+    var offset=Position.page(this.grid.outerDiv);\r
+    offset[1]+=RicoUtil.docScrollTop();\r
+    this.responseDialog.style.top=offset[1]+"px";\r
+    this.responseDialog.style.display='';\r
+  },\r
+  \r
+  processResponse: function() {\r
+    var ch=this.responseDiv.childNodes;\r
+    for (var i=ch.length-1; i>=0; i--) {\r
+      if (ch[i].nodeType==1 && ch[i].nodeName!='P' && ch[i].nodeName!='DIV' && ch[i].nodeName!='BR')\r
+        this.responseDiv.removeChild(ch[i]);\r
+    }\r
+    var responseText=this.responseDiv.innerHTML;\r
+    if (responseText.toLowerCase().indexOf('error')==-1 && this.options.showSaveMsg!='full') {\r
+      this.hideResponse('');\r
+      this.grid.resetContents();\r
+      this.grid.buffer.foundRowCount = false;\r
+      this.grid.buffer.fetch(this.grid.lastRowPos || 0);\r
+      if (this.saveMsg) this.saveMsg.innerHTML='&nbsp;'+responseText.stripTags()+'&nbsp;';\r
+    }\r
+    this.processCallback(this.options.onSubmitResponse);\r
+  },\r
+  \r
+  processCallback: function(callback) {\r
+    switch (typeof callback) {\r
+      case 'string': eval(callback); break;\r
+      case 'function': callback(); break;\r
+    }\r
+  },\r
+  \r
+  // called when ok pressed on error response message\r
+  ackResponse: function() {\r
+    this.hideResponse('');\r
+    this.grid.highlightEnabled=true;\r
+  },\r
+\r
+  editRecord: function(e) {\r
+    this.grid.highlightEnabled=false;\r
+    this.menu.hidemenu();\r
+    this.hideResponse('Saving...');\r
+    this.grid.outerDiv.style.cursor = 'auto';\r
+    this.action.value="upd";\r
+    for (var i=0; i<this.grid.columns.length; i++) {\r
+      if (this.grid.columns[i].format) {\r
+        var v=this.grid.columns[i].getValue(this.rowIdx);\r
+        this.setField(this.grid.columns[i].format,v);\r
+        if (this.grid.columns[i].format.selectDesc)\r
+          this.grid.columns[i].format.selectDesc.innerHTML=this.grid.columns[i]._format(v);\r
+      }\r
+    }\r
+    this.setReadOnly(false);\r
+    this.key=this.getKey();\r
+    this.makeFormVisible(this.rowIdx);\r
+  },\r
+\r
+  addRecord: function() {\r
+    this.menu.hidemenu();\r
+    this.hideResponse('Saving...');\r
+    this.setReadOnly(true);\r
+    this.form.reset();\r
+    this.action.value="ins";\r
+    for (var i=0; i<this.grid.columns.length; i++) {\r
+      if (this.grid.columns[i].format) {\r
+        this.setField(this.grid.columns[i].format,this.grid.columns[i].format.ColData);\r
+        if (this.grid.columns[i].format.SelectCtl)\r
+          RicoEditControls.resetValue(this.grid.columns[i]);\r
+      }\r
+    }\r
+    this.key='';\r
+    this.makeFormVisible(-1);\r
+    if (this.Accordion) this.Accordion.selectionSet.selectIndex(0);\r
+  },\r
+  \r
+  drillDown: function(e,masterColNum,detailColNum) {\r
+    var cell=Event.element(e || window.event);\r
+    cell=RicoUtil.getParentByTagName(cell,'div','ricoLG_cell');\r
+    if (!cell) return;\r
+    this.grid.unhighlight();\r
+    var idx=this.grid.winCellIndex(cell);\r
+    this.grid.menuIdx=idx;  // ensures selection gets cleared when menu is displayed\r
+    this.grid.highlight(idx);\r
+    var drillValue=this.grid.columns[masterColNum].getValue(idx.row);\r
+    for (var i=3; i<arguments.length; i++)\r
+      arguments[i].setDetailFilter(detailColNum,drillValue);\r
+    return idx.row;\r
+  },\r
+  \r
+  // set filter on a detail grid that is in a master-detail relationship\r
+  setDetailFilter: function(colNumber,filterValue) {\r
+    var c=this.grid.columns[colNumber];\r
+    c.format.ColData=filterValue;\r
+    c.setSystemFilter('EQ',filterValue);\r
+  },\r
+  \r
+  makeFormVisible: function(row) {\r
+    this.editDiv.style.display='block';\r
+\r
+    // set left position\r
+    var editWi=this.editDiv.offsetWidth;\r
+    var odOffset=Position.page(this.grid.outerDiv);\r
+    var winWi=RicoUtil.windowWidth();\r
+    if (editWi+odOffset[0] > winWi)\r
+      this.editDiv.style.left=(winWi-editWi)+'px';\r
+    else\r
+      this.editDiv.style.left=(odOffset[0]+1)+'px';\r
+\r
+    // set top position\r
+    var scrTop=RicoUtil.docScrollTop();\r
+    var editHt=this.editDiv.offsetHeight;\r
+    var newTop=odOffset[1]+this.grid.hdrHt+scrTop;\r
+    var bottom=RicoUtil.windowHeight()+scrTop;\r
+    if (row >= 0) {\r
+      newTop+=(row+1)*this.grid.rowHeight;\r
+      if (newTop+editHt>bottom) newTop-=(editHt+this.grid.rowHeight);\r
+    } else {\r
+      if (newTop+editHt>bottom) newTop=bottom-editHt;\r
+    }\r
+    this.processCallback(this.options.formOpen);\r
+    this.formPopup.openPopup(null,Math.max(newTop,scrTop));\r
+    this.editDiv.style.visibility='visible';\r
+    if (this.initialized) return;\r
+    for (i = 0; i < this.grid.columns.length; i++) {\r
+      spec=this.grid.columns[i].format;\r
+      if (!spec || !spec.EntryType || !spec.FieldName) continue;\r
+      switch (spec.EntryType) {\r
+        case 'tinyMCE':\r
+          if (typeof tinyMCE!='undefined') tinyMCE.execCommand('mceAddControl', true, spec.FieldName);\r
+          break;\r
+      }\r
+    }\r
+    this.formPopup.openPopup();  // tinyMCE may have changed the dimensions of the form\r
+    this.initialized=true;\r
+  },\r
+\r
+  makeFormInvisible: function() {\r
+    this.editDiv.style.visibility='hidden';\r
+    this.formPopup.closePopup();\r
+    this.processCallback(this.options.formClose);\r
+  },\r
+  \r
+  getConfirmDesc: function(rowIdx) {\r
+    var desc=this.grid.columns[this.options.ConfirmDeleteCol].cell(rowIdx).innerHTML;\r
+    desc=this.getLookupValue(desc)[1];\r
+    return desc.stripTags();\r
+  },\r
+\r
+  deleteRecord: function() {\r
+    this.menu.hidemenu();\r
+    var desc;\r
+    if (this.options.ConfirmDeleteCol < 0) {\r
+      desc=RicoTranslate.getPhrase("this "+this.options.RecordName);\r
+    } else {\r
+      desc=this.getConfirmDesc(this.rowIdx);\r
+      if (desc.length>50) desc=desc.substring(0,50)+'...';\r
+      desc='\"' + desc + '\"'\r
+    }\r
+    if (!this.options.ConfirmDelete.valueOf || confirm(RicoTranslate.getPhrase("Are you sure you want to delete ") + desc + " ?")) {\r
+      this.hideResponse('Deleting...');\r
+      this.showResponse();\r
+      var parms=this.action.name+"=del"+this.getKey();\r
+      //alert(parms);\r
+      new Ajax.Updater(this.responseDiv, window.location.pathname, {parameters:parms,onComplete:this.processResponse.bind(this)});\r
+    }\r
+    this.menu.cancelmenu();\r
+  },\r
+  \r
+  getKey: function() {\r
+    var key='';\r
+    for (var i=0; i<this.grid.columns.length; i++) {\r
+      if (this.grid.columns[i].format && this.grid.columns[i].format.isKey) {\r
+        var value=this.grid.columns[i].getValue(this.rowIdx);\r
+        value=this.getLookupValue(value)[0];\r
+        key+='&_k'+i+'='+value;\r
+      }\r
+    }\r
+    return key;\r
+  },\r
+\r
+  TESubmit: function(e) {\r
+    var i,lbl,spec,elem,entrytype;\r
+    \r
+    if (!e) e=window.event;\r
+    Event.stop(e);\r
+\r
+    // check fields that are supposed to be non-blank\r
+\r
+    for (i = 0; i < this.grid.columns.length; i++) {\r
+      spec=this.grid.columns[i].format;\r
+      if (!spec || !spec.EntryType || !spec.FieldName) continue;\r
+      entrytype=spec.EntryType.charAt(0).toLowerCase();\r
+      if (!entrytype.match(/d|i|b/)) continue;\r
+      if (spec.isNullable==true && entrytype!='b') continue;\r
+      elem=$(spec.FieldName);\r
+      if (!elem) continue;\r
+      //alert("nonblank check: " + spec.FieldName);\r
+      if (elem.tagName.toLowerCase()!='input') continue;\r
+      if (elem.type.toLowerCase()!='text') continue;\r
+      if (elem.value.length == 0) {\r
+        alert(RicoTranslate.getPhrase("Please enter\t a value for")+" \"" + this.grid.columns[i].formLabel.innerHTML + "\"");\r
+        //setTimeout("FocusField(document." + this.form.name + "." + this.options.NonBlanks[i] + ")",2000);\r
+        return false;\r
+      }\r
+    }\r
+\r
+    // recheck any elements on the form with an onchange event\r
+\r
+    var InputFields = this.form.getElementsByTagName("input");\r
+    this.TEerror=false;\r
+    for (i=0; i < InputFields.length; i++) {\r
+      if (InputFields[i].type=="text" && InputFields[i].onchange) {\r
+        InputFields[i].onchange();\r
+        if (this.TEerror) return false;\r
+      }\r
+    }\r
+    if (typeof tinyMCE!='undefined') tinyMCE.triggerSave();\r
+    this.makeFormInvisible();\r
+    this.showResponse();\r
+    var parms=Form.serialize(this.form)+this.key\r
+    Rico.writeDebugMsg("TESubmit:"+parms);\r
+    new Ajax.Updater(this.responseDiv, window.location.pathname, {parameters:parms,onComplete:this.responseHandler});\r
+    this.menu.cancelmenu();\r
+    return false;\r
+  },\r
+  \r
+  FocusField: function(elem) {\r
+    elem.focus();\r
+    elem.select();\r
+  },\r
+\r
+  TableEditCheckInt: function(TxtObj) {\r
+    var val=TxtObj.value;\r
+    if (val=='') return;\r
+    if (val!=parseInt(val)) {\r
+      alert(RicoTranslate.getPhrase("Please enter\t an integer value for")+" \"" + $("lbl_"+TxtObj.id).innerHTML + "\"");\r
+      setTimeout(this.FocusField.bind(this,TxtObj),0);\r
+      this.TEerror=true;\r
+    }\r
+  },\r
+\r
+  TableEditCheckPosInt: function(TxtObj) {\r
+    var val=TxtObj.value;\r
+    if (val=='') return;\r
+    if (val!=parseInt(val) || val<0) {\r
+      alert(RicoTranslate.getPhrase("Please enter\t a positive integer value for")+" \"" + $("lbl_"+TxtObj.id).innerHTML + "\"");\r
+      setTimeout(this.FocusField.bind(this,TxtObj),0);\r
+      this.TEerror=true;\r
+    }\r
+  }\r
+}\r
+\r
+\r
+// Registers custom popup widgets to fill in a text box (e.g. ricoCalendar and ricoTree)\r
+//\r
+// Custom widget must implement:\r
+//   open() method (make control visible)\r
+//   close() method (hide control)\r
+//   container property (div element that contains the control)\r
+//   id property (uniquely identifies the widget class)\r
+//\r
+// widget calls returnValue method to return a value to the caller\r
+//\r
+// this object handles clicks on the control's icon and positions the control appropriately.\r
+var RicoEditControls = {\r
+  widgetList:$H(),\r
+  elemList:$H(),\r
+  \r
+  register: function(widget, imgsrc) {\r
+    var tmp={};\r
+    tmp[widget.id]={imgsrc:imgsrc, widget:widget, currentEl:''};\r
+    this.widgetList=this.widgetList.merge(tmp);\r
+    widget.returnValue=this.setValue.bind(this,widget);\r
+    Rico.writeDebugMsg("RicoEditControls.register:"+widget.id);\r
+  },\r
+  \r
+  atLoad: function() {\r
+    var k=this.widgetList.keys();\r
+    for (var i=0; i<k.length; i++) {\r
+      var w=this.widgetList[k[i]].widget;\r
+      if (w.atLoad) w.atLoad();\r
+    }\r
+  },\r
+  \r
+  applyTo: function(column,inputCtl) {\r
+    var wInfo=this.widgetList[column.format.SelectCtl];\r
+    if (!wInfo) return null;\r
+    Rico.writeDebugMsg('RicoEditControls.applyTo: '+column.displayName+' : '+column.format.SelectCtl);\r
+    var descSpan = document.createElement('span');
+    var newimg = document.createElement('img');
+    newimg.style.paddingLeft='4px';
+    newimg.style.cursor='pointer';\r
+    newimg.align='top';
+    newimg.src=wInfo.imgsrc;\r
+    newimg.id=this.imgId(column.format.FieldName);\r
+    newimg.onclick=this.processClick.bindAsEventListener(this);\r
+    inputCtl.parentNode.appendChild(descSpan);
+    inputCtl.parentNode.appendChild(newimg);
+    inputCtl.style.display='none';    // comment out this line for debugging\r
+    var tmp=new Object();\r
+    tmp[newimg.id]={descSpan:descSpan, inputCtl:inputCtl, widget:wInfo.widget, listObj:wInfo, column:column};\r
+    this.elemList=this.elemList.merge(tmp);\r
+    column.format.selectIcon=newimg;\r
+    column.format.selectDesc=descSpan;\r
+  },\r
+\r
+  processClick: function(e) {\r
+    var elem=Event.element(e);\r
+    var el=this.elemList[elem.id];\r
+    if (!el) return;\r
+    if (el.listObj.currentEl==elem.id && el.widget.container.style.display!='none') {\r
+      el.widget.close();\r
+      el.listObj.currentEl='';\r
+    } else {\r
+      el.listObj.currentEl=elem.id;\r
+      Rico.writeDebugMsg('RicoEditControls.processClick: '+el.widget.id+' : '+el.inputCtl.value);\r
+      RicoUtil.positionCtlOverIcon(el.widget.container,elem);\r
+      el.widget.open(el.inputCtl.value);\r
+    }\r
+  },\r
+  \r
+  imgId: function(fieldname) {\r
+    return 'icon_'+fieldname;\r
+  },\r
+  \r
+  resetValue: function(column) {\r
+    var el=this.elemList[this.imgId(column.format.FieldName)];\r
+    if (!el) return;\r
+    el.inputCtl.value=column.format.ColData;\r
+    el.descSpan.innerHTML=column._format(column.format.ColData);\r
+  },\r
+  \r
+  setValue: function(widget,newVal,newDesc) {\r
+    var wInfo=this.widgetList[widget.id];\r
+    if (!wInfo) return null;\r
+    var id=wInfo.currentEl;\r
+    if (!id) return null;\r
+    var el=this.elemList[id];\r
+    if (!el) return null;\r
+    el.inputCtl.value=newVal;\r
+    if (!newDesc) newDesc=el.column._format(newVal);\r
+    el.descSpan.innerHTML=newDesc;\r
+    //alert(widget.id+':'+id+':'+el.inputCtl.id+':'+el.inputCtl.value+':'+newDesc);\r
+  },\r
+  \r
+  close: function(id) {\r
+    var wInfo=this.widgetList[id];\r
+    if (!wInfo) return;\r
+    if (wInfo.widget.container.style.display!='none')\r
+      wInfo.widget.close();\r
+  }\r
+}\r
+\r
+Rico.includeLoaded('ricoLiveGridForms.js');
diff --git a/NP_TrackBack/trunk/trackback/js/rico/ricoLiveGridMenu.js b/NP_TrackBack/trunk/trackback/js/rico/ricoLiveGridMenu.js
new file mode 100644 (file)
index 0000000..6815fa7
--- /dev/null
@@ -0,0 +1,110 @@
+if(typeof Rico=='undefined')\r
+  throw("GridMenu requires the Rico JavaScript framework");\r
+\r
+\r
+/**\r
+ * Standard menu for LiveGrid\r
+ */\r
+Rico.GridMenu = Class.create();
+
+Rico.GridMenu.prototype = {
+
+initialize: function(options) {
+  this.options = {\r
+    width           : '20em',\r
+    dataMenuHandler : null          // put custom items on the menu\r
+  };\r
+  Object.extend(this.options, options || {});\r
+  Object.extend(this, new Rico.Menu(this.options));\r
+  this.sortmenu = new Rico.Menu(this.options);\r
+  this.filtermenu = new Rico.Menu(this.options);\r
+  this.exportmenu = new Rico.Menu(this.options);\r
+  this.hideshowmenu = new Rico.Menu(this.options);\r
+  this.createDiv();\r
+  this.sortmenu.createDiv();\r
+  this.filtermenu.createDiv();\r
+  this.exportmenu.createDiv();\r
+  this.hideshowmenu.createDiv();\r
+},\r
+\r
+// Build context menu for grid\r
+buildGridMenu: function(r,c) {\r
+  this.clearMenu();\r
+  var totrows=this.liveGrid.buffer.totalRows;\r
+  var onBlankRow=r >= totrows;\r
+  var column=this.liveGrid.columns[c];\r
+  if (this.options.dataMenuHandler) {\r
+     var showMenu=this.options.dataMenuHandler(this.liveGrid,r,c,onBlankRow);\r
+     if (!showMenu) return false;\r
+  }\r
+\r
+  // menu items for sorting\r
+  if (column.sortable && totrows>0) {\r
+    this.sortmenu.clearMenu();\r
+    this.addSubMenuItem(RicoTranslate.getPhrase("Sort by")+": "+column.displayName, this.sortmenu, false);\r
+    this.sortmenu.addMenuItem("Ascending", column.sortAsc.bind(column), true);\r
+    this.sortmenu.addMenuItem("Descending", column.sortDesc.bind(column), true);\r
+  }\r
+\r
+  // menu items for filtering\r
+  if (column.canFilter() && (!onBlankRow || column.filterType == Rico.TableColumn.USERFILTER)) {\r
+    this.filtermenu.clearMenu();\r
+    this.addSubMenuItem(RicoTranslate.getPhrase("Filter by")+": "+column.displayName, this.filtermenu, false);    \r
+    column.userFilter=column.getValue(r);\r
+    if (column.filterType == Rico.TableColumn.USERFILTER) {\r
+      this.filtermenu.addMenuItem("Remove filter", column.setUnfiltered.bind(column), true);\r
+      this.filtermenu.addMenuItem("Refresh", this.liveGrid.filterHandler.bind(this.liveGrid), true);\r
+      if (column.filterOp=='LIKE')\r
+        this.filtermenu.addMenuItem("Change keyword...", column.setFilterKW.bind(column), true);\r
+      if (column.filterOp=='NE' && !onBlankRow)\r
+        this.filtermenu.addMenuItem("Exclude this value also", column.addFilterNE.bind(column), true);\r
+    } else if (!onBlankRow) {\r
+      this.filtermenu.addMenuItem("Include only this value", column.setFilterEQ.bind(column), true);\r
+      this.filtermenu.addMenuItem("Greater than or equal to this value", column.setFilterGE.bind(column), column.userFilter!='');\r
+      this.filtermenu.addMenuItem("Less than or equal to this value", column.setFilterLE.bind(column), column.userFilter!='');\r
+      if (column.isText)\r
+        this.filtermenu.addMenuItem("Contains keyword...", column.setFilterKW.bind(column), true);\r
+      this.filtermenu.addMenuItem("Exclude this value", column.setFilterNE.bind(column), true);\r
+    }\r
+    if (this.liveGrid.filterCount() > 0)\r
+      this.filtermenu.addMenuItem("Remove all filters", this.liveGrid.clearFilters.bind(this.liveGrid), true);\r
+  }\r
+\r
+  // menu items for Print/Export\r
+  if (this.liveGrid.options.maxPrint > 0 && totrows>0) {\r
+    this.exportmenu.clearMenu();\r
+    this.addSubMenuItem('Print\t/Export',this.exportmenu);\r
+    this.exportmenu.addMenuItem("Visible rows to web page", this.liveGrid.printVisible.bind(this.liveGrid,'plain'), true);\r
+    this.exportmenu.addMenuItem("All rows to web page", this.liveGrid.printAll.bind(this.liveGrid,'plain'), this.liveGrid.buffer.totalRows <= this.liveGrid.options.maxPrint);\r
+    if (Prototype.Browser.IE) {\r
+      this.exportmenu.addMenuBreak();\r
+      this.exportmenu.addMenuItem("Visible rows to spreadsheet", this.liveGrid.printVisible.bind(this.liveGrid,'owc'), true);\r
+      this.exportmenu.addMenuItem("All rows to spreadsheet", this.liveGrid.printAll.bind(this.liveGrid,'owc'), this.liveGrid.buffer.totalRows <= this.liveGrid.options.maxPrint);\r
+    }\r
+  }\r
+\r
+  // menu items for hide/unhide\r
+  var hiddenCols=this.liveGrid.listInvisible();\r
+  for (var showableCnt=0,x=0; x<hiddenCols.length; x++)\r
+    if (hiddenCols[x].canHideShow()) showableCnt++;\r
+  if (showableCnt > 0 || column.canHideShow()) {\r
+    this.hideshowmenu.clearMenu();\r
+    this.addSubMenuItem('Hide\t/Show',this.hideshowmenu);\r
+    var visibleCnt=this.liveGrid.columns.length-hiddenCols.length;\r
+    var enabled=(visibleCnt>1 && column.visible && column.canHideShow());\r
+    this.hideshowmenu.addMenuItem(RicoTranslate.getPhrase('Hide')+': '+column.displayName, column.hideColumn.bind(column), enabled);\r
+    for (var cnt=0,x=0; x<hiddenCols.length; x++) {\r
+      if (hiddenCols[x].canHideShow()) {\r
+        if (cnt++==0) this.hideshowmenu.addMenuBreak();\r
+        this.hideshowmenu.addMenuItem(RicoTranslate.getPhrase('Show')+': '+hiddenCols[x].displayName, hiddenCols[x].showColumn.bind(hiddenCols[x]));\r
+      }\r
+    }\r
+    if (hiddenCols.length > 1)\r
+      this.hideshowmenu.addMenuItem(RicoTranslate.getPhrase('Show All'), this.liveGrid.showAll.bind(this.liveGrid));\r
+  }\r
+  return true;\r
+}\r
+\r
+}\r
+\r
+Rico.includeLoaded('ricoLiveGridMenu.js');
diff --git a/NP_TrackBack/trunk/trackback/js/rico/ricoMenu.js b/NP_TrackBack/trunk/trackback/js/rico/ricoMenu.js
new file mode 100644 (file)
index 0000000..544d3e8
--- /dev/null
@@ -0,0 +1,197 @@
+Rico.Menu = Class.create();
+
+Rico.Menu.prototype = {
+
+  initialize: function(options) {
+    Object.extend(this, new Rico.Popup());
+    Object.extend(this.options, {
+      width        : "15em",
+      margin       : 6   // account for shadow
+    });
+    if (typeof options=='string')
+      this.options.width=options;
+    else
+      Object.extend(this.options, options || {});
+    this.hideFunc=null;
+    this.highlightElem=null;
+    new Image().src = Rico.imgDir+'left.gif';
+    new Image().src = Rico.imgDir+'right.gif';
+  },
+  
+  createDiv: function(parentNode) {
+    if (this.div) return;
+    this.div = document.createElement('div');
+    this.div.className = Prototype.Browser.WebKit ? 'ricoMenuSafari' : 'ricoMenu';
+    this.div.style.position="absolute";
+    this.div.style.width=this.options.width;
+    if (!parentNode) parentNode = document.getElementsByTagName("body")[0];
+    parentNode.appendChild(this.div);
+    this.width=this.div.offsetWidth
+    this.setDiv(this.div,this.cancelmenu.bindAsEventListener(this));
+    this.direction=Element.getStyle(this.div,'direction') || 'ltr';
+    this.direction=this.direction.toLowerCase();  // ltr or rtl
+    this.hidemenu();
+    this.itemCount=0;
+  },
+  
+  showmenu: function(e,hideFunc){
+    Event.stop(e);
+    this.hideFunc=hideFunc;
+    if (this.div.childNodes.length==0) {
+      this.cancelmenu();
+      return false;
+    }
+    this.openmenu(e.clientX,e.clientY,0,0);
+  },
+  
+  openmenu: function(x,y,clickItemWi,clickItemHt) {
+    var newLeft=RicoUtil.docScrollLeft()+x;
+    //window.status='openmenu: newLeft='+newLeft+' width='+this.width+' windowWi='+RicoUtil.windowWidth();
+    if (this.direction == 'rtl') {
+      if (newLeft > this.width+clickItemWi) newLeft-=this.width+clickItemWi;
+    } else {
+      if (x+this.width+this.options.margin > RicoUtil.windowWidth()) newLeft-=this.width+clickItemWi;
+    }
+    var newTop=RicoUtil.docScrollTop()+y;
+    this.div.style.visibility="hidden";
+    this.div.style.display="block";
+    var contentHt=this.div.offsetHeight;
+    if (y+contentHt+this.options.margin > RicoUtil.windowHeight())
+      newTop=Math.max(newTop-contentHt+clickItemHt,0);
+    this.openPopup(newLeft,newTop);
+    this.div.style.visibility ="visible";
+    return false;
+  },
+
+  clearMenu: function() {
+    this.div.innerHTML="";
+    this.defaultAction=null;
+    this.itemCount=0;
+  },
+
+  addMenuHeading: function(hdg,translate) {
+    var el=document.createElement('div')
+    el.innerHTML =(translate==null || translate==true) ? RicoTranslate.getPhrase(hdg) : hdg;
+    el.className='ricoMenuHeading';
+    this.div.appendChild(el);
+  },
+
+  addMenuBreak: function() {
+    var brk=document.createElement('div');
+    brk.className="ricoMenuBreak";
+    this.div.appendChild(brk);
+  },
+
+  addSubMenuItem: function(menutext, submenu, translate) {
+    var dir=this.direction=='rtl' ? 'left' : 'right';
+    var a=this.addMenuItem(menutext,null,true,null,translate);
+    a.className='ricoSubMenu';
+    a.style.backgroundImage='url('+Rico.imgDir+dir+'.gif)';
+    a.style.backgroundRepeat='no-repeat';
+    a.style.backgroundPosition=dir;
+    a.onmouseover=this.showSubMenu.bind(this,a,submenu);
+    a.onmouseout=this.subMenuOut.bindAsEventListener(this);
+  },
+  
+  showSubMenu: function(a,submenu) {
+    if (this.openSubMenu) this.hideSubMenu();
+    this.openSubMenu=submenu;
+    this.openMenuAnchor=a;
+    var pos=Position.page(a);
+    if (a.className=='ricoSubMenu') a.className='ricoSubMenuOpen';
+    submenu.openmenu(pos[0]+a.offsetWidth, pos[1], a.offsetWidth-2, a.offsetHeight+2);
+  },
+  
+  subMenuOut: function(e) {
+    if (!this.openSubMenu) return;
+    Event.stop(e);
+    var elem=Event.element(e);
+    var reltg = (e.relatedTarget) ? e.relatedTarget : e.toElement;
+    try {
+      while (reltg != null && reltg != this.openSubMenu.div)
+        reltg=reltg.parentNode;
+    } catch(err) {}
+    if (reltg == this.openSubMenu.div) return;
+    this.hideSubMenu();
+  },
+  
+  hideSubMenu: function() {
+    if (this.openMenuAnchor) {
+      this.openMenuAnchor.className='ricoSubMenu';
+      this.openMenuAnchor=null;
+    }
+    if (this.openSubMenu) {
+      this.openSubMenu.hidemenu();
+      this.openSubMenu=null;
+    }
+  },
+
+  addMenuItem: function(menutext,action,enabled,title,translate,target) {
+    this.itemCount++;
+    if (translate==null) translate=true;
+    var a = document.createElement(typeof action=='string' ? 'a' : 'div');
+    if ( arguments.length < 3 || enabled ) {
+      switch (typeof action) {
+        case 'function': 
+          a.onclick = action; 
+          break;
+        case 'string'  : 
+          a.href = action; 
+          if (target) a.target = target; 
+          break
+      }
+      a.className = 'enabled';
+      if (this.defaultAction==null) this.defaultAction=action;
+    } else {
+      a.disabled = true;
+      a.className = 'disabled';
+    }
+    a.innerHTML = translate ? RicoTranslate.getPhrase(menutext) : menutext;
+    if (typeof title=='string')
+      a.title = translate ? RicoTranslate.getPhrase(title) : title;
+    a=this.div.appendChild(a);
+    Event.observe(a,"mouseover", this.mouseOver.bindAsEventListener(this));
+    Event.observe(a,"mouseout", this.mouseOut.bindAsEventListener(this));
+    return a;
+  },
+  
+  mouseOver: function(e) {
+    if (this.highlightElem && this.highlightElem.className=='enabled-hover') {
+      // required for Safari
+      this.highlightElem.className='enabled';
+      this.highlightElem=null;
+    }
+    var elem=Event.element(e);
+    if (this.openMenuAnchor && this.openMenuAnchor!=elem)
+      this.hideSubMenu();
+    if (elem.className=='enabled') {
+      elem.className='enabled-hover';
+      this.highlightElem=elem;
+    }
+  },
+
+  mouseOut: function(e) {
+    var elem=Event.element(e);
+    if (elem.className=='enabled-hover') elem.className='enabled';
+    if (this.highlightElem==elem) this.highlightElem=null;
+  },
+
+  isVisible: function() {
+    return this.div && Element.visible(this.div);
+  },
+  
+  cancelmenu: function() {
+    if (this.hideFunc) this.hideFunc();
+    this.hideFunc=null;
+    this.hidemenu();
+  },
+
+  hidemenu: function() {
+    if (!this.div) return;
+    if (this.openSubMenu) this.openSubMenu.hidemenu();
+    this.closePopup();
+  }
+
+};
+
+Rico.includeLoaded('ricoMenu.js');
diff --git a/NP_TrackBack/trunk/trackback/js/rico/ricoSheet.js b/NP_TrackBack/trunk/trackback/js/rico/ricoSheet.js
new file mode 100644 (file)
index 0000000..97faef6
--- /dev/null
@@ -0,0 +1,1577 @@
+Object.extend(Rico.SimpleGrid.prototype, {
+
+initSheet: function() {
+  this.highlightDiv=[];
+  for (var i=0; i<4; i++) {
+    this.highlightDiv[i] = this.createDiv("highlight",this.scrollDiv);
+    this.highlightDiv[i].style.display="none";
+    this.highlightDiv[i].id+=i;
+    this.highlightDiv[i].style[i % 2==0 ? 'height' : 'width']="0px";
+  }
+  for (var c=1; c<this.columns.length; c++) {
+    var col=this.columns[c];
+    for (var r=0; r<col.numRows(); r++) {
+      var cell=col.cell(r);
+      cell.RicoRow=r+1;
+      cell.RicoCol=c;
+      cell.RicoValue=null;
+    }
+  }
+  if (this.menu) {
+    if (!this.menu.grid) this.registerScrollMenu(this.menu);
+    this.menu.showmenu=this.menu.showSheetMenu;
+  }
+  this.inputArea=RicoUtil.createFormField(this.scrollDiv,'textarea',null,'inputArea');
+  this.inputArea.style.position='absolute';
+  this.inputArea.style.display='none';
+  this.inputArea.style.zIndex=2;
+  this.inputArea.cols=30;
+  this.inputArea.rows=4;
+  this.inputArea.blur();
+  this.clipBox=RicoUtil.createFormField(this.innerDiv,'textarea',null,'clipBox');
+  this.clipBox.style.position='absolute';
+  this.clipBox.style.display='none';
+  this.clipBox.cols=80;
+  this.clipBox.rows=10;
+  this.clipBox.style.top='0px';
+  this.clipBox.style.left='0px';
+  this.selectCellRC(0,1);
+  this.mouseOverHandler = this.selectMouseOver.bindAsEventListener(this);
+  this.mouseUpHandler  = this.selectMouseUp.bindAsEventListener(this);
+  Event.observe(this.inputArea,'keydown',this.inputKeydown.bindAsEventListener(this),false);
+  Event.observe(Prototype.Browser.IE ? document.body : window,'keydown',this.gridKeydown.bindAsEventListener(this),false);
+  Event.observe(this.tbody[1],"mousedown", this.selectMouseDown.bindAsEventListener(this), false);
+
+  // disable drag & select events in IE
+  this.outerDiv.ondrag = this.disableEvent;
+  this.outerDiv.onselectstart = this.disableEvent;
+  this.tbody[1].ondrag = this.disableEvent;
+  this.tbody[1].onselectstart = this.disableEvent;
+},
+
+disableEvent: function(e) {
+  e=e || event;
+  Event.stop(e);
+  return false;
+},
+
+cellIndex: function(cell) {
+  var a=cell.id.split(/_/);
+  var l=a.length;
+  var r=parseInt(a[l-2]);
+  var c=parseInt(a[l-1]);
+  return {row:r, column:c, tabIdx:this.columns[c].tabIdx, cell:cell};
+},
+
+AdjustSelection: function(cell) {
+  var newIdx=this.cellIndex(cell);
+  if (this.SelectIdxStart.tabIdx != newIdx.tabIdx) return;
+  this.HideSelection();
+  this.SelectIdxEnd=newIdx;
+  this.ShowSelection();
+},
+
+selectMouseDown: function(e) {
+  if (this.highlightEnabled==false) return true;
+  this.cancelMenu();
+  var cell=Event.element(e);
+  Event.stop(e);
+  if (!Event.isLeftClick(e)) return;
+  cell=RicoUtil.getParentByTagName(cell,'div','ricoLG_cell');
+  if (!cell) return;
+  var newIdx=this.cellIndex(cell);
+  if (e.shiftKey) {
+    if (!this.SelectIdxStart) return;
+    this.selectCellRC(newIdx.row,newIdx.column,true);
+  } else {
+    this.selectCellRC(newIdx.row,newIdx.column,false);
+    this.pluginSelect();
+  }
+},
+
+pluginSelect: function() {
+  if (this.selectPluggedIn) return;
+  var tBody=this.tbody[this.SelectIdxStart.tabIdx];
+  Event.observe(tBody,"mouseover", this.mouseOverHandler, false);
+  Event.observe(this.outerDiv,"mouseup",  this.mouseUpHandler,  false);
+  this.selectPluggedIn=true;
+},
+
+unplugSelect: function() {
+  var tBody=this.tbody[this.SelectIdxStart.tabIdx];
+  Event.stopObserving(tBody,"mouseover", this.mouseOverHandler , false);
+  Event.stopObserving(this.outerDiv,"mouseup", this.mouseUpHandler , false);
+  this.selectPluggedIn=false;
+},
+
+selectMouseUp: function(e) {
+  this.unplugSelect();
+  var cell=Event.element(e);
+  cell=RicoUtil.getParentByTagName(cell,'div','ricoLG_cell');
+  if (!cell) return;
+  this.AdjustSelection(cell);
+},
+
+selectMouseOver: function(e) {
+  var cell=Event.element(e);
+  cell=RicoUtil.getParentByTagName(cell,'div','ricoLG_cell');
+  if (!cell) return;
+  this.AdjustSelection(cell);
+  Event.stop(e);
+},
+
+getSelection: function() {
+  if (!this.SelectIdxStart || !this.SelectIdxEnd) return false;
+  var r1=Math.min(this.SelectIdxEnd.row,this.SelectIdxStart.row);
+  var r2=Math.max(this.SelectIdxEnd.row,this.SelectIdxStart.row);
+  var c1=Math.min(this.SelectIdxEnd.column,this.SelectIdxStart.column);
+  var c2=Math.max(this.SelectIdxEnd.column,this.SelectIdxStart.column);
+  return {r1:r1,c1:c1,r2:r2,c2:c2};
+},
+
+updateSelectOutline: function() {
+  var s=this.getSelection();
+  if (!s || s.r1 > s.r2) {
+    this.HideSelection();
+    return;
+  }
+  var top1=this.columns[s.c1].cell(s.r1).offsetTop;
+  var cell2=this.columns[s.c1].cell(s.r2);
+  var bottom2=cell2.offsetTop+cell2.offsetHeight;
+  var left1=this.columns[s.c1].dataCell.offsetLeft;
+  var left2=this.columns[s.c2].dataCell.offsetLeft;
+  var right2=left2+this.columns[s.c2].dataCell.offsetWidth;
+  //window.status='updateSelectOutline: '+s.r1+' '+s.r2+' top='+top1+' bot='+bottom2;
+  this.highlightDiv[0].style.top=this.highlightDiv[3].style.top=this.highlightDiv[1].style.top=(top1-3) + 'px';
+  this.highlightDiv[2].style.top=(bottom2-2)+'px';
+  this.highlightDiv[3].style.left=(left1-2)+'px';
+  this.highlightDiv[0].style.left=this.highlightDiv[2].style.left=(left1-1)+'px';
+  this.highlightDiv[1].style.left=(right2-1)+'px';
+  this.highlightDiv[0].style.width=this.highlightDiv[2].style.width=(right2-left1-1) + 'px';
+  this.highlightDiv[1].style.height=this.highlightDiv[3].style.height=(bottom2-top1) + 'px';
+  for (var i=0; i<4; i++)
+    this.highlightDiv[i].style.display='';
+},
+
+isSelected: function(r,c) {
+  var s=this.getSelection();
+  return s ? (s.r1 <= r) && (r <= s.r2) && (s.c1 <= c) && (c <= s.c2) : false;
+},
+
+HideSelection: function(cellList) {
+  for (var i=0; i<4; i++)
+    this.highlightDiv[i].style.display='none';
+},
+
+ShowSelection: function() {
+  this.updateSelectOutline();
+},
+
+/*
+ * @param what valid values are: null, 'all', 'formats', 'formulas', 'values'
+ */
+clearSelection: function() {
+  var s=this.getSelection();
+  if (!s) return;
+  var args=$A(arguments);
+  var what=args.shift();
+  if (typeof what=='object') what=args.shift();  // in case first arg is an event object
+  var v=(!what || what=='all') ? 1 : 0;
+  var whatobj={formats:v,formulas:v,values:v};
+  if (typeof what=='string') whatobj[what]=1;
+  if (whatobj.values) whatobj.formulas=1;
+  for (var r=s.r1; r<=s.r2; r++) {
+    for (var c=s.c1; c<=s.c2; c++) {
+      var gridcell=this.columns[c].cell(r);
+      if (whatobj.formats) {
+        gridcell.style.cssText='';
+        gridcell.RicoFormat={};
+      }
+      if (whatobj.formulas) gridcell.RicoFormula=null;
+      if (whatobj.values) gridcell.RicoValue=null;
+      this.formatCell(gridcell);
+    }
+  }
+},
+
+selectCellRC: function(r,c,adjFlag) {
+  if (r < 0 || r >= this.columns[0].numRows()) return;
+  this.HideSelection();
+  if (adjFlag) {
+    if (this.SelectIdxStart.tabIdx == this.columns[c].tabIdx)
+      this.SelectIdxEnd={row:r, column:c, tabIdx:this.columns[c].tabIdx};
+  } else {
+    this.SelectIdxStart=this.SelectIdxEnd={row:r, column:c, tabIdx:this.columns[c].tabIdx};
+    this.columns[c].cell(r).focus(); // causes IE to scroll cell into view (but not FF)
+  }
+  this.ShowSelection();
+},
+
+moveSelection: function(dr,dc,adjFlag,e) {
+  var selIdx=adjFlag ? this.SelectIdxEnd : this.SelectIdxStart;
+  var newr=selIdx.row+dr;
+  var newc=selIdx.column+dc;
+  if (newr>=0 && newr<this.columns[0].numRows() && newc>=1 && newc<this.columns.length)
+    this.selectCellRC(newr,newc,adjFlag);
+  if (e) Event.stop(e);
+},
+
+formatCell: function(cell) {
+  // TO DO: add currency/date formatting here
+  var v=cell.RicoValue;
+  if (v==null)
+    v='';
+  else if (typeof(v)=='number')
+    v = isNaN(v) ? '#VALUE' : cell.RicoFormat ? v.formatNumber(cell.RicoFormat) : v.toString();
+  else if (typeof v!='string')
+    v=v.toString();
+  v=v.replace(/^(\s*)/, '');
+  cell.style.paddingLeft=(RegExp.$1.length/2)+'em';
+  cell.innerHTML = v;
+},
+
+// action='add' or 'remove'
+updateDependencies: function(formulaCell,action) {
+  if (!formulaCell.RicoFormula) return;
+  //alert('updateDependencies '+action+': '+formulaCell.RicoRow+','+formulaCell.RicoCol);
+  var ranges=formulaCell.RicoFormula.getRanges();
+  for (var i=0; i<ranges.length; i++) {
+    if (!ranges[i]) continue;
+    var r1=Math.min(ranges[i][0],ranges[i][2]);
+    var r2=Math.max(ranges[i][0],ranges[i][2]);
+    var c1=Math.min(ranges[i][1],ranges[i][3]);
+    var c2=Math.max(ranges[i][1],ranges[i][3]);
+    for (var c=c1; c<=c2; c++) {
+      var col=this.columns[c];
+      for (var r=r1; r<=r2; r++) {
+        var cell=col.cell(r-1);
+        if (!cell.RicoDependencies) cell.RicoDependencies=new Rico.Formula.f_dependencies();
+        //alert('updateDependencies '+action+': '+formulaCell.RicoRow+','+formulaCell.RicoCol+' is dependent on '+cell.RicoRow+','+cell.RicoCol);
+        cell.RicoDependencies[action](formulaCell);
+      }
+    }
+  }
+},
+
+checkDependencies: function(cell) {
+  if (!cell.RicoDependencies) return;
+  var depcells=cell.RicoDependencies.items;
+  for (var i=0; i<depcells.length; i++) {
+    depcells[i].RicoValue=depcells[i].RicoFormula.eval();
+    this.formatCell(depcells[i]);
+    this.checkDependencies(depcells[i]);
+  }
+},
+
+showInputArea: function(clear,e) {
+  this.unplugScroll();
+  this.inputIdx=this.SelectIdxStart;
+  var col=this.columns[this.inputIdx.column];
+  this.inputIdx.cell=col.cell(this.inputIdx.row);
+  this.inputArea.style.top=(this.inputIdx.cell.offsetTop+col.dataCell.offsetTop)+'px';
+  this.inputArea.style.left=col.dataCell.offsetLeft+'px';
+  this.inputArea.style.display='';
+  this.inputArea.focus();
+  if (clear) {
+    if (Prototype.Browser.WebKit) {
+      // Safari does not bubble the event to the inputArea, so force it
+      this.inputArea.value=String.fromCharCode(e.charCode);
+      this.inputArea.setSelectionRange(1,1);
+      Event.stop(e);
+    } else this.inputArea.value='';
+  } else {
+    if (this.inputIdx.cell.RicoFormula)
+      this.inputArea.value=this.inputIdx.cell.RicoFormula.toEditString();
+    else
+      this.inputArea.value=this.inputIdx.cell.RicoValue || '';
+  }
+},
+
+closeInputArea: function(dr,dc,e) {
+  var newVal=this.inputArea.value;
+  var cell=this.inputIdx.cell;
+  if (this.options.checkEntry)
+    newVal=this.options.checkEntry(newVal,this.inputIdx.cell);
+  this.updateDependencies(cell,'remove');
+  cell.RicoFormula=null;
+  if (!this.options.noFormulas && newVal.charAt(0) == '=') {
+    // parse formula
+    cell.RicoFormula = new Rico.Formula(grid,cell);
+    cell.RicoFormula.parse(newVal);
+    cell.RicoValue = cell.RicoFormula.eval();
+    this.updateDependencies(cell,'add');
+  } else if (newVal=='') {
+    cell.RicoValue = null;
+  } else if (newVal.match(/^(true|false)$/i)) {
+    cell.RicoValue = eval(newVal.toLowerCase());
+  } else if (newVal.match(/^-?\d+(.\d*)?$/)) {
+    // parse number
+    cell.RicoValue = parseFloat(newVal);
+  } else {
+    cell.RicoValue=newVal;
+  }
+  this.formatCell(cell);
+  this.inputArea.blur();
+  this.inputArea.style.display='none';
+  this.checkDependencies(cell);
+  this.pluginScroll();
+  this.moveSelection(dr,dc,false,e);
+},
+
+inputKeydown: function(e) {
+  //window.status='inputKeydown keyCode='+e.keyCode;
+  switch (e.keyCode) {
+    case 13:
+      Event.stop(e);
+      this.closeInputArea(1,0,e);
+      return false;
+    case 9:
+      Event.stop(e);
+      this.closeInputArea(0,e.shiftKey ? -1 : 1,e);
+      return false;
+    case 27:
+      Event.stop(e);
+      this.inputArea.blur();
+      this.inputArea.style.display='none';
+      return false;
+  }
+  return true;
+},
+
+copyToClipbox: function() {
+  var s=this.getSelection();
+  if (!s) return;
+  var clipstr='';
+  for (var r=s.r1; r<=s.r2; r++) {
+    for (var c=s.c1; c<=s.c2; c++) {
+      if (c>s.c1) clipstr+="\t";
+      clipstr+=this.columns[c].cell(r).RicoValue;
+    }
+    clipstr+="\r\n";
+  }
+  this.clipBox.style.display='block';
+  this.clipBox.value=clipstr;
+  this.clipBox.select();
+},
+
+copySelection: function() {
+  var s=this.getSelection();
+  if (!s) return;
+  var clipArray=[];
+  for (var r=s.r1; r<=s.r2; r++) {
+    var cliprow=[];
+    for (var c=s.c1; c<=s.c2; c++) {
+      var clipcell={};
+      var gridcell=this.columns[c].cell(r);
+      clipcell.value=gridcell.RicoValue;
+      clipcell.style=gridcell.style.cssText;
+      if (gridcell.RicoFormat)
+        clipcell.format=Object.extend({}, gridcell.RicoFormat || {});
+      if (gridcell.RicoFormula)
+        clipcell.formula=Object.extend({}, gridcell.RicoFormula);
+      cliprow[c-s.c1]=clipcell;
+    }
+    clipArray[r-s.r1]=cliprow;
+  }
+  return clipArray;
+},
+
+pasteSelection: function(clipArray,pasteType) {
+  var s=this.getSelection();
+  if (!s || !clipArray) return;
+  pasteType=pasteType || 'all';
+  var clipclen=clipArray[0].length;
+  if (s.r1==s.r2 && s.c1==s.c2) {
+    s.r2=Math.min(s.r1+clipArray.length,this.columns[0].numRows())-1;
+    s.c2=Math.min(s.c1+clipclen,this.columns.length)-1;
+  }
+  for (var r=s.r1,clipr=0; r<=s.r2; r++) {
+    var arow=clipArray[clipr];
+    for (var c=s.c1,clipc=0; c<=s.c2; c++) {
+      var clipcell=arow[clipc];
+      var gridcell=this.columns[c].cell(r);
+      this.updateDependencies(gridcell,'remove');
+      gridcell.RicoFormula=null;
+      if (clipcell.formula) {
+        gridcell.RicoFormula=Object.extend({}, clipcell.formula);
+        gridcell.RicoFormula.cell=gridcell;
+        gridcell.RicoValue = gridcell.RicoFormula.eval();
+        this.updateDependencies(gridcell,'add');
+      } else {
+        gridcell.RicoValue=clipcell.value;
+      }
+      gridcell.style.cssText=clipcell.style;
+      if (clipcell.format)
+        gridcell.RicoFormat=Object.extend({}, clipcell.format);
+      this.formatCell(gridcell);
+      this.checkDependencies(gridcell);
+      clipc=(clipc+1) % clipclen;
+    }
+    clipr=(clipr+1) % clipArray.length;
+  }
+},
+
+formatSelection: function(newFormat) {\r
+  var s=this.getSelection();
+  if (!s) return;\r
+  for (var r=s.r1; r<=s.r2; r++) {
+    for (var c=s.c1; c<=s.c2; c++) {
+      var gridcell=this.cell(r,c);
+      gridcell.RicoFormat=newFormat;
+      this.formatCell(gridcell);
+    }
+  }\r
+},\r
+\r
+handleCtrlKey: function(e) {
+  switch (e.keyCode) {
+    // Ctrl-C
+    case 67:
+      this.clip=this.copySelection();
+      window.status='copy: '+this.clip.length;
+      Event.stop(e);
+      break;
+
+    // Ctrl-X
+    case 88:
+      this.clip=this.copySelection();
+      this.clearSelection();
+      Event.stop(e);
+      break;
+
+    // Ctrl-V
+    case 86:
+      window.status='paste: '+this.clip.length;
+      this.pasteSelection(this.clip);
+      Event.stop(e);
+      break;
+
+    // Ctrl-B
+    case 66:
+      this.toggleAttr('font-weight','normal','bold');
+      Event.stop(e);
+      break;
+
+    // Ctrl-I
+    case 73:
+      this.toggleAttr('font-style','normal','italic');
+      Event.stop(e);
+      break;
+  }
+},
+
+handleNormalKey: function(e) {
+  switch (e.keyCode) {
+    case 91:
+    case 16:
+    case 17:
+    case 18:
+    case 20:
+    case 27: return;
+
+    // tab
+    case 9:  this.moveSelection(0,e.shiftKey ? -1 : 1,false,e); break;
+    // enter/return
+    case 13: this.moveSelection(1,0,false,e); break;
+    // arrow keys
+    case 37: this.moveSelection(0,-1,e.shiftKey,e); break;
+    case 38: this.moveSelection(-1,0,e.shiftKey,e); break;
+    case 39: this.moveSelection(0,1,e.shiftKey,e); break;
+    case 40: this.moveSelection(1,0,e.shiftKey,e); break;
+    // home
+    case 36: this.selectCellRC(this.SelectIdxStart.row,1); Event.stop(e); break;
+    // F2
+    case 113: this.showInputArea(false,e); break;
+
+    default: this.showInputArea(true,e); break;
+  }
+  return false;
+},
+
+gridKeydown: function(e) {
+  if (e.altKey) return;
+  var elem=Event.element(e);
+  if (elem.id=='inputArea') return true;
+  //window.status='gridKeydown keyCode='+e.keyCode;
+  if (e.ctrlKey)
+    this.handleCtrlKey(e);
+  else
+    this.handleNormalKey(e);
+},
+
+toggleAttr: function(attr,v1,v2) {
+  var v=this.getStyle(this.SelectIdxStart.row,this.SelectIdxStart.column,attr);
+  v=v==v2 ? v1 : v2;
+  this.updateSelectionStyle(attr,v);
+},
+
+getStyle: function(row,col,attr) {
+  var csstxt=this.columns[col].cell(row).style.cssText;
+  if (!csstxt) return;
+  if (csstxt.charAt(csstxt.length-1)!=';') csstxt+=';';   // opera
+  csstxt=' '+csstxt;
+  var re=new RegExp("[ ;]"+attr+"\\s*:\\s*([^ ;]*)\\s*;","i");
+  if (re.test(csstxt))
+    return RegExp.$1;
+  else
+    return;
+},
+
+updateStyleText: function(csstxt,attr,value) {
+  var newval=attr+':'+value+';';
+  if (!csstxt) return newval;
+  csstxt=' '+csstxt.strip();
+  if (csstxt.charAt(csstxt.length-1)!=';') csstxt+=';';   // opera
+  var re=new RegExp("([ ;])"+attr+"\\s*:\\s*([^ ;]*)\\s*;","i");
+  // safari must process the regexp twice, everyone else can run it once
+  if (re.test(csstxt))
+    return Prototype.Browser.WebKit ? csstxt.replace(re,"$1"+newval) : RegExp.leftContext+RegExp.$1+newval+RegExp.rightContext;
+  else
+    return csstxt+newval;
+},
+
+updateSelectionStyle: function(attr,newVal) {
+  var s=this.getSelection();
+  if (!s) return;
+  for (var c=s.c1; c<=s.c2; c++) {
+    var col=this.columns[c];
+    for (var r=s.r1; r<=s.r2; r++)
+      col.cell(r).style.cssText=this.updateStyleText(col.cell(r).style.cssText,attr,newVal);
+  }
+},
+
+showHelp: function() {
+  var msg="Rico Spreadsheet\n\n";
+  msg+="Ctrl-C = copy, Ctrl-X = cut, Ctrl-V = paste (only from/to cells on this grid)\n\n";
+  msg+="Formulas starting with '=' are supported\n";
+  msg+="Formulas may contain parentheses and the following operators: + - * / & % = > < <= >= <>\n";
+  msg+="'+' follows javascript rules regarding type conversion (which are slightly different from Excel)\n";
+  msg+="Formulas may refer to cells using 'A1' notation (and 'A1:B2' for ranges).\n";
+  msg+="The following functions are supported in formulas:\n\n";
+  var funclist=[];
+  for (var funcname in Rico.Formula.prototype)
+    if (funcname.substring(0,5)=='eval_') funclist.push(funcname.substring(5));
+  funclist.sort();
+  var funcstr=funclist.join(', ');
+  var i=funcstr.indexOf(' ',Math.floor(funcstr.length/2));
+  msg+=funcstr.substring(0,i)+"\n"+funcstr.substring(i+1);
+  msg+="\n\nFormula parsing based on code originally published by E. W. Bachtal at http://ewbi.blogs.com/develops/";
+  msg+="\nFuture functionality may include copy/paste from external applications, load/save, number & date formatting, and support for additional functions.";
+  alert(msg);
+}
+
+});
+
+
+Rico.Formula = Class.create();
+
+Rico.Formula.TOK_TYPE_NOOP      = "noop";
+Rico.Formula.TOK_TYPE_OPERAND   = "operand";
+Rico.Formula.TOK_TYPE_FUNCTION  = "function";
+Rico.Formula.TOK_TYPE_SUBEXPR   = "subexpression";
+Rico.Formula.TOK_TYPE_ARGUMENT  = "argument";
+Rico.Formula.TOK_TYPE_OP_PRE    = "operator-prefix";
+Rico.Formula.TOK_TYPE_OP_IN     = "operator-infix";
+Rico.Formula.TOK_TYPE_OP_POST   = "operator-postfix";
+Rico.Formula.TOK_TYPE_WSPACE    = "white-space";
+Rico.Formula.TOK_TYPE_UNKNOWN   = "unknown";
+
+Rico.Formula.TOK_SUBTYPE_START       = "start";
+Rico.Formula.TOK_SUBTYPE_STOP        = "stop";
+
+Rico.Formula.TOK_SUBTYPE_TEXT        = "text";
+Rico.Formula.TOK_SUBTYPE_NUMBER      = "number";
+Rico.Formula.TOK_SUBTYPE_LOGICAL     = "logical";
+Rico.Formula.TOK_SUBTYPE_ERROR       = "error";
+Rico.Formula.TOK_SUBTYPE_RANGE       = "range";
+
+Rico.Formula.TOK_SUBTYPE_MATH        = "math";
+Rico.Formula.TOK_SUBTYPE_CONCAT      = "concatenate";
+Rico.Formula.TOK_SUBTYPE_INTERSECT   = "intersect";
+Rico.Formula.TOK_SUBTYPE_UNION       = "union";
+
+Rico.Formula.prototype = {
+
+initialize: function(grid,cell) {
+  this.grid=grid;
+  this.cell=cell;
+},
+
+// 'A' -> 1, 'AA' -> 27
+colLetter2Num: function(colstr) {
+  colstr=colstr.toUpperCase();
+  switch (colstr.length) {
+    case 1: return colstr.charCodeAt(0)-64;
+    case 2: return (colstr.charCodeAt(0)-64) * 26 + colstr.charCodeAt(1)-64;
+    default: return -1;
+  }
+},
+
+// 1 -> 'A', 27 -> 'AA'
+colNum2Letter: function(colnum) {
+  if (colnum <= 26) return String.fromCharCode(64+colnum);
+  colnum-=1;
+  return String.fromCharCode(64+Math.floor(colnum / 26),65+(colnum % 26));
+},
+
+
+toHTML: function() {
+  var indentCount = 0;
+
+  var indent = function() {
+    var s = "|";
+    for (var i = 0; i < indentCount; i++) {
+      s += "&nbsp;&nbsp;&nbsp;|";
+    }
+    return s;
+  };
+
+  var tokensHtml = "<table cellspacing='0'>";
+  tokensHtml += "<tr>";
+  tokensHtml += "<td class='token' style='font-weight: bold; width: 50px'>index</td>";
+  tokensHtml += "<td class='token' style='font-weight: bold; width: 125px'>type</td>";
+  tokensHtml += "<td class='token' style='font-weight: bold; width: 125px'>subtype</td>";
+  tokensHtml += "<td class='token' style='font-weight: bold; width: 150px'>token</td>";
+  tokensHtml += "<td class='token' style='font-weight: bold; width: 300px'>token tree</td></tr>";
+
+  this.tokens.reset();
+  while (this.tokens.moveNext()) {
+
+    var token = this.tokens.current();
+
+    if (token.subtype == Rico.Formula.TOK_SUBTYPE_STOP)
+      indentCount -= ((indentCount > 0) ? 1 : 0);
+
+    tokensHtml += "<tr>";
+
+    tokensHtml += "<td class='token'>" + (this.tokens.index + 1) + "</td>";
+    tokensHtml += "<td class='token'>" + token.type + "</td>";
+    tokensHtml += "<td class='token'>" + ((token.subtype.length == 0) ? "&nbsp;" : token.subtype) + "</td>";
+    tokensHtml += "<td class='token'>" + ((token.value.length == 0) ? "&nbsp;" : token.value).split(" ").join("&nbsp;") + "</td>";
+    tokensHtml += "<td class='token'>" + indent() + ((token.value.length == 0) ? "&nbsp;" : token.value).split(" ").join("&nbsp;") + "</td>";
+
+    tokensHtml += "</tr>";
+
+    if (token.subtype == Rico.Formula.TOK_SUBTYPE_START) indentCount++;
+  }
+  tokensHtml += "</table>";
+  return tokensHtml;
+},
+
+
+parseCellRef: function(refString) {
+  if (!refString) return null;
+  if (!refString.match(/^(\$?)([a-z]*)(\$?)(\d*)$/i)) return null;
+  var abscol=(RegExp.$1=='$');
+  var absrow=(RegExp.$3=='$');
+  var r=null,c=null;
+  if (RegExp.$2) {
+    c=this.colLetter2Num(RegExp.$2);
+    if (c<0 || c>=this.grid.columns.length) return null;
+    if (!abscol) c-=this.cell.RicoCol;
+  }
+  if (RegExp.$4) {
+    r=parseInt(RegExp.$4);
+    if (!absrow) r-=this.cell.RicoRow;
+  }
+  //alert('parseCellRef: '+refString+"\n"+'r='+r+' c='+c+' absrow='+absrow+' abscol='+abscol);
+  return {row:r, col:c, absRow:absrow, absCol:abscol};
+},
+
+
+resolveCellRef: function(cellRef) {
+  var r=cellRef.row;
+  var c=cellRef.col;
+  if (!cellRef.absRow) r+=this.cell.RicoRow;
+  if (!cellRef.absCol) c+=this.cell.RicoCol;
+  return {row:r, col:c};
+},
+
+
+resolveRange: function(token) {
+  if (!token.rangeStart) return null;
+  var a1=this.resolveCellRef(token.rangeStart);
+  var a2=this.resolveCellRef(token.rangeEnd);
+  //alert('resolveRange: '+a1.row+','+a1.col+' '+a2.row+','+a2.col);
+  var r1=Math.min(a1.row,a2.row);
+  var r2=Math.max(a1.row,a2.row);
+  var c1=Math.min(a1.col,a2.col) || 0;
+  var c2=Math.max(a1.col,a2.col) || this.grid.columns.length-1;
+  return [r1,c1,r2,c2];
+},
+
+
+range2evalstr: function(token) {
+  var rng=this.resolveRange(token);
+  return rng ? rng.join(',') : '';
+},
+
+
+cellref2str: function(cellRef) {
+  var ref=this.resolveCellRef(cellRef);
+  var c=this.colNum2Letter(ref.col);
+  if (cellRef.absCol) c='$'+c;
+  var r=ref.row.toString();
+  if (cellRef.absRow) r='$'+r;
+  return c+r;
+},
+
+
+range2str: function(token) {
+  var s1=this.cellref2str(token.rangeStart);
+  var s2=this.cellref2str(token.rangeEnd);
+  return (s1==s2) ? s1 : s1+':'+s2;
+},
+
+
+GetRange: function(r1,c1,r2,c2) {
+  if (typeof r1=='undefined' || typeof c1=='undefined') return NaN;
+  if (r1==r2 && c1==c2) return this.grid.columns[c1].cell(r1-1).RicoValue;
+  var result=[];
+  for (var r=r1; r<=r2; r++) {
+    var newRow=[];
+    for (var c=c1; c<=c2; c++)
+      newRow.push(this.grid.columns[c].cell(r-1).RicoValue);
+    result.push(newRow);
+  }
+  return result;
+},
+
+
+getRanges: function() {
+  var result=[];
+  this.tokens.reset();
+  while (this.tokens.moveNext()) {
+    var token = this.tokens.current();
+    if (token.subtype=='range') result.push(this.resolveRange(token));
+  }
+  return result;
+},
+
+
+eval_sum: function() {
+  var result=0;
+  for (var i=0; i<arguments.length; i++) {
+    arg=arguments[i];
+    if (arg==null) continue;
+    switch (typeof arg) {
+      case 'number':
+        result+=arg;
+        break;
+      case 'object':
+        for (var r=0; r<arg.length; r++)
+          for (var c=0; c<arg[r].length; c++)
+            if (typeof arg[r][c]=='number') result+=arg[r][c];
+        break;
+    }
+  }
+  return result;
+},
+
+
+eval_count: function() {
+  var result=0;
+  for (var i=0; i<arguments.length; i++) {
+    arg=arguments[i];
+    if (arg==null) continue;
+    switch (typeof arg) {
+      case 'object':
+        for (var r=0; r<arg.length; r++)
+          for (var c=0; c<arg[r].length; c++)
+            if (arg[r][c] || typeof arg[r][c]=='number') result++;
+        break;
+      default:
+        if (arg || typeof arg=='number') result++;
+        break;
+    }
+  }
+  return result;
+},
+
+
+eval_t: function(arg) {
+  return (typeof arg=='string') ? arg : '';
+},
+
+
+eval_trim: function(arg) {
+  arg=this.argString(arg);
+  return arg.strip();
+},
+
+
+eval_lower: function(arg) {
+  arg=this.argString(arg);
+  return arg.toLowerCase();
+},
+
+
+eval_upper: function(arg) {
+  arg=this.argString(arg);
+  return arg.toUpperCase();
+},
+
+
+eval_len: function(arg) {
+  arg=this.argString(arg);
+  return arg.length;
+},
+
+
+eval_value: function(arg) {
+  arg=this.argString(arg);
+  return parseFloat(arg);
+},
+
+
+eval_left: function(arg,numchars) {
+  arg=this.argString(arg);
+  if (typeof numchars!='number') numchars=1;
+  if (numchars<0) return NaN;
+  return arg.slice(0,numchars);
+},
+
+
+eval_right: function(arg,numchars) {
+  arg=this.argString(arg);
+  if (typeof numchars!='number') numchars=1;
+  if (numchars<0) return NaN;
+  if (numchars==0) return '';
+  return arg.slice(-numchars);
+},
+
+
+eval_mid: function(arg,start,numchars) {
+  arg=this.argString(arg);
+  if (typeof start!='number' || start<1) return NaN;
+  if (typeof numchars!='number' || numchars<0) return NaN;
+  return arg.substr(start-1,numchars);
+},
+
+
+eval_if: function(logical_test, value_true, value_false) {
+  var v=this.argBool(logical_test);
+  if (v==null) return NaN;
+  return v ? value_true : value_false;
+},
+
+
+eval_not: function(arg) {
+  var v=this.argBool(arg);
+  return (v==null) ? NaN : !v;
+},
+
+
+eval_and: function() {
+  var args = $A(arguments);
+  args.unshift(function(a,b) { return a&&b; });
+  return this.or_and.apply(this, args);
+},
+
+
+eval_or: function() {
+  var args = $A(arguments);
+  args.unshift(function(a,b) { return a||b; });
+  return this.or_and.apply(this, args);
+},
+
+
+or_and: function() {
+  var result;
+  var func=arguments[0];
+  for (var i=1; i<arguments.length; i++) {
+    arg=arguments[i];
+    if (arg==null) continue;
+    switch (typeof arg) {
+      case 'object':
+        for (var r=0; r<arg.length; r++)
+          for (var c=0; c<arg[r].length; c++) {
+            var v=this.argBool(arg[r][c])
+            if (v!=null) result=(typeof result=='undefined') ? v : func(result,v);
+          }
+        break;
+      default:
+        var v=this.argBool(arg)
+        if (v!=null) result=(typeof result=='undefined') ? v : func(result,v);
+        break;
+    }
+  }
+  return (typeof result=='undefined') ? NaN : result;
+},
+
+
+eval_abs:     function(arg) { return Math.abs(this.argNumber(arg)); },
+eval_acos:    function(arg) { return Math.acos(this.argNumber(arg)); },
+eval_asin:    function(arg) { return Math.asin(this.argNumber(arg)); },
+eval_atan:    function(arg) { return Math.atan(this.argNumber(arg)); },
+eval_atan2:   function(argx,argy) { return Math.atan2(this.argNumber(argy),this.argNumber(argx)); },
+eval_ceiling: function(arg) { return Math.ceil(this.argNumber(arg)); },
+eval_cos:     function(arg) { return Math.cos(this.argNumber(arg)); },
+eval_exp:     function(arg) { return Math.exp(this.argNumber(arg)); },
+eval_floor:   function(arg) { return Math.floor(this.argNumber(arg)); },
+eval_ln:      function(arg) { return Math.log(this.argNumber(arg)); },
+eval_mod:     function(num,divisor) { return this.argNumber(num) % this.argNumber(divisor); },
+eval_pi:      function() { return Math.PI; },
+eval_power:   function(argx,argy) { return Math.pow(this.argNumber(argx),this.argNumber(argy)); },
+eval_rand:    function() { return Math.random(); },
+eval_round:   function(arg) { return Math.round(this.argNumber(arg)); },
+eval_sin:     function(arg) { return Math.sin(this.argNumber(arg)); },
+eval_sqrt:    function(arg) { return Math.sqrt(this.argNumber(arg)); },
+eval_tan:     function(arg) { return Math.tan(this.argNumber(arg)); },
+
+
+argNumber: function(arg) {
+  switch (typeof arg) {
+    case 'boolean': return arg;
+    case 'number': return arg;
+    case 'string': return parseFloat(arg);
+    default: return null;
+  }
+},
+
+
+argBool: function(arg) {
+  switch (typeof arg) {
+    case 'boolean': return arg;
+    case 'number': return arg!=0;
+    default: return null;
+  }
+},
+
+
+argString: function(arg) {
+  switch (typeof arg) {
+    case 'string': return arg;
+    case 'boolean':
+    case 'number': return arg.toString();
+    default: return '';
+  }
+},
+
+
+eval: function() {
+  var evalstr='';
+  this.tokens.reset();
+  while (this.tokens.moveNext()) {
+    var token = this.tokens.current();
+    switch (token.type) {
+      case 'function':
+        if (token.subtype=='start') {
+          var funcname='eval_'+token.value.toLowerCase();
+          if (typeof this[funcname]!='function') {
+            alert('Unknown function: '+token.value);
+            return '#ERROR';
+          }
+          evalstr+='this.'+funcname+'(';
+        } else
+          evalstr+=')';
+        break;
+      case 'subexpression':
+        if (token.subtype=='start')
+          evalstr+='(';
+        else
+          evalstr+=')';
+        break;
+      case 'operator-infix':
+        if (token.value=='&')
+          evalstr+='+';
+        else if (token.value=='=')
+          evalstr+='==';
+        else if (token.value=='<>')
+          evalstr+='!=';
+        else
+          evalstr+=token.value;
+        break;
+      case 'operator-postfix':
+        if (token.value=='%')
+          evalstr+='/100';
+        else
+          evalstr+=token.value;
+        break;
+      case 'operand':
+        if (token.subtype=='range')
+          evalstr+='this.GetRange('+this.range2evalstr(token)+')';
+        else if (token.subtype=='text')
+          evalstr+='"'+token.value+'"';
+        else
+          evalstr+=token.value;
+        break;
+      default:
+        evalstr+=token.value;
+        break;
+    }
+  }
+  this.lastEval=evalstr;
+  //window.status=evalstr;
+  try {
+    var result=eval(evalstr)
+    return result;
+  } catch(e) { alert(e.message); return '#ERROR'; }
+},
+
+
+toEditString: function() {
+  var s='=';
+  this.tokens.reset();
+  while (this.tokens.moveNext()) {
+    var token = this.tokens.current();
+    switch (token.type) {
+      case 'function':
+        if (token.subtype=='start')
+          s+=token.value+'(';
+        else
+          s+=')';
+        break;
+      case 'subexpression':
+        if (token.subtype=='start')
+          s+='(';
+        else
+          s+=')';
+        break;
+      case 'operand':
+        if (token.subtype=='range')
+          s+=this.range2str(token);
+        else if (token.subtype=='text')
+          s+='"'+token.value+'"';
+        else
+          s+=token.value;
+        break;
+      default:
+        s+=token.value;
+    }
+  }
+  return s;
+},
+
+
+// Excel formula parser
+// from http://ewbi.blogs.com/develops/2004/12/excel_formula_p.html
+parse: function(formula) {
+  var tokens = new Rico.Formula.f_tokens();
+  var tokenStack = new Rico.Formula.f_tokenStack();
+
+  var offset = 0;
+
+  var currentChar = function() { return formula.substr(offset, 1); };
+  var doubleChar  = function() { return formula.substr(offset, 2); };
+  var nextChar    = function() { return formula.substr(offset + 1, 1); };
+  var EOF         = function() { return (offset >= formula.length); };
+
+  var token = "";
+
+  var inString = false;
+  var inPath = false;
+  var inRange = false;
+  var inError = false;
+
+  while (formula.length > 0) {
+    if (formula.substr(0, 1) == " ")
+      formula = formula.substr(1);
+    else {
+      if (formula.substr(0, 1) == "=")
+        formula = formula.substr(1);
+      break;
+    }
+  }
+
+  while (!EOF()) {
+
+    // state-dependent character evaluation (order is important)
+
+    // double-quoted strings
+    // embeds are doubled
+    // end marks token
+
+    if (inString) {
+      if (currentChar() == "\"") {
+        if (nextChar() == "\"") {
+          token += "\"";
+          offset += 1;
+        } else {
+          inString = false;
+          tokens.add(token, Rico.Formula.TOK_TYPE_OPERAND, Rico.Formula.TOK_SUBTYPE_TEXT);
+          token = "";
+        }
+      } else {
+        token += currentChar();
+      }
+      offset += 1;
+      continue;
+    }
+
+    // single-quoted strings (links)
+    // embeds are double
+    // end does not mark a token
+
+    if (inPath) {
+      if (currentChar() == "'") {
+        if (nextChar() == "'") {
+          token += "'";
+          offset += 1;
+        } else {
+          inPath = false;
+        }
+      } else {
+        token += currentChar();
+      }
+      offset += 1;
+      continue;
+    }
+
+    // bracked strings (range offset or linked workbook name)
+    // no embeds (changed to "()" by Excel)
+    // end does not mark a token
+
+    if (inRange) {
+      if (currentChar() == "]") {
+        inRange = false;
+      }
+      token += currentChar();
+      offset += 1;
+      continue;
+    }
+
+    // error values
+    // end marks a token, determined from absolute list of values
+
+    if (inError) {
+      token += currentChar();
+      offset += 1;
+      if ((",#NULL!,#DIV/0!,#VALUE!,#REF!,#NAME?,#NUM!,#N/A,").indexOf("," + token + ",") != -1) {
+        inError = false;
+        tokens.add(token, Rico.Formula.TOK_TYPE_OPERAND, Rico.Formula.TOK_SUBTYPE_ERROR);
+        token = "";
+      }
+      continue;
+    }
+
+    // independent character evaulation (order not important)
+
+    // establish state-dependent character evaluations
+
+    if (currentChar() == "\"") {
+      if (token.length > 0) {
+        // not expected
+        tokens.add(token, Rico.Formula.TOK_TYPE_UNKNOWN);
+        token = "";
+      }
+      inString = true;
+      offset += 1;
+      continue;
+    }
+
+    if (currentChar() == "'") {
+      if (token.length > 0) {
+        // not expected
+        tokens.add(token, Rico.Formula.TOK_TYPE_UNKNOWN);
+        token = "";
+      }
+      inPath = true;
+      offset += 1;
+      continue;
+    }
+
+    if (currentChar() == "[") {
+      inRange = true;
+      token += currentChar();
+      offset += 1;
+      continue;
+    }
+
+    if (currentChar() == "#") {
+      if (token.length > 0) {
+        // not expected
+        tokens.add(token, Rico.Formula.TOK_TYPE_UNKNOWN);
+        token = "";
+      }
+      inError = true;
+      token += currentChar();
+      offset += 1;
+      continue;
+    }
+
+    // mark start and end of arrays and array rows
+
+    if (currentChar() == "{") {
+      if (token.length > 0) {
+        // not expected
+        tokens.add(token, Rico.Formula.TOK_TYPE_UNKNOWN);
+        token = "";
+      }
+      tokenStack.push(tokens.add("ARRAY", Rico.Formula.TOK_TYPE_FUNCTION, Rico.Formula.TOK_SUBTYPE_START));
+      tokenStack.push(tokens.add("ARRAYROW", Rico.Formula.TOK_TYPE_FUNCTION, Rico.Formula.TOK_SUBTYPE_START));
+      offset += 1;
+      continue;
+    }
+
+    if (currentChar() == ";") {
+      if (token.length > 0) {
+        tokens.add(token, Rico.Formula.TOK_TYPE_OPERAND);
+        token = "";
+      }
+      tokens.addRef(tokenStack.pop());
+      tokens.add(",", Rico.Formula.TOK_TYPE_ARGUMENT);
+      tokenStack.push(tokens.add("ARRAYROW", Rico.Formula.TOK_TYPE_FUNCTION, Rico.Formula.TOK_SUBTYPE_START));
+      offset += 1;
+      continue;
+    }
+
+    if (currentChar() == "}") {
+      if (token.length > 0) {
+        tokens.add(token, Rico.Formula.TOK_TYPE_OPERAND);
+        token = "";
+      }
+      tokens.addRef(tokenStack.pop());
+      tokens.addRef(tokenStack.pop());
+      offset += 1;
+      continue;
+    }
+
+    // trim white-space
+
+    if (currentChar() == " ") {
+      if (token.length > 0) {
+        tokens.add(token, Rico.Formula.TOK_TYPE_OPERAND);
+        token = "";
+      }
+      tokens.add("", Rico.Formula.TOK_TYPE_WSPACE);
+      offset += 1;
+      while ((currentChar() == " ") && (!EOF())) {
+        offset += 1;
+      }
+      continue;
+    }
+
+    // multi-character comparators
+
+    if ((",>=,<=,<>,").indexOf("," + doubleChar() + ",") != -1) {
+      if (token.length > 0) {
+        tokens.add(token, Rico.Formula.TOK_TYPE_OPERAND);
+        token = "";
+      }
+      tokens.add(doubleChar(), Rico.Formula.TOK_TYPE_OP_IN, Rico.Formula.TOK_SUBTYPE_LOGICAL);
+      offset += 2;
+      continue;
+    }
+
+    // standard infix operators
+
+    if (("+-*/^&=><").indexOf(currentChar()) != -1) {
+      if (token.length > 0) {
+        tokens.add(token, Rico.Formula.TOK_TYPE_OPERAND);
+        token = "";
+      }
+      tokens.add(currentChar(), Rico.Formula.TOK_TYPE_OP_IN);
+      offset += 1;
+      continue;
+    }
+
+    // standard postfix operators
+
+    if (("%").indexOf(currentChar()) != -1) {
+      if (token.length > 0) {
+        tokens.add(token, Rico.Formula.TOK_TYPE_OPERAND);
+        token = "";
+      }
+      tokens.add(currentChar(), Rico.Formula.TOK_TYPE_OP_POST);
+      offset += 1;
+      continue;
+    }
+
+    // start subexpression or function
+
+    if (currentChar() == "(") {
+      if (token.length > 0) {
+        tokenStack.push(tokens.add(token, Rico.Formula.TOK_TYPE_FUNCTION, Rico.Formula.TOK_SUBTYPE_START));
+        token = "";
+      } else {
+        tokenStack.push(tokens.add("", Rico.Formula.TOK_TYPE_SUBEXPR, Rico.Formula.TOK_SUBTYPE_START));
+      }
+      offset += 1;
+      continue;
+    }
+
+    // function, subexpression, array parameters
+
+    if (currentChar() == ",") {
+      if (token.length > 0) {
+        tokens.add(token, Rico.Formula.TOK_TYPE_OPERAND);
+        token = "";
+      }
+      if (!(tokenStack.type() == Rico.Formula.TOK_TYPE_FUNCTION)) {
+        tokens.add(currentChar(), Rico.Formula.TOK_TYPE_OP_IN, Rico.Formula.TOK_SUBTYPE_UNION);
+      } else {
+        tokens.add(currentChar(), Rico.Formula.TOK_TYPE_ARGUMENT);
+      }
+      offset += 1;
+      continue;
+    }
+
+    // stop subexpression
+
+    if (currentChar() == ")") {
+      if (token.length > 0) {
+        tokens.add(token, Rico.Formula.TOK_TYPE_OPERAND);
+        token = "";
+      }
+      tokens.addRef(tokenStack.pop());
+      offset += 1;
+      continue;
+    }
+
+    // token accumulation
+
+    token += currentChar();
+    offset += 1;
+
+  }
+
+  // dump remaining accumulation
+
+  if (token.length > 0) tokens.add(token, Rico.Formula.TOK_TYPE_OPERAND);
+
+  // move all tokens to a new collection, excluding all unnecessary white-space tokens
+
+  var tokens2 = new Rico.Formula.f_tokens();
+
+  while (tokens.moveNext()) {
+
+    token = tokens.current();
+
+    if (token.type == Rico.Formula.TOK_TYPE_WSPACE) {
+      if ((tokens.BOF()) || (tokens.EOF())) {}
+      else if (!(
+                 ((tokens.previous().type == Rico.Formula.TOK_TYPE_FUNCTION) && (tokens.previous().subtype == Rico.Formula.TOK_SUBTYPE_STOP)) ||
+                 ((tokens.previous().type == Rico.Formula.TOK_TYPE_SUBEXPR) && (tokens.previous().subtype == Rico.Formula.TOK_SUBTYPE_STOP)) ||
+                 (tokens.previous().type == Rico.Formula.TOK_TYPE_OPERAND)
+                )
+              ) {}
+      else if (!(
+                 ((tokens.next().type == Rico.Formula.TOK_TYPE_FUNCTION) && (tokens.next().subtype == Rico.Formula.TOK_SUBTYPE_START)) ||
+                 ((tokens.next().type == Rico.Formula.TOK_TYPE_SUBEXPR) && (tokens.next().subtype == Rico.Formula.TOK_SUBTYPE_START)) ||
+                 (tokens.next().type == Rico.Formula.TOK_TYPE_OPERAND)
+                 )
+               ) {}
+      else
+        tokens2.add(token.value, Rico.Formula.TOK_TYPE_OP_IN, Rico.Formula.TOK_SUBTYPE_INTERSECT);
+      continue;
+    }
+
+    tokens2.addRef(token);
+
+  }
+
+  // switch infix "-" operator to prefix when appropriate, switch infix "+" operator to noop when appropriate, identify operand
+  // and infix-operator subtypes, pull "@" from in front of function names
+
+  while (tokens2.moveNext()) {
+
+    token = tokens2.current();
+
+    if ((token.type == Rico.Formula.TOK_TYPE_OP_IN) && (token.value == "-")) {
+      if (tokens2.BOF())
+        token.type = Rico.Formula.TOK_TYPE_OP_PRE;
+      else if (
+               ((tokens2.previous().type == Rico.Formula.TOK_TYPE_FUNCTION) && (tokens2.previous().subtype == Rico.Formula.TOK_SUBTYPE_STOP)) ||
+               ((tokens2.previous().type == Rico.Formula.TOK_TYPE_SUBEXPR) && (tokens2.previous().subtype == Rico.Formula.TOK_SUBTYPE_STOP)) ||
+               (tokens2.previous().type == Rico.Formula.TOK_TYPE_OP_POST) ||
+               (tokens2.previous().type == Rico.Formula.TOK_TYPE_OPERAND)
+              )
+        token.subtype = Rico.Formula.TOK_SUBTYPE_MATH;
+      else
+        token.type = Rico.Formula.TOK_TYPE_OP_PRE;
+      continue;
+    }
+
+    if ((token.type == Rico.Formula.TOK_TYPE_OP_IN) && (token.value == "+")) {
+      if (tokens2.BOF())
+        token.type = Rico.Formula.TOK_TYPE_NOOP;
+      else if (
+               ((tokens2.previous().type == Rico.Formula.TOK_TYPE_FUNCTION) && (tokens2.previous().subtype == Rico.Formula.TOK_SUBTYPE_STOP)) ||
+               ((tokens2.previous().type == Rico.Formula.TOK_TYPE_SUBEXPR) && (tokens2.previous().subtype == Rico.Formula.TOK_SUBTYPE_STOP)) ||
+               (tokens2.previous().type == Rico.Formula.TOK_TYPE_OP_POST) ||
+               (tokens2.previous().type == Rico.Formula.TOK_TYPE_OPERAND)
+              )
+        token.subtype = Rico.Formula.TOK_SUBTYPE_MATH;
+      else
+        token.type = Rico.Formula.TOK_TYPE_NOOP;
+      continue;
+    }
+
+    if ((token.type == Rico.Formula.TOK_TYPE_OP_IN) && (token.subtype.length == 0)) {
+      if (("<>=").indexOf(token.value.substr(0, 1)) != -1)
+        token.subtype = Rico.Formula.TOK_SUBTYPE_LOGICAL;
+      else if (token.value == "&")
+        token.subtype = Rico.Formula.TOK_SUBTYPE_CONCAT;
+      else
+        token.subtype = Rico.Formula.TOK_SUBTYPE_MATH;
+      continue;
+    }
+
+    if ((token.type == Rico.Formula.TOK_TYPE_OPERAND) && (token.subtype.length == 0)) {
+      if (isNaN(parseFloat(token.value)))
+        if ((token.value == 'TRUE') || (token.value == 'FALSE'))
+          token.subtype = Rico.Formula.TOK_SUBTYPE_LOGICAL;
+        else {
+          token.subtype = Rico.Formula.TOK_SUBTYPE_RANGE;
+          var a=token.value.split(':');
+          token.rangeStart=this.parseCellRef(a[0]);
+          token.rangeEnd=a.length>1 ? this.parseCellRef(a[1]) : token.rangeStart;
+        }
+      else
+        token.subtype = Rico.Formula.TOK_SUBTYPE_NUMBER;
+      continue;
+    }
+
+    if (token.type == Rico.Formula.TOK_TYPE_FUNCTION) {
+      if (token.value.substr(0, 1) == "@")
+        token.value = token.value.substr(1);
+      continue;
+    }
+
+  }
+
+  tokens2.reset();
+
+  // move all tokens to a new collection, excluding all noops
+
+  this.tokens = new Rico.Formula.f_tokens();
+
+  while (tokens2.moveNext()) {
+    if (tokens2.current().type != Rico.Formula.TOK_TYPE_NOOP)
+      this.tokens.addRef(tokens2.current());
+  }
+}
+
+}
+
+
+Rico.Formula.f_token = Class.create();
+Rico.Formula.f_token.prototype = {
+  initialize: function(value, type, subtype) {
+    this.value = value;
+    this.type = type;
+    this.subtype = subtype;
+  }
+}
+
+
+Rico.Formula.f_tokens = Class.create();
+Rico.Formula.f_tokens.prototype = {
+  initialize: function() {
+    this.items = new Array();
+    this.index = -1;
+  },
+
+  addRef: function(token) {
+    this.items.push(token);
+  },
+
+  add: function(value, type, subtype) {
+    if (!subtype) subtype = "";
+    var token = new Rico.Formula.f_token(value, type, subtype);
+    this.addRef(token);
+    return token;
+  },
+
+  reset: function() {
+    this.index = -1;
+  },
+
+  BOF: function() {
+    return (this.index <= 0);
+  },
+
+  EOF: function() {
+    return (this.index >= (this.items.length - 1));
+  },
+
+  moveNext: function() {
+    if (this.EOF()) return false; this.index++; return true;
+  },
+
+  current: function() {
+    if (this.index == -1) return null; return (this.items[this.index]);
+  },
+
+  next: function() {
+    if (this.EOF()) return null; return (this.items[this.index + 1]);
+  },
+
+  previous: function() {
+    if (this.index < 1) return null; return (this.items[this.index - 1]);
+  }
+}
+
+
+Rico.Formula.f_tokenStack = Class.create();
+Rico.Formula.f_tokenStack.prototype = {
+  initialize: function() {
+    this.items = new Array();
+  },
+
+  push: function(token) {
+    this.items.push(token);
+  },
+
+  pop: function() {
+    var token = this.items.pop();
+    return (new Rico.Formula.f_token("", token.type, Rico.Formula.TOK_SUBTYPE_STOP));
+  },
+
+  token: function() {
+    return ((this.items.length > 0) ? this.items[this.items.length - 1] : null);
+  },
+
+  value: function() {
+    return ((this.token()) ? this.token().value : "");
+  },
+
+  type: function() {
+    return ((this.token()) ? this.token().type : "");
+  },
+
+  subtype: function() {
+    return ((this.token()) ? this.token().subtype : "");
+  }
+}
+
+
+Rico.Formula.f_dependencies = Class.create();
+Rico.Formula.f_dependencies.prototype = {
+  initialize: function() {
+    this.items = [];
+  },
+
+  add: function(cell) {
+    if (!this.items.include(cell)) this.items.push(cell);
+  },
+
+  remove: function(cell) {
+    this.items=this.items.select(function(item) { return (item != cell); });
+  },
+
+  find: function(cell) {
+    return this.items.detect(function(item) { return (item==cell); });
+  },
+
+  clear: function() {
+    this.items.clear();
+  }
+}
+
+
+Object.extend(Rico.Menu.prototype, {
+
+showSheetMenu: function(e,hideFunc) {
+  var elem=this.showSimpleMenu(e,hideFunc);
+  if (!this.grid) return;
+  var newIdx=this.grid.cellIndex(elem);
+  if (!this.grid.isSelected(newIdx.row,newIdx.column))
+    this.grid.selectCellRC(newIdx.row,newIdx.column,false);
+}
+
+});
+
+
+Rico.includeLoaded('ricoSheet.js');
diff --git a/NP_TrackBack/trunk/trackback/js/rico/ricoSimpleGrid.js b/NP_TrackBack/trunk/trackback/js/rico/ricoSimpleGrid.js
new file mode 100644 (file)
index 0000000..a6b407e
--- /dev/null
@@ -0,0 +1,160 @@
+/**
+  *  (c) 2005-2007 Richard Cowin (http://openrico.org)
+  *  (c) 2005-2007 Matt Brown (http://dowdybrown.com)
+  *
+  *  Rico is 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
+  **/
+
+
+if(typeof Rico=='undefined') throw("SimpleGrid requires the Rico JavaScript framework");
+if(typeof RicoUtil=='undefined') throw("SimpleGrid requires the RicoUtil Library");
+if(typeof RicoTranslate=='undefined') throw("SimpleGrid requires the RicoTranslate Library");
+
+Rico.SimpleGrid = Class.create();
+
+Rico.SimpleGrid.prototype = {
+
+  initialize: function( tableId, options ) {
+    Object.extend(this, new Rico.GridCommon);
+    this.baseInit(tableId);
+    Rico.setDebugArea(tableId+"_debugmsgs");    // if used, this should be a textarea
+    Object.extend(this.options, options || {});
+    this.tableId = tableId;
+    this.createDivs();
+    this.hdrTabs=new Array(2);
+    for (var i=0; i<2; i++) {
+      this.tabs[i]=$(tableId+'_tab'+i);
+      this.hdrTabs[i]=$(tableId+'_tab'+i+'h');
+      if (i==0) this.tabs[i].style.position='absolute';
+      if (i==0) this.tabs[i].style.left='0px';
+      this.hdrTabs[i].style.position='absolute';
+      this.hdrTabs[i].style.top='0px';
+      this.hdrTabs[i].style.zIndex=1;
+      this.thead[i]=this.hdrTabs[i];
+      this.tbody[i]=this.tabs[i];
+      this.headerColCnt = this.getColumnInfo(this.hdrTabs[i].rows);
+      if (i==0) this.options.frozenColumns=this.headerColCnt;
+    }
+    if (this.headerColCnt==0) {
+      alert('ERROR: no columns found in "'+this.tableId+'"');
+      return;
+    }
+    this.hdrHt=Math.max(RicoUtil.nan2zero(this.hdrTabs[0].offsetHeight),this.hdrTabs[1].offsetHeight);
+    for (var i=0; i<2; i++)
+      if (i==0) this.tabs[i].style.top=this.hdrHt+'px';
+    this.createColumnArray();
+    this.pageSize=this.columns[0].dataColDiv.childNodes.length;
+    this.sizeDivs();
+    this.attachMenuEvents();
+    this.scrollEventFunc=this.handleScroll.bindAsEventListener(this);
+    this.pluginScroll();
+    if (this.options.windowResize)
+      Event.observe(window,"resize", this.sizeDivs.bindAsEventListener(this), false);
+  },
+
+  /**
+   * Register a menu that will only be used in the scrolling part of the grid.
+   * If submenus are used, they must be registered after the main menu.
+   */
+  registerScrollMenu: function(menu) {
+    if (!this.menu) this.menu=menu;
+    menu.grid=this;
+    menu.showmenu=menu.showSimpleMenu;
+    menu.showSubMenu=menu.showSimpleSubMenu;
+    menu.createDiv(this.scrollDiv);
+  },
+  
+  handleMenuClick: function(e) {
+    this.cancelMenu();
+    this.menuCell=RicoUtil.getParentByTagName(Event.element(e),'div');
+    this.highlightEnabled=false;
+    if (this.hideScroll) this.scrollDiv.style.overflow="hidden";
+    if (this.menu.buildGridMenu) this.menu.buildGridMenu(this.menuCell);
+    this.menu.showmenu(e,this.closeMenu.bind(this));
+  },
+
+  closeMenu: function() {
+    if (this.hideScroll) this.scrollDiv.style.overflow="";
+    this.highlightEnabled=true;
+  },
+
+  sizeDivs: function() {
+    if (this.outerDiv.offsetParent.style.display=='none') return;
+    this.baseSizeDivs();
+    var maxHt=Math.max(this.options.maxHt || this.availHt(), 50);
+    var totHt=Math.min(this.hdrHt+this.dataHt, maxHt);
+    Rico.writeDebugMsg('sizeDivs '+this.tableId+': hdrHt='+this.hdrHt+' dataHt='+this.dataHt);
+    this.dataHt=totHt-this.hdrHt;
+    if (this.scrWi>0) this.dataHt+=this.options.scrollBarWidth;
+    this.scrollDiv.style.height=this.dataHt+'px';
+    var divAdjust=2;
+    this.innerDiv.style.width=(this.scrWi-this.options.scrollBarWidth+divAdjust)+'px';
+    this.innerDiv.style.height=this.hdrHt+'px';
+    totHt+=divAdjust;
+    this.resizeDiv.style.height=this.frozenTabs.style.height=totHt+'px';
+    this.outerDiv.style.height=(totHt+this.options.scrollBarWidth)+'px';
+    this.setHorizontalScroll();
+  }
+
+};
+
+if (Rico.Menu) {
+Object.extend(Rico.Menu.prototype, {
+
+showSimpleMenu: function(e,hideFunc) {
+  Event.stop(e);
+  this.hideFunc=hideFunc;
+  if (this.div.childNodes.length==0) {
+    this.cancelmenu();
+    return false;
+  }
+  this.clientX=Event.pointerX(e);
+  this.clientY=Event.pointerY(e);
+  var elem=Event.element(e);
+  while (elem && !Element.hasClassName(elem,'ricoLG_cell'))
+    elem=elem.parentNode;
+  if (!elem) return false;
+  var td=RicoUtil.getParentByTagName(elem,'td');
+
+  var newLeft=Math.floor(td.offsetLeft+td.offsetWidth/2);
+  if (this.direction == 'rtl') {
+    if (newLeft > this.width) newLeft-=this.width;
+  } else {
+    if (newLeft+this.width+this.options.margin > this.grid.scrollDiv.scrollLeft+this.grid.scrollDiv.clientWidth) newLeft-=this.width;
+  }
+  this.div.style.visibility="hidden";
+  this.div.style.display="block";
+  var contentHt=this.div.offsetHeight;
+  var newTop=Math.floor(elem.offsetTop+elem.offsetHeight/2);
+  if (newTop+contentHt+this.options.margin > this.grid.scrollDiv.scrollTop+this.grid.scrollDiv.clientHeight)
+    newTop=Math.max(newTop-contentHt,0);
+  this.openPopup(newLeft,newTop);
+  this.div.style.visibility ="visible";
+  return elem;
+},
+
+showSimpleSubMenu: function(a,submenu) {
+  if (this.openSubMenu) this.hideSubMenu();
+  this.openSubMenu=submenu;
+  this.openMenuAnchor=a;
+  if (a.className=='ricoSubMenu') a.className='ricoSubMenuOpen';
+  var top=parseInt(this.div.style.top);
+  var left=parseInt(this.div.style.left);
+  submenu.openPopup(left+a.offsetWidth,top+a.offsetTop);
+  submenu.div.style.visibility ="visible";
+}
+    
+});
+}
+
+Object.extend(Rico.TableColumn.prototype, {
+
+initialize: function(grid,colIdx,hdrInfo,tabIdx) {
+  this.baseInit(grid,colIdx,hdrInfo,tabIdx);
+}
+
+});
+
+Rico.includeLoaded('ricoSimpleGrid.js');
diff --git a/NP_TrackBack/trunk/trackback/js/rico/ricoSimpleGrid.xsl b/NP_TrackBack/trunk/trackback/js/rico/ricoSimpleGrid.xsl
new file mode 100644 (file)
index 0000000..f3c0dc2
--- /dev/null
@@ -0,0 +1,285 @@
+<?xml version="1.0"?>\r
+\r
+<xsl:stylesheet version="1.0"\r
+  xmlns:xhtml="http://www.w3.org/1999/xhtml"\r
+  xmlns="http://www.w3.org/1999/xhtml"\r
+  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"\r
+  xmlns:xs="http://www.w3.org/2001/XMLSchema"\r
+  xmlns:fn="http://www.w3.org/2005/02/xpath-functions"\r
+  xmlns:xdt="http://www.w3.org/2005/02/xpath-datatypes"\r
+exclude-result-prefixes="xhtml xsl fn xs xdt">\r
+\r
+<xsl:output\r
+omit-xml-declaration="yes"\r
+method="html"\r
+doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"\r
+doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"/>\r
+\r
+<xsl:attribute-set name="ricoTable"> \r
+  <xsl:attribute name="cellspacing">0</xsl:attribute> \r
+  <xsl:attribute name="cellpadding">0</xsl:attribute> \r
+</xsl:attribute-set> \r
+\r
+<!-- the identity template -->\r
+\r
+<xsl:template match="*">\r
+  <xsl:copy>\r
+  <xsl:copy-of select="@*"/>\r
+  <xsl:apply-templates/>\r
+  </xsl:copy>\r
+</xsl:template>\r
+\r
+\r
+<!-- Transform head section -->\r
+\r
+<xsl:template match="xhtml:head">\r
+  <xsl:copy>\r
+  <xsl:apply-templates mode="head"/>\r
+<script type="text/javascript">\r
+//<![CDATA[\r
+if (typeof ricoInit!='undefined') {\r
+  if (window.addEventListener)\r
+    window.addEventListener('load', ricoInit, false);\r
+  else if (window.attachEvent)\r
+    window.attachEvent('onload', ricoInit);\r
+}\r
+// ]]>\r
+</script>\r
+  </xsl:copy>\r
+</xsl:template>\r
+\r
+<xsl:template match="*[name()!='script']" mode="head">\r
+  <xsl:copy>\r
+  <xsl:copy-of select="@*|node()"/>\r
+  </xsl:copy>\r
+</xsl:template>\r
+\r
+<xsl:template match="xhtml:script" mode="head">\r
+  <xsl:copy>\r
+  <xsl:copy-of select="@*"/>\r
+  <xsl:value-of select="." disable-output-escaping="yes"/>\r
+  </xsl:copy>\r
+</xsl:template>\r
+\r
+\r
+<!-- Transform tables with class ricoSimpleGrid -->\r
+  \r
+<xsl:template match="xhtml:table[@class='ricoSimpleGrid']">\r
+<xsl:choose>\r
+\r
+<xsl:when test="xhtml:thead">\r
+<xsl:call-template name="processTable">\r
+<xsl:with-param name="id" select="@id"/>\r
+<xsl:with-param name="headRows" select="xhtml:thead/xhtml:tr"/>\r
+<xsl:with-param name="bodyRows" select="xhtml:tbody/xhtml:tr"/>\r
+</xsl:call-template>\r
+</xsl:when>\r
+\r
+<xsl:when test="xhtml:tbody">\r
+<xsl:call-template name="processTable">\r
+<xsl:with-param name="id" select="@id"/>\r
+<xsl:with-param name="headRows" select="xhtml:tbody/xhtml:tr[1]"/>\r
+<xsl:with-param name="bodyRows" select="xhtml:tbody/xhtml:tr[position() &gt; 1]"/>\r
+</xsl:call-template>\r
+</xsl:when>\r
+\r
+<xsl:otherwise>\r
+<xsl:call-template name="processTable">\r
+<xsl:with-param name="id" select="@id"/>\r
+<xsl:with-param name="headRows" select="xhtml:tr[1]"/>\r
+<xsl:with-param name="bodyRows" select="xhtml:tr[position() &gt; 1]"/>\r
+</xsl:call-template>\r
+</xsl:otherwise>\r
+\r
+</xsl:choose>\r
+</xsl:template>\r
+\r
+\r
+<!-- Perform the actual table transformation -->\r
+  \r
+<xsl:template name="processTable">\r
+<xsl:param name="id" />\r
+<xsl:param name="headRows" />\r
+<xsl:param name="bodyRows" />\r
+\r
+<xsl:variable name="headIdx">\r
+<xsl:choose>\r
+<xsl:when test="$headRows[@class='ricoHeading']">\r
+<xsl:value-of select="count($headRows[@class='ricoHeading']/preceding-sibling::*)+1"/>\r
+</xsl:when>\r
+<xsl:otherwise>\r
+<xsl:value-of select="count($headRows)"/>\r
+</xsl:otherwise>\r
+</xsl:choose>\r
+</xsl:variable>\r
+\r
+<xsl:variable name="headMain" select="$headRows[position()=$headIdx]"/>\r
+<xsl:variable name="headCols" select="$headMain/xhtml:th | $headMain/xhtml:td"/>\r
+\r
+<!--\r
+<p><xsl:value-of select="$id"/>\r
+<br />headRowCnt: <xsl:value-of select="count($headRows)"/>\r
+<br />headIdx: <xsl:value-of select="$headIdx"/>\r
+<br />bodyRowCnt: <xsl:value-of select="count($bodyRows)"/>\r
+</p>\r
+-->\r
+\r
+<xsl:element name="div">\r
+<xsl:attribute name="id"><xsl:value-of select="concat($id,'_outerDiv')"/></xsl:attribute>\r
+<xsl:attribute name="class">ricoLG_outerDiv</xsl:attribute>\r
+<xsl:attribute name="onload"></xsl:attribute>\r
+\r
+<!-- Create frozen (left) pane -->\r
+\r
+<xsl:element name="div">\r
+<xsl:attribute name="id"><xsl:value-of select="concat($id,'_frozenTabsDiv')"/></xsl:attribute>\r
+<xsl:attribute name="class">ricoLG_frozenTabsDiv</xsl:attribute>\r
+\r
+<xsl:call-template name="convertTHead">\r
+<xsl:with-param name="rows" select="$headRows"/>\r
+<xsl:with-param name="headIdx" select="$headIdx"/>\r
+<xsl:with-param name="frozen" select="1"/>\r
+<xsl:with-param name="id" select="concat($id,'_tab0h')"/>\r
+</xsl:call-template>\r
+\r
+<xsl:call-template name="convertTBody">\r
+<xsl:with-param name="rows" select="$bodyRows"/>\r
+<xsl:with-param name="cols" select="$headCols"/>\r
+<xsl:with-param name="id" select="concat($id,'_tab0')"/>\r
+<xsl:with-param name="frozen" select="1"/>\r
+</xsl:call-template>\r
+\r
+</xsl:element>\r
+\r
+<xsl:element name="div">\r
+<xsl:attribute name="id"><xsl:value-of select="concat($id,'_innerDiv')"/></xsl:attribute>\r
+<xsl:attribute name="class">ricoLG_innerDiv</xsl:attribute>\r
+\r
+<xsl:element name="div">\r
+<xsl:attribute name="id"><xsl:value-of select="concat($id,'_scrollTabsDiv')"/></xsl:attribute>\r
+<xsl:attribute name="class">ricoLG_scrollTabsDiv</xsl:attribute>\r
+\r
+<xsl:call-template name="convertTHead">\r
+<xsl:with-param name="rows" select="$headRows"/>\r
+<xsl:with-param name="headIdx" select="$headIdx"/>\r
+<xsl:with-param name="frozen" select="0"/>\r
+<xsl:with-param name="id" select="concat($id,'_tab1h')"/>\r
+</xsl:call-template>\r
+\r
+</xsl:element>\r
+</xsl:element>\r
+\r
+<xsl:element name="div">\r
+<xsl:attribute name="id"><xsl:value-of select="concat($id,'_scrollDiv')"/></xsl:attribute>\r
+<xsl:attribute name="class">ricoLG_scrollDiv</xsl:attribute>\r
+\r
+<xsl:call-template name="convertTBody">\r
+<xsl:with-param name="rows" select="$bodyRows"/>\r
+<xsl:with-param name="cols" select="$headCols"/>\r
+<xsl:with-param name="id" select="concat($id,'_tab1')"/>\r
+<xsl:with-param name="frozen" select="0"/>\r
+</xsl:call-template>\r
+</xsl:element>\r
+\r
+</xsl:element>\r
+\r
+</xsl:template>\r
+\r
+\r
+<!-- Convert thead section -->\r
+\r
+<xsl:template name="convertTHead">\r
+<xsl:param name = "rows" />\r
+<xsl:param name = "headIdx" />\r
+<xsl:param name = "frozen" />\r
+<xsl:param name = "id" />\r
+<xsl:element name="table" use-attribute-sets="ricoTable">\r
+<xsl:attribute name="id"><xsl:value-of select="$id"/></xsl:attribute>\r
+<xsl:attribute name="class">ricoLG_table ricoLG_top\r
+<xsl:if test="$frozen">ricoLG_left</xsl:if>\r
+<xsl:if test="not($frozen)">ricoLG_right</xsl:if>\r
+</xsl:attribute>\r
+<xsl:element name="thead">\r
+  <xsl:for-each select="$rows">\r
+    <xsl:choose>\r
+    <xsl:when test="position() = $headIdx">\r
+      <xsl:apply-templates select="." mode="convertHeadRow">\r
+      <xsl:with-param name="id" select="concat($id,'_main')"/>\r
+      <xsl:with-param name="frozen" select="$frozen"/>\r
+      </xsl:apply-templates>\r
+    </xsl:when>\r
+    <xsl:otherwise>\r
+      <xsl:apply-templates select="." mode="convertHeadRow">\r
+      <xsl:with-param name="id" select="concat($id,'_',position())"/>\r
+      <xsl:with-param name="frozen" select="$frozen"/>\r
+      </xsl:apply-templates>\r
+    </xsl:otherwise>\r
+    </xsl:choose>\r
+  </xsl:for-each>\r
+</xsl:element>\r
+<tbody />\r
+</xsl:element>\r
+</xsl:template>\r
+\r
+\r
+<xsl:template match="*" mode="convertHeadRow">\r
+<xsl:param name = "id" />\r
+<xsl:param name = "frozen" />\r
+  <xsl:variable name="class" select="@class"/>\r
+  <xsl:variable name="cells" select="xhtml:th | xhtml:td"/>\r
+  <xsl:element name="tr">\r
+  <xsl:if test="$id">\r
+    <xsl:attribute name="id"><xsl:value-of select="$id"/></xsl:attribute>\r
+  </xsl:if>\r
+  <xsl:attribute name="class">ricoLG_hdg <xsl:value-of select="$class"/></xsl:attribute>\r
+  <xsl:for-each select="$cells[@class='ricoFrozen' and $frozen or not(@class='ricoFrozen') and not($frozen)]">\r
+      <xsl:copy>\r
+        <xsl:copy-of select="@*"/>\r
+        <div class='ricoLG_col' style='width:100px'>\r
+          <xsl:element name="div">\r
+          <xsl:attribute name="class">ricoLG_cell <xsl:value-of select="@class"/></xsl:attribute>\r
+            <xsl:copy-of select="* | @*[name()!='class'] | text()"/>\r
+          </xsl:element>\r
+        </div>\r
+      </xsl:copy>\r
+    </xsl:for-each>\r
+  </xsl:element>\r
+</xsl:template>\r
+\r
+\r
+<!-- Convert tbody section -->\r
+\r
+<xsl:template name="convertTBody">\r
+<xsl:param name = "rows" />\r
+<xsl:param name = "cols" />\r
+<xsl:param name = "id" />\r
+<xsl:param name = "frozen" />\r
+<xsl:element name="table" use-attribute-sets="ricoTable">\r
+<xsl:attribute name="id"><xsl:value-of select="$id"/></xsl:attribute>\r
+<xsl:attribute name="class">ricoLG_table ricoLG_bottom\r
+<xsl:if test="$frozen">ricoLG_left</xsl:if>\r
+<xsl:if test="not($frozen)">ricoLG_right</xsl:if>\r
+</xsl:attribute> \r
+<xsl:element name="tbody">\r
+  <tr>\r
+  <xsl:for-each select="$cols">\r
+    <xsl:if test="@class='ricoFrozen' and $frozen or not(@class='ricoFrozen') and not($frozen)">\r
+      <xsl:variable name="colpos" select="position()"/>\r
+      <td>\r
+        <div class='ricoLG_col' style='width:100px'>\r
+          <xsl:for-each select="$rows">\r
+            <xsl:element name="div">\r
+            <xsl:attribute name="class">ricoLG_cell <xsl:value-of select="xhtml:td[$colpos]/@class"/></xsl:attribute>\r
+              <xsl:copy-of select="xhtml:td[$colpos]/* | xhtml:td[$colpos]/@*[name()!='class'] | xhtml:td[$colpos]/text()"/>\r
+            </xsl:element>\r
+          </xsl:for-each>\r
+        </div>\r
+      </td>\r
+    </xsl:if>\r
+  </xsl:for-each>\r
+  </tr>\r
+</xsl:element>\r
+</xsl:element>\r
+</xsl:template>\r
+\r
+</xsl:stylesheet> \r
diff --git a/NP_TrackBack/trunk/trackback/js/rico/ricoSimpleGrid2xl.xsl b/NP_TrackBack/trunk/trackback/js/rico/ricoSimpleGrid2xl.xsl
new file mode 100644 (file)
index 0000000..f04b2e1
--- /dev/null
@@ -0,0 +1,160 @@
+<xsl:stylesheet version="1.0"\r
+  xmlns="urn:schemas-microsoft-com:office:spreadsheet"\r
+  xmlns:xhtml="http://www.w3.org/1999/xhtml"\r
+  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" \r
+       xmlns:msxsl="urn:schemas-microsoft-com:xslt"\r
+       xmlns:o="urn:schemas-microsoft-com:office:office"\r
+       xmlns:x="urn:schemas-microsoft-com:office:excel"\r
+  xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">\r
+\r
+<xsl:output method="xml" indent="yes" omit-xml-declaration="no" media-type="application/xml"/>\r
+\r
+<xsl:template match="/">\r
+  <xsl:processing-instruction name="mso-application">\r
+  <xsl:text>progid="Excel.Sheet"</xsl:text> \r
+  </xsl:processing-instruction>\r
+\r
+  <Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"\r
+   xmlns:o="urn:schemas-microsoft-com:office:office"\r
+   xmlns:x="urn:schemas-microsoft-com:office:excel"\r
+   xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"\r
+   xmlns:html="http://www.w3.org/TR/REC-html40">\r
+\r
+ <Styles>\r
+  <Style ss:ID="Default" ss:Name="Normal">\r
+   <Alignment ss:Vertical="Bottom"/>\r
+   <Borders/>\r
+   <Font/>\r
+   <Interior/>\r
+   <NumberFormat/>\r
+   <Protection/>\r
+  </Style>\r
+  <Style ss:ID="s21">\r
+   <Font ss:Bold="1"/>\r
+   <Alignment ss:Horizontal="Center" ss:Vertical="Bottom"/>\r
+  </Style>\r
+  <Style ss:ID="s22">\r
+   <Alignment ss:Horizontal="Left" ss:Vertical="Bottom"/>\r
+   <Font ss:Bold="1"/>\r
+   <Interior ss:Color="#99CCFF" ss:Pattern="Solid"/>\r
+  </Style>\r
+  <Style ss:ID="s23" ss:Name="Currency">\r
+   <NumberFormat\r
+    ss:Format="_(&quot;$&quot;* #,##0.00_);_(&quot;$&quot;* \(#,##0.00\);_(&quot;$&quot;* &quot;-&quot;??_);_(@_)"/>\r
+  </Style>\r
+  <Style ss:ID="s24">\r
+   <NumberFormat ss:Format="_(* #,##0.00_);_(* \(#,##0.00\);_(* &quot;-&quot;??_);_(@_)"/>\r
+  </Style>\r
+  <Style ss:ID="s25">\r
+   <Alignment ss:Horizontal="Center" ss:Vertical="Bottom"/>\r
+  </Style>\r
+ </Styles>\r
+\r
+  <xsl:apply-templates mode="top"/> \r
+\r
+  </Workbook>\r
+</xsl:template>\r
+\r
+\r
+<xsl:template match="*" mode="top">\r
+  <xsl:choose>\r
+\r
+  <xsl:when test="xhtml:table[@class='ricoSimpleGrid']">\r
+  <xsl:apply-templates mode="grid"/> \r
+  </xsl:when>\r
+\r
+  <xsl:otherwise>\r
+  <xsl:apply-templates select="*" mode="top"/> \r
+  </xsl:otherwise>\r
+  \r
+  </xsl:choose>\r
+</xsl:template>\r
+\r
+\r
+<xsl:template match="*" mode="grid">\r
+\r
+  <xsl:choose>\r
+  \r
+  <xsl:when test="xhtml:thead">\r
+  <xsl:call-template name="processTable">\r
+  <xsl:with-param name="id" select="@id"/>\r
+  <xsl:with-param name="headRows" select="xhtml:thead/xhtml:tr"/>\r
+  <xsl:with-param name="bodyRows" select="xhtml:tbody/xhtml:tr"/>\r
+  </xsl:call-template>\r
+  </xsl:when>\r
+  \r
+  <xsl:when test="xhtml:tbody">\r
+  <xsl:call-template name="processTable">\r
+  <xsl:with-param name="id" select="@id"/>\r
+  <xsl:with-param name="headRows" select="xhtml:tbody/xhtml:tr[1]"/>\r
+  <xsl:with-param name="bodyRows" select="xhtml:tbody/xhtml:tr[position() &gt; 1]"/>\r
+  </xsl:call-template>\r
+  </xsl:when>\r
+  \r
+  <xsl:otherwise>\r
+  <xsl:call-template name="processTable">\r
+  <xsl:with-param name="id" select="@id"/>\r
+  <xsl:with-param name="headRows" select="xhtml:tr[1]"/>\r
+  <xsl:with-param name="bodyRows" select="xhtml:tr[position() &gt; 1]"/>\r
+  </xsl:call-template>\r
+  </xsl:otherwise>\r
+  \r
+  </xsl:choose>\r
+\r
+</xsl:template>\r
+\r
+\r
+<!-- Perform the actual table transformation -->\r
+  \r
+<xsl:template name="processTable">\r
+<xsl:param name="id" />\r
+<xsl:param name="headRows" />\r
+<xsl:param name="bodyRows" />\r
+\r
+ <Worksheet>\r
+ <xsl:attribute name="ss:Name">\r
+   <xsl:value-of select='$id'/>\r
+ </xsl:attribute>\r
+  <Table>\r
+\r
+  <xsl:apply-templates select="$headRows" mode="convertHeadRow"/>\r
+  <xsl:apply-templates select="$bodyRows" mode="convertBodyRow"/>\r
+\r
+  </Table>\r
+ </Worksheet>\r
+\r
+</xsl:template>\r
+\r
+\r
+<xsl:template match="*" mode="convertHeadRow">\r
+   <Row>\r
+    <xsl:apply-templates select="xhtml:td | xhtml:th" mode="convertHeadCell"/>\r
+   </Row>\r
+</xsl:template>\r
+\r
+\r
+<xsl:template match="*" mode="convertHeadCell">\r
+  <xsl:element name="Cell">\r
+  <xsl:attribute name="ss:StyleID">s22</xsl:attribute>\r
+  <xsl:if test="@colspan">\r
+  <xsl:attribute name="ss:MergeAcross"><xsl:value-of select="number(@colspan)-1"/></xsl:attribute>\r
+  </xsl:if>\r
+    <Data ss:Type="String">\r
+    <xsl:value-of select="."/>\r
+    </Data>\r
+  </xsl:element>\r
+</xsl:template>\r
+\r
+\r
+<xsl:template match="*" mode="convertBodyRow">\r
+   <Row>\r
+    <xsl:apply-templates select="xhtml:td | xhtml:th" mode="convertBodyCell"/>\r
+   </Row>\r
+</xsl:template>\r
+\r
+\r
+<xsl:template match="*" mode="convertBodyCell">\r
+    <Cell><Data ss:Type="String"><xsl:value-of select="."/></Data></Cell>\r
+</xsl:template>\r
+\r
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/NP_TrackBack/trunk/trackback/js/rico/ricoStyles.js b/NP_TrackBack/trunk/trackback/js/rico/ricoStyles.js
new file mode 100644 (file)
index 0000000..34af1aa
--- /dev/null
@@ -0,0 +1,469 @@
+/**
+  *
+  *  Copyright 2005 Sabre Airline Solutions
+  *
+  *  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.
+  **/
+
+//-------------------- ricoColor.js
+Rico.Color = Class.create();
+
+Rico.Color.prototype = {
+
+   initialize: function(red, green, blue) {
+      this.rgb = { r: red, g : green, b : blue };
+   },
+
+   setRed: function(r) {
+      this.rgb.r = r;
+   },
+
+   setGreen: function(g) {
+      this.rgb.g = g;
+   },
+
+   setBlue: function(b) {
+      this.rgb.b = b;
+   },
+
+   setHue: function(h) {
+
+      // get an HSB model, and set the new hue...
+      var hsb = this.asHSB();
+      hsb.h = h;
+
+      // convert back to RGB...
+      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
+   },
+
+   setSaturation: function(s) {
+      // get an HSB model, and set the new hue...
+      var hsb = this.asHSB();
+      hsb.s = s;
+
+      // convert back to RGB and set values...
+      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
+   },
+
+   setBrightness: function(b) {
+      // get an HSB model, and set the new hue...
+      var hsb = this.asHSB();
+      hsb.b = b;
+
+      // convert back to RGB and set values...
+      this.rgb = Rico.Color.HSBtoRGB( hsb.h, hsb.s, hsb.b );
+   },
+
+   darken: function(percent) {
+      var hsb  = this.asHSB();
+      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.max(hsb.b - percent,0));
+   },
+
+   brighten: function(percent) {
+      var hsb  = this.asHSB();
+      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.min(hsb.b + percent,1));
+   },
+
+   blend: function(other) {
+      this.rgb.r = Math.floor((this.rgb.r + other.rgb.r)/2);
+      this.rgb.g = Math.floor((this.rgb.g + other.rgb.g)/2);
+      this.rgb.b = Math.floor((this.rgb.b + other.rgb.b)/2);
+   },
+
+   isBright: function() {
+      var hsb = this.asHSB();
+      return this.asHSB().b > 0.5;
+   },
+
+   isDark: function() {
+      return ! this.isBright();
+   },
+
+   asRGB: function() {
+      return "rgb(" + this.rgb.r + "," + this.rgb.g + "," + this.rgb.b + ")";
+   },
+
+   asHex: function() {
+      return "#" + this.rgb.r.toColorPart() + this.rgb.g.toColorPart() + this.rgb.b.toColorPart();
+   },
+
+   asHSB: function() {
+      return Rico.Color.RGBtoHSB(this.rgb.r, this.rgb.g, this.rgb.b);
+   },
+
+   toString: function() {
+      return this.asHex();
+   }
+
+};
+
+Rico.Color.createFromHex = function(hexCode) {
+  if(hexCode.length==4) {
+    var shortHexCode = hexCode; 
+    var hexCode = '#';
+    for(var i=1;i<4;i++)
+      hexCode += (shortHexCode.charAt(i) + shortHexCode.charAt(i));
+  }
+  if ( hexCode.indexOf('#') == 0 )
+    hexCode = hexCode.substring(1);
+  var red   = hexCode.substring(0,2);
+  var green = hexCode.substring(2,4);
+  var blue  = hexCode.substring(4,6);
+  return new Rico.Color( parseInt(red,16), parseInt(green,16), parseInt(blue,16) );
+}
+
+/**
+ * Factory method for creating a color from the background of
+ * an HTML element.
+ */
+Rico.Color.createColorFromBackground = function(elem) {
+
+   var actualColor = Element.getStyle(elem, "background-color");
+
+   // if color is tranparent, check parent
+   // Safari returns "rgba(0, 0, 0, 0)", which means transparent
+   if ( actualColor.match(/^(transparent|rgba\(0,\s*0,\s*0,\s*0\))$/i) && elem.parentNode )
+      return Rico.Color.createColorFromBackground(elem.parentNode);
+
+   if ( actualColor == null )
+      return new Rico.Color(255,255,255);
+
+   if ( actualColor.indexOf("rgb(") == 0 ) {
+      var colors = actualColor.substring(4, actualColor.length - 1 );
+      var colorArray = colors.split(",");
+      return new Rico.Color( parseInt( colorArray[0] ),
+                            parseInt( colorArray[1] ),
+                            parseInt( colorArray[2] )  );
+
+   }
+   else if ( actualColor.indexOf("#") == 0 ) {
+      return Rico.Color.createFromHex(actualColor);
+   }
+   else
+      return new Rico.Color(255,255,255);
+}
+
+Rico.Color.HSBtoRGB = function(hue, saturation, brightness) {
+
+   var red   = 0;
+       var green = 0;
+       var blue  = 0;
+
+   if (saturation == 0) {
+      red = parseInt(brightness * 255.0 + 0.5);
+          green = red;
+          blue = red;
+       }
+       else {
+      var h = (hue - Math.floor(hue)) * 6.0;
+      var f = h - Math.floor(h);
+      var p = brightness * (1.0 - saturation);
+      var q = brightness * (1.0 - saturation * f);
+      var t = brightness * (1.0 - (saturation * (1.0 - f)));
+
+      switch (parseInt(h)) {
+         case 0:
+            red   = (brightness * 255.0 + 0.5);
+            green = (t * 255.0 + 0.5);
+            blue  = (p * 255.0 + 0.5);
+            break;
+         case 1:
+            red   = (q * 255.0 + 0.5);
+            green = (brightness * 255.0 + 0.5);
+            blue  = (p * 255.0 + 0.5);
+            break;
+         case 2:
+            red   = (p * 255.0 + 0.5);
+            green = (brightness * 255.0 + 0.5);
+            blue  = (t * 255.0 + 0.5);
+            break;
+         case 3:
+            red   = (p * 255.0 + 0.5);
+            green = (q * 255.0 + 0.5);
+            blue  = (brightness * 255.0 + 0.5);
+            break;
+         case 4:
+            red   = (t * 255.0 + 0.5);
+            green = (p * 255.0 + 0.5);
+            blue  = (brightness * 255.0 + 0.5);
+            break;
+          case 5:
+            red   = (brightness * 255.0 + 0.5);
+            green = (p * 255.0 + 0.5);
+            blue  = (q * 255.0 + 0.5);
+            break;
+           }
+       }
+
+   return { r : parseInt(red), g : parseInt(green) , b : parseInt(blue) };
+}
+
+Rico.Color.RGBtoHSB = function(r, g, b) {
+
+   var hue;
+   var saturation;
+   var brightness;
+
+   var cmax = (r > g) ? r : g;
+   if (b > cmax)
+      cmax = b;
+
+   var cmin = (r < g) ? r : g;
+   if (b < cmin)
+      cmin = b;
+
+   brightness = cmax / 255.0;
+   if (cmax != 0)
+      saturation = (cmax - cmin)/cmax;
+   else
+      saturation = 0;
+
+   if (saturation == 0)
+      hue = 0;
+   else {
+      var redc   = (cmax - r)/(cmax - cmin);
+       var greenc = (cmax - g)/(cmax - cmin);
+       var bluec  = (cmax - b)/(cmax - cmin);
+
+       if (r == cmax)
+          hue = bluec - greenc;
+       else if (g == cmax)
+          hue = 2.0 + redc - bluec;
+      else
+          hue = 4.0 + greenc - redc;
+
+       hue = hue / 6.0;
+       if (hue < 0)
+          hue = hue + 1.0;
+   }
+
+   return { h : hue, s : saturation, b : brightness };
+}
+
+//-------------------- ricoCorner.js
+Rico.Corner = {
+
+   round: function(e, options) {
+      var e = $(e);
+      this._setOptions(options);
+      var color = this.options.color == "fromElement" ? this._background(e) : this.options.color;
+      var bgColor = this.options.bgColor == "fromParent" ? this._background(e.parentNode) : this.options.bgColor;
+      this._roundCornersImpl(e, color, bgColor);
+   },
+
+   _roundCornersImpl: function(e, color, bgColor) {
+      if(this.options.border)
+         this._renderBorder(e,bgColor);
+      if(this._isTopRounded())
+         this._roundTopCorners(e,color,bgColor);
+      if(this._isBottomRounded())
+         this._roundBottomCorners(e,color,bgColor);
+   },
+
+   _renderBorder: function(el,bgColor) {
+      var borderValue = "1px solid " + this._borderColor(bgColor);
+      var borderL = "border-left: "  + borderValue;
+      var borderR = "border-right: " + borderValue;
+      var style   = "style='" + borderL + ";" + borderR +  "'";
+      el.innerHTML = "<div " + style + ">" + el.innerHTML + "</div>"
+   },
+
+   _roundTopCorners: function(el, color, bgColor) {
+      var corner = this._createCorner(bgColor);
+      for(var i=0 ; i < this.options.numSlices ; i++ )
+         corner.appendChild(this._createCornerSlice(color,bgColor,i,"top"));
+      el.style.paddingTop = '0px';
+      el.insertBefore(corner,el.firstChild);
+   },
+
+   _roundBottomCorners: function(el, color, bgColor) {
+      var corner = this._createCorner(bgColor);
+      for(var i=(this.options.numSlices-1) ; i >= 0 ; i-- )
+         corner.appendChild(this._createCornerSlice(color,bgColor,i,"bottom"));
+      el.style.paddingBottom = 0;
+      el.appendChild(corner);
+   },
+
+   _createCorner: function(bgColor) {
+      var corner = document.createElement("div");
+      corner.style.backgroundColor = (this._isTransparent() ? "transparent" : bgColor);
+      return corner;
+   },
+
+   _createCornerSlice: function(color,bgColor, n, position) {
+      var slice = document.createElement("span");
+
+      var inStyle = slice.style;
+      inStyle.backgroundColor = color;
+      inStyle.display  = "block";
+      inStyle.height   = "1px";
+      inStyle.overflow = "hidden";
+      inStyle.fontSize = "1px";
+
+      var borderColor = this._borderColor(color,bgColor);
+      if ( this.options.border && n == 0 ) {
+         inStyle.borderTopStyle    = "solid";
+         inStyle.borderTopWidth    = "1px";
+         inStyle.borderLeftWidth   = "0px";
+         inStyle.borderRightWidth  = "0px";
+         inStyle.borderBottomWidth = "0px";
+         inStyle.height            = "0px"; // assumes css compliant box model
+         inStyle.borderColor       = borderColor;
+      }
+      else if(borderColor) {
+         inStyle.borderColor = borderColor;
+         inStyle.borderStyle = "solid";
+         inStyle.borderWidth = "0px 1px";
+      }
+
+      if ( !this.options.compact && (n == (this.options.numSlices-1)) )
+         inStyle.height = "2px";
+
+      this._setMargin(slice, n, position);
+      this._setBorder(slice, n, position);
+      return slice;
+   },
+
+   _setOptions: function(options) {
+      this.options = {
+         corners : "all",
+         color   : "fromElement",
+         bgColor : "fromParent",
+         blend   : true,
+         border  : false,
+         compact : false
+      }
+      Object.extend(this.options, options || {});
+
+      this.options.numSlices = this.options.compact ? 2 : 4;
+      if ( this._isTransparent() )
+         this.options.blend = false;
+   },
+
+   _whichSideTop: function() {
+      if ( this._hasString(this.options.corners, "all", "top") )
+         return "";
+
+      if ( this.options.corners.indexOf("tl") >= 0 && this.options.corners.indexOf("tr") >= 0 )
+         return "";
+
+      if (this.options.corners.indexOf("tl") >= 0)
+         return "left";
+      else if (this.options.corners.indexOf("tr") >= 0)
+          return "right";
+      return "";
+   },
+
+   _whichSideBottom: function() {
+      if ( this._hasString(this.options.corners, "all", "bottom") )
+         return "";
+
+      if ( this.options.corners.indexOf("bl")>=0 && this.options.corners.indexOf("br")>=0 )
+         return "";
+
+      if(this.options.corners.indexOf("bl") >=0)
+         return "left";
+      else if(this.options.corners.indexOf("br")>=0)
+         return "right";
+      return "";
+   },
+
+   _borderColor : function(color,bgColor) {
+      if ( color == "transparent" )
+         return bgColor;
+      else if ( this.options.border )
+         return this.options.border;
+      else if ( this.options.blend )
+         return this._blend( bgColor, color );
+      else
+         return "";
+   },
+
+
+   _setMargin: function(el, n, corners) {
+      var marginSize = this._marginSize(n);
+      var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();
+
+      if ( whichSide == "left" ) {
+         el.style.marginLeft = marginSize + "px"; el.style.marginRight = "0px";
+      }
+      else if ( whichSide == "right" ) {
+         el.style.marginRight = marginSize + "px"; el.style.marginLeft  = "0px";
+      }
+      else {
+         el.style.marginLeft = marginSize + "px"; el.style.marginRight = marginSize + "px";
+      }
+   },
+
+   _setBorder: function(el,n,corners) {
+      var borderSize = this._borderSize(n);
+      var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();
+      if ( whichSide == "left" ) {
+         el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = "0px";
+      }
+      else if ( whichSide == "right" ) {
+         el.style.borderRightWidth = borderSize + "px"; el.style.borderLeftWidth  = "0px";
+      }
+      else {
+         el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px";
+      }
+      if (this.options.border != false)
+        el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px";
+   },
+
+   _marginSize: function(n) {
+      if ( this._isTransparent() )
+         return 0;
+
+      var marginSizes          = [ 5, 3, 2, 1 ];
+      var blendedMarginSizes   = [ 3, 2, 1, 0 ];
+      var compactMarginSizes   = [ 2, 1 ];
+      var smBlendedMarginSizes = [ 1, 0 ];
+
+      if ( this.options.compact && this.options.blend )
+         return smBlendedMarginSizes[n];
+      else if ( this.options.compact )
+         return compactMarginSizes[n];
+      else if ( this.options.blend )
+         return blendedMarginSizes[n];
+      else
+         return marginSizes[n];
+   },
+
+   _borderSize: function(n) {
+      var transparentBorderSizes = [ 5, 3, 2, 1 ];
+      var blendedBorderSizes     = [ 2, 1, 1, 1 ];
+      var compactBorderSizes     = [ 1, 0 ];
+      var actualBorderSizes      = [ 0, 2, 0, 0 ];
+
+      if ( this.options.compact && (this.options.blend || this._isTransparent()) )
+         return 1;
+      else if ( this.options.compact )
+         return compactBorderSizes[n];
+      else if ( this.options.blend )
+         return blendedBorderSizes[n];
+      else if ( this.options.border )
+         return actualBorderSizes[n];
+      else if ( this._isTransparent() )
+         return transparentBorderSizes[n];
+      return 0;
+   },
+
+   _hasString: function(str) { for(var i=1 ; i<arguments.length ; i++) if (str.indexOf(arguments[i]) >= 0) return true; return false; },
+   _blend: function(c1, c2) { var cc1 = Rico.Color.createFromHex(c1); cc1.blend(Rico.Color.createFromHex(c2)); return cc1; },
+   _background: function(el) { try { return Rico.Color.createColorFromBackground(el).asHex(); } catch(err) { return "#ffffff"; } },
+   _isTransparent: function() { return this.options.color == "transparent"; },
+   _isTopRounded: function() { return this._hasString(this.options.corners, "all", "top", "tl", "tr"); },
+   _isBottomRounded: function() { return this._hasString(this.options.corners, "all", "bottom", "bl", "br"); },
+   _hasSingleTextChild: function(el) { return el.childNodes.length == 1 && el.childNodes[0].nodeType == 3; }
+}
+
+Rico.includeLoaded('ricoStyles.js');
diff --git a/NP_TrackBack/trunk/trackback/js/rico/ricoTree.js b/NP_TrackBack/trunk/trackback/js/rico/ricoTree.js
new file mode 100644 (file)
index 0000000..45f7a95
--- /dev/null
@@ -0,0 +1,318 @@
+//  Rico Tree Control\r
+//  by Matt Brown\r
+//  Oct 2006\r
+//  email: dowdybrown@yahoo.com\r
+\r
+//  Requires prototype.js and ricoCommon.js\r
+\r
+// each node in nodeIndex is an Array with 6+n positions\r
+//  node[0] is 0/1 when the node is closed/open\r
+//  node[1] is 0/1 when the folder is closed/open\r
+//  node[2] is 1 if the node is a leaf node\r
+//  node[3] is the node id\r
+//  node[4] is the node description\r
+//  node[5] is 1 when the node is selectable, 0 otherwise\r
+//  node[6]...node[6+n] are the child nodes\r
+\r
+Rico.TreeControl = Class.create();\r
+\r
+Rico.TreeControl.prototype = {\r
+\r
+  initialize: function(id,url,options) {\r
+    Object.extend(this, new Rico.Popup({ignoreClicks:true}));\r
+    Object.extend(this.options, {\r
+      nodeIdDisplay:'none',   // first, last, tooltip, or none\r
+      showCheckBox: false,\r
+      showFolders: false,\r
+      showPlusMinus: true,\r
+      defaultAction: this.nodeClick.bindAsEventListener(this),\r
+      height: '300px',\r
+      width: '300px',\r
+      leafIcon: Rico.imgDir+'doc.gif'\r
+    });\r
+    Object.extend(this.options, options || {});\r
+    this.img=[];\r
+    this.FirstChildNode=6;\r
+    this.nodeIndex={};\r
+    this.nodeCount=0;\r
+    this.foldersTree=0;\r
+    this.timeOutId=0;\r
+    this.id=id;\r
+    this.dataSource=url;\r
+    this.close=this.closePopup;\r
+  },\r
+\r
+  atLoad : function() {\r
+    var imgsrc = new Array("node.gif","nodelast.gif","folderopen.gif","folderclosed.gif");\r
+    for (i=0;i<imgsrc.length;i++) {\r
+      this.img[i] = new Image\r
+      this.img[i].src = Rico.imgDir+imgsrc[i]\r
+      //this.img[i].src = Rico.imgDir + imgsrc[i]\r
+    }\r
+    this.treeDiv=document.createElement("div");\r
+    this.treeDiv.id=this.id;\r
+    this.treeDiv.className='ricoTree';\r
+    this.treeDiv.style.height=this.options.height;\r
+    this.treeDiv.style.width=this.options.width;\r
+    this.container=document.createElement("div");\r
+    this.container.style.display="none"\r
+    this.container.className='ricoTreeContainer';\r
+    this.container.appendChild(this.treeDiv);\r
+    document.body.appendChild(this.container);\r
+    this.setDiv(this.container);\r
+    this.close();\r
+  },\r
+\r
+  // Building the data in the tree\r
+  open: function() {\r
+    this.openPopup();\r
+    if (this.nodeCount==0) this.loadXMLDoc();\r
+  },\r
+\r
+  loadXMLDoc: function(branchPin) {\r
+    var parms="id="+this.id;\r
+    if (branchPin) parms+="&Parent="+branchPin;\r
+    Rico.writeDebugMsg('Tree loadXMLDoc:\n'+parms+'\n'+this.dataSource);\r
+    new Ajax.Request(this.dataSource, {parameters:parms,method:'get',onComplete:this.processResponse.bind(this)});\r
+  },\r
+\r
+  processResponse: function(request) {\r
+    var response = request.responseXML.getElementsByTagName("ajax-response");\r
+    if (response == null || response.length != 1) return;\r
+    var rowsElement = response[0].getElementsByTagName('rows')[0];\r
+    var trs = rowsElement.getElementsByTagName("tr");\r
+    //alert('processResponse: '+trs.length);\r
+    for ( var i=0 ; i < trs.length; i++ ) {\r
+      var cells = trs[i].getElementsByTagName("td");\r
+      if (cells.length != 5) continue;\r
+      // cells[0]=parent node id\r
+      // cells[1]=node id\r
+      // cells[2]=description\r
+      // cells[3]=L/zero (leaf), C/non-zero (container)\r
+      // cells[4]= 0->not selectable, 1->selectable (use default action), otherwise the node is selectable and cells[4] contains the action\r
+      var content=[];\r
+      for (var j=0; j<cells.length; j++)\r
+        content[j]=this.getContent(cells[j]);\r
+        //content[j]=RicoUtil.getContentAsString(cells[j]);\r
+      var node=this.addNode(content[3],content[1],content[2],content[4]);\r
+      if (this.foldersTree==0) {\r
+        this.foldersTree = node;\r
+        node[0]=1;\r
+        node[1]=1;\r
+      } else {\r
+        var parentNode=this.nodeIndex[content[0]]\r
+        if (typeof parentNode=='undefined')\r
+          alert('ERROR!\nReceived invalid response from server - could not find parent in existing tree:\n'+content[0]);\r
+        else\r
+          parentNode.push(node);\r
+      }\r
+    }\r
+    if (this.nodeCount==1 && node[2])\r
+      this.loadXMLDoc(node[3]);\r
+    else\r
+      this.redrawTree();\r
+  },\r
+\r
+  getContent: function(cell) {\r
+    if (cell.innerHTML) return cell.innerHTML;\r
+    switch (cell.childNodes.length) {\r
+      case 0:  return "";\r
+      case 1:  return cell.firstChild.nodeValue;\r
+      default: return cell.childNodes[1].nodeValue;\r
+    }\r
+  },\r
+\r
+  // create new node\r
+  // NodeType is "C" or non-zero (container), or "L" or zero (leaf)\r
+  // id is the unique identifier for the node\r
+  // desc is the text displayed to the user\r
+  addNode: function(NodeType,id,desc,selectable) {\r
+    var arrayAux\r
+    //alert("addNode: " + desc + " (" + selectable + ")")\r
+    arrayAux = new Array\r
+    arrayAux[0] = 0\r
+    arrayAux[1] = 0\r
+    arrayAux[2] = (NodeType=='0' || NodeType.toUpperCase()=='L' ? 0 : 1)\r
+    arrayAux[3] = id\r
+    arrayAux[4] = desc\r
+    arrayAux[5] = parseInt(selectable);\r
+    this.nodeIndex[id]=arrayAux\r
+    this.nodeCount++;\r
+  \r
+    return arrayAux\r
+  },\r
+\r
+  RemoveAllChildren: function(obj) {\r
+       while (obj.hasChildNodes()) {\r
+               this.RemoveAllChildren(obj.childNodes[0])\r
+               obj.removeChild(obj.childNodes[0])\r
+       }\r
+  },\r
+\r
+  redrawTree: function() {\r
+    //alert('redrawTree');\r
+    this.RemoveAllChildren(this.treeDiv)\r
+    this.redrawNode(this.foldersTree, 0, 1, [])\r
+  },\r
+\r
+  DisplayImages: function(row,arNames) {\r
+    var i,img,td\r
+    for(i=0;i<arNames.length;i++) {\r
+      img = document.createElement("img")\r
+      img.src=Rico.imgDir+arNames[i] + ".gif"\r
+      td=row.insertCell(-1)\r
+      td.appendChild(img)\r
+    }\r
+  },\r
+\r
+  redrawNode: function(foldersNode, level, lastNode, leftSide) {\r
+    var tab,row\r
+    //alert("redrawNode at level " + level + " (" + foldersNode[3] + ")")\r
+    \r
+    tab = document.createElement("table")\r
+    tab.border=0\r
+    tab.cellSpacing=0\r
+    tab.cellPadding=0\r
+    row=tab.insertRow(0)\r
+    this.DisplayImages(row,leftSide)\r
+    var newLeft=leftSide.slice(0);\r
+    if (level>0) {\r
+      var suffix=lastNode ? 'last' : '';\r
+      if (this.options.showPlusMinus && foldersNode[2])\r
+        this.showPlusMinus(row.insertCell(-1),foldersNode,suffix);\r
+      else\r
+        this.NodeImage(row.insertCell(-1),suffix)\r
+      newLeft.push(lastNode ? "nodeblank" : "nodeline")\r
+    }\r
+    if (this.options.showFolders)\r
+      this.showFolders(row.insertCell(-1),foldersNode);\r
+    if (this.options.showCheckBox && foldersNode[5])\r
+      this.showCheckBox(row.insertCell(-1),foldersNode);\r
+    this.displayLabel(row,foldersNode)\r
+    this.treeDiv.appendChild(tab)\r
+  \r
+    if (foldersNode.length > this.FirstChildNode && foldersNode[0]) {\r
+      //there are sub-nodes and the folder is open\r
+      for (var i=this.FirstChildNode; i<foldersNode.length;i++)\r
+        this.redrawNode(foldersNode[i], level+1, (i==foldersNode.length-1 ? 1 : 0), newLeft)\r
+    }\r
+  },\r
+\r
+  NodeImage: function(td, suffix) {\r
+    var img\r
+    img = document.createElement("img")\r
+    img.src=Rico.imgDir+"node"+suffix+".gif"\r
+    td.appendChild(img)\r
+  },\r
+\r
+\r
+  showPlusMinus: function(td,foldersNode,suffix) {\r
+    var img = document.createElement("img")\r
+    img.name=foldersNode[3];\r
+    img.style.cursor='pointer';\r
+    if (foldersNode.length > this.FirstChildNode)\r
+      img.onclick=this.openBranch.bindAsEventListener(this);\r
+    else\r
+      img.onclick=this.getChildren.bindAsEventListener(this);\r
+    var prefix=foldersNode[1] ? "nodem" : "nodep"\r
+    img.src=Rico.imgDir+prefix+suffix+".gif";\r
+    td.appendChild(img)\r
+  },\r
+\r
+  showFolders: function(td,foldersNode) {\r
+    var img = document.createElement("img")\r
+    if (!foldersNode[2]) {\r
+      img.src=this.options.leafIcon;\r
+    } else {\r
+      img.name=foldersNode[3];\r
+      img.style.cursor='pointer';\r
+      if (foldersNode.length > this.FirstChildNode)\r
+        img.onclick=this.openBranch.bindAsEventListener(this);\r
+      else\r
+        img.onclick=this.getChildren.bindAsEventListener(this);\r
+      img.src=Rico.imgDir+(foldersNode[1] ? "folderopen.gif" : "folderclosed.gif");\r
+    }\r
+    td.appendChild(img)\r
+  },\r
+\r
+  showCheckBox: function(td,foldersNode) {\r
+    var inp=document.createElement("input")\r
+    inp.type="checkbox"\r
+    inp.name=foldersNode[3]\r
+    td.appendChild(inp)\r
+  },\r
+\r
+  displayLabel: function(row,foldersNode) {\r
+    if (foldersNode[5]) {\r
+      var span=document.createElement('a');\r
+      span.href='#';\r
+      span.onclick=this.options.defaultAction;\r
+    } else {\r
+      var span=document.createElement('p');\r
+    }\r
+    span.id=this.id+"__"+foldersNode[3];\r
+    var desc=foldersNode[4];\r
+    switch (this.options.nodeIdDisplay) {\r
+      case 'last': desc+=' ('+foldersNode[3]+')'; break;\r
+      case 'first': desc=foldersNode[3]+' - '+desc; break;\r
+      case 'tooltip': span.title=foldersNode[3]; break;\r
+    }\r
+       span.appendChild(document.createTextNode(desc))\r
+    var td=row.insertCell(-1)\r
+    td.appendChild(span)\r
+  },\r
+\r
+  //when a parent is closed all children also are\r
+  closeFolders: function(foldersNode) {\r
+    var i=0\r
+    if (foldersNode[2]) {\r
+      for (i=this.FirstChildNode; i< foldersNode.length; i++)\r
+        this.closeFolders(foldersNode[i])\r
+    }\r
+    foldersNode[0] = 0\r
+    foldersNode[1] = 0\r
+  },\r
+  \r
+  nodeClick: function(e) {\r
+    var node=Event.element(e);\r
+    if (this.returnValue) {\r
+      var v=node.id;\r
+      var i=v.indexOf('__');\r
+      if (i>=0) v=v.substr(i+2);\r
+      this.returnValue(v,node.innerHTML);\r
+    }\r
+    this.close();\r
+  },\r
+\r
+  //recurse over the tree structure\r
+  //called by openbranch\r
+  clickOnFolderRec: function(foldersNode, folderName) {\r
+    var i=0\r
+    if (foldersNode[3] == folderName) {\r
+      if (foldersNode[0]) {\r
+        this.closeFolders(foldersNode)\r
+      } else {\r
+        foldersNode[0] = 1\r
+        foldersNode[1] = 1\r
+      }\r
+    } else if (foldersNode[2]) {\r
+      for (i=this.FirstChildNode; i< foldersNode.length; i++)\r
+        this.clickOnFolderRec(foldersNode[i], folderName)\r
+    }\r
+  },\r
+\r
+  openBranch: function(e) {\r
+    var node=Event.element(e);\r
+    this.clickOnFolderRec(this.foldersTree, node.name)\r
+    this.timeOutId = setTimeout(this.redrawTree.bind(this),100)\r
+  },\r
+\r
+  getChildren: function(e) {\r
+    var node=Event.element(e);\r
+    this.loadXMLDoc(node.name)\r
+    this.openBranch(e)\r
+  }\r
+\r
+}\r
+\r
+Rico.includeLoaded('ricoTree.js');
diff --git a/NP_TrackBack/trunk/trackback/js/rico/translations/livegrid_de.js b/NP_TrackBack/trunk/trackback/js/rico/translations/livegrid_de.js
new file mode 100644 (file)
index 0000000..530820a
--- /dev/null
@@ -0,0 +1,86 @@
+/*****************************************************************\r
+ Page : livegrid_DE.js\r
+ Description : LiveGrid text for German menus\r
+ Translator: rainer@langheiter@.com\r
+ Version 0.1 - please send corrections to dowdybrown@yahoo.com\r
+******************************************************************/\r
+// 2007-02-09, made some improvements - debach@gmx.de\r
+\r
+RicoTranslate.addPhrase("Sort by","Sortiert nach")\r
+RicoTranslate.addPhrase("Filter by","Gefiltert nach")\r
+RicoTranslate.addPhrase("Hide","Verbergen")\r
+RicoTranslate.addPhrase("Show","Zeige")\r
+RicoTranslate.addPhrase("Show All","Alle zeigen")\r
+RicoTranslate.addPhrase("Ascending","Aufsteigend")\r
+RicoTranslate.addPhrase("Descending","Absteigend")\r
+\r
+RicoTranslate.addPhrase("Include only this value","Nur diesen Wert einbeziehen")\r
+RicoTranslate.addPhrase("Exclude this value","Diesen Wert ausschließen")\r
+RicoTranslate.addPhrase("Exclude this value also","Diesen Wert ebenfalls ausschließen")\r
+RicoTranslate.addPhrase("Greater than or equal to this value","Größer oder gleich diesem Wert")\r
+RicoTranslate.addPhrase("Less than or equal to this value","Kleiner oder gleich diesem wert")\r
+RicoTranslate.addPhrase("Contains keyword","Enthält Schlüsselwort")\r
+RicoTranslate.addPhrase("Change keyword","Schlüsselwort ändern ")\r
+RicoTranslate.addPhrase("Enter keyword to search for","Schlüsselwort eintragen, nach dem gesucht werden soll")\r
+RicoTranslate.addPhrase("use * as a wildcard","verwende * als Wildcard")\r
+RicoTranslate.addPhrase("Remove filter","Entferne Filter")\r
+\r
+RicoTranslate.addPhrase("Waiting for data","Warte auf Daten")\r
+RicoTranslate.addPhrase("Request for data timed out","Zeitüberschreitung")\r
+\r
+RicoTranslate.addPhrase("No matching records","Keine übereinstimmenden Einträge")\r
+RicoTranslate.addPhrase("Listing records","Zeige Einträge")\r
+RicoTranslate.addPhrase("of","von")\r
+RicoTranslate.addPhrase("of about","von ungefähr")\r
+\r
+RicoTranslate.monthNames=["Januar", "Februar", "März", "April", "Mai","Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"]\r
+RicoTranslate.dayNames=["Sonntag", "Montag","Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"]\r
+\r
+RicoTranslate.thouSep="."\r
+RicoTranslate.decPoint=","\r
+RicoTranslate.dateFmt="dd.mm.yyyy"\r
+\r
+// added for 22 August release\r
+\r
+RicoTranslate.addPhrase("Print","Druck")\r
+RicoTranslate.addPhrase("Export","Exportiert")\r
+RicoTranslate.addPhrase("Visible rows","Sichtbare Zeilen")\r
+RicoTranslate.addPhrase("All rows","Alle Zeilen")\r
+\r
+// added for Feb 2007 release\r
+\r
+RicoTranslate.addPhrase("Loading","Lade")\r
+RicoTranslate.addPhrase("minutes before your session expires","Minuten, bevor Ihre Sitzung abläuft") // formal\r
+//RicoTranslate.addPhrase("minutes before your session expires","Minuten, bevor deine Sitzung abläuft") // informal\r
+//RicoTranslate.addPhrase("minutes before your session expires","Minuten, bevor die Sitzung abläuft") // indirect\r
+RicoTranslate.addPhrase("EXPIRED","ABGELAUFEN")\r
+RicoTranslate.addPhrase("Close","Schließen")\r
+RicoTranslate.addPhrase("Cancel","Abbrechen")\r
+RicoTranslate.addPhrase("Save","Speichere")\r
+RicoTranslate.addPhrase("Add","Erstelle")\r
+RicoTranslate.addPhrase("Edit","Bearbeite")\r
+RicoTranslate.addPhrase("Delete","Lösche")\r
+RicoTranslate.addPhrase("Are you sure you want to delete","Möchten Sie dies wirklich löschen:") // formal\r
+//RicoTranslate.addPhrase("Are you sure you want to delete","Möchtest du das wirklich löschen:") // informal\r
+//RicoTranslate.addPhrase("Are you sure you want to delete","Wirklich löschen:") // indirect\r
+RicoTranslate.addPhrase("record","Eintrag")\r
+RicoTranslate.addPhrase("this record","diesen Eintrag") // "dieser"?\r
+RicoTranslate.addPhrase("new record","neuen Eintrag") // "neuer"?\r
+RicoTranslate.addPhrase("Please enter","Bitte eingeben")\r
+RicoTranslate.addPhrase("a value for","einen Wert für")\r
+RicoTranslate.addPhrase("an integer value for","einen ganzzahligen Wert für")\r
+RicoTranslate.addPhrase("a positive integer value for","einen positiven ganzzahligen Wert für")\r
+RicoTranslate.addPhrase("The request returned an error","Die Anfrage ergab einen Fehler")\r
+\r
+// for demo only:\r
+\r
+RicoTranslate.addPhrase("Show orders for","Zeige Bestellungen für")\r
+RicoTranslate.addPhrase("Show order detail","Zeige Bestelldetails")\r
+RicoTranslate.addPhrase("order","Bestellung")\r
+RicoTranslate.addPhrase("this order","diese Bestellung")\r
+RicoTranslate.addPhrase("new order","neue Bestellung")\r
+\r
+// add your own here:\r
+\r
+RicoTranslate.addPhrase("Visible rows to web page","Sichtbare Zeilen auf eine Webseite")\r
+RicoTranslate.addPhrase("All rows to web page","Alle Zeilen auf eine Webseite")
\ No newline at end of file
diff --git a/NP_TrackBack/trunk/trackback/js/rico/translations/livegrid_es.js b/NP_TrackBack/trunk/trackback/js/rico/translations/livegrid_es.js
new file mode 100644 (file)
index 0000000..dc5b8f6
--- /dev/null
@@ -0,0 +1,79 @@
+/*****************************************************************\r
+ Page : livegrid_es.js\r
+ Description : LiveGrid text for Spanish menus\r
+ Version 0.2 (by Marco Scarnatto)\r
+ If you have better translations, or would like to include\r
+ translations for another language, please send them to dowdybrown@yahoo.com\r
+******************************************************************/\r
+\r
+RicoTranslate.addPhrase("Sort by","Ordenar por")\r
+RicoTranslate.addPhrase("Filter by","Filtrar por")\r
+RicoTranslate.addPhrase("Hide","Ocultar")\r
+RicoTranslate.addPhrase("Show","Mostrar")\r
+RicoTranslate.addPhrase("Show All","Mostrar todo")\r
+RicoTranslate.addPhrase("Ascending","Ascendente")\r
+RicoTranslate.addPhrase("Descending","Descendente")\r
+\r
+RicoTranslate.addPhrase("Include only this value","Incluir solo este valor")\r
+RicoTranslate.addPhrase("Exclude this value","Excluir este valor")\r
+RicoTranslate.addPhrase("Exclude this value also","Excluir este valor también")\r
+RicoTranslate.addPhrase("Greater than or equal to this value","Mayor o igual a este valor")\r
+RicoTranslate.addPhrase("Less than or equal to this value","Menor o igual a este valor")\r
+RicoTranslate.addPhrase("Contains keyword","Contiene el texto")\r
+RicoTranslate.addPhrase("Change keyword","Cambiar texto")\r
+RicoTranslate.addPhrase("Enter keyword to search for","Ingrese texto a buscar")\r
+RicoTranslate.addPhrase("use * as a wildcard","use * como un comodín")\r
+RicoTranslate.addPhrase("Remove filter","Quitar filtro")\r
+\r
+RicoTranslate.addPhrase("Waiting for data","Esperando datos")\r
+RicoTranslate.addPhrase("Request for data timed out","Tiempo excedido en recibir datos ")\r
+\r
+RicoTranslate.addPhrase("No matching records","No hay datos coincidentes")\r
+RicoTranslate.addPhrase("Listing records","Mostrando datos")\r
+RicoTranslate.addPhrase("of","de")\r
+RicoTranslate.addPhrase("of about","de alrededor de")\r
+\r
+RicoTranslate.thouSep=","\r
+RicoTranslate.decPoint="."\r
+RicoTranslate.dateFmt="dd/mm/yyyy"\r
+\r
+RicoTranslate.monthNames=['Enero','Febrero', 'Marzo', 'Abril', 'Mayo','Junio', 'Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre']\r
+RicoTranslate.dayNames=['Domingo','Lunes','Martes','Miércoles','Jueves','Viernes','Sábado']\r
+\r
+// added for Aug 2006 release\r
+\r
+RicoTranslate.addPhrase("Print","Imprimir")\r
+RicoTranslate.addPhrase("Export","Exportar")\r
+RicoTranslate.addPhrase("Visible rows","Filas visibles")\r
+RicoTranslate.addPhrase("All rows","Todas las filas")\r
+\r
+// added for Feb 2007 release
+
+RicoTranslate.addPhrase("Loading","Cargar")
+RicoTranslate.addPhrase("minutes before your session expires","minutos antes de su sesión expiran")
+RicoTranslate.addPhrase("EXPIRED","EXPIRADO")\r
+RicoTranslate.addPhrase("Close","Cerrar")
+RicoTranslate.addPhrase("Cancel","Cancelar")
+RicoTranslate.addPhrase("Save","Guardar")
+RicoTranslate.addPhrase("Add","Añadir")
+RicoTranslate.addPhrase("Edit","Editar")
+RicoTranslate.addPhrase("Delete","Borrar")
+RicoTranslate.addPhrase("Are you sure you want to delete","¿Está seguro de que quiere borrar")\r
+RicoTranslate.addPhrase("record","expediente")
+RicoTranslate.addPhrase("this record","este expediente")
+RicoTranslate.addPhrase("new record","expediente nuevo")
+RicoTranslate.addPhrase("Please enter","Introduzca")
+RicoTranslate.addPhrase("a value for","un valor para")
+RicoTranslate.addPhrase("an integer value for","un valor del número entero para")
+RicoTranslate.addPhrase("a positive integer value for","un valor positivo del número entero para")
+RicoTranslate.addPhrase("The request returned an error","Un error ocurrió mientras que recibía datos")
+
+// for demo only:\r
+\r
+RicoTranslate.addPhrase("Show orders for","Mostrar las ordenes de")\r
+RicoTranslate.addPhrase("Show order detail","Mostrar detalle de la orden")\r
+RicoTranslate.addPhrase("order","la orden")\r
+RicoTranslate.addPhrase("this order","esta orden")\r
+RicoTranslate.addPhrase("new order","nueva orden")\r
+\r
+// add your own here:\r
diff --git a/NP_TrackBack/trunk/trackback/js/rico/translations/livegrid_fr.js b/NP_TrackBack/trunk/trackback/js/rico/translations/livegrid_fr.js
new file mode 100644 (file)
index 0000000..7f92442
--- /dev/null
@@ -0,0 +1,79 @@
+/*****************************************************************
+ Page : livegrid_FR.js
+ Description : LiveGrid text for French menus
+ Version 0.3 (revisions by Pierre Grellet)
+ If you have better translations, or would like to include
+ translations for another language, please send them to dowdybrown@yahoo.com
+******************************************************************/
+
+RicoTranslate.addPhrase("Sort by","Trier par")
+RicoTranslate.addPhrase("Filter by","Filtrer par")
+RicoTranslate.addPhrase("Hide","Masquer")
+RicoTranslate.addPhrase("Show","Afficher")
+RicoTranslate.addPhrase("Show All","Afficher tous")
+RicoTranslate.addPhrase("Ascending","Croissant")
+RicoTranslate.addPhrase("Descending","Décroissant")
+
+RicoTranslate.addPhrase("Include only this value","Inclure seulement cette valeur")
+RicoTranslate.addPhrase("Exclude this value","Exclure cette valeur aussi")
+RicoTranslate.addPhrase("Exclude this value also","Exclure cette valeur")
+RicoTranslate.addPhrase("Greater than or equal to this value","Supérieur ou égal à cette valeur")
+RicoTranslate.addPhrase("Less than or equal to this value","Inférieur ou égal à cette valeur")
+RicoTranslate.addPhrase("Contains keyword","Contenant le mot-clé")
+RicoTranslate.addPhrase("Change keyword","Changer le mot-clé")
+RicoTranslate.addPhrase("Enter keyword to search for","Écrivez le mot-clé à rechercher")
+RicoTranslate.addPhrase("use * as a wildcard","utiliser * comme caractère générique")
+RicoTranslate.addPhrase("Remove filter","Enlever le filtre")
+
+RicoTranslate.addPhrase("Waiting for data","En attente des données")
+RicoTranslate.addPhrase("Request for data timed out","La requête a atteint sa limite de temps ")
+
+RicoTranslate.addPhrase("No matching records","Aucun articles ne correspond")
+RicoTranslate.addPhrase("Listing records","Résultats")
+RicoTranslate.addPhrase("of","de")
+RicoTranslate.addPhrase("of about","sur un total d'environ")
+
+RicoTranslate.thouSep="'"
+RicoTranslate.decPoint="."
+RicoTranslate.dateFmt="dd.mm.yyyy"
+
+RicoTranslate.monthNames=['Janvier','Février', 'Mars', 'Avril', 'Mai','Juin', 'Juillet','Août','Septembre','Octobre','Novembre','Décembre']
+RicoTranslate.dayNames=['Dimanche','Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi']
+
+// added for 22 August release
+
+RicoTranslate.addPhrase("Print","Imprimer")
+RicoTranslate.addPhrase("Export","Exporter")
+RicoTranslate.addPhrase("Visible rows","Rangs visible")
+RicoTranslate.addPhrase("All rows","Tous les rangs")
+
+// added for Feb 2007 release
+
+RicoTranslate.addPhrase("Loading","Chargement")
+RicoTranslate.addPhrase("minutes before your session expires","minutes avant votre session expire")
+RicoTranslate.addPhrase("EXPIRED","EXPIRÉ")
+RicoTranslate.addPhrase("Close","Fermer")
+RicoTranslate.addPhrase("Cancel","Annuler")
+RicoTranslate.addPhrase("Save","Sauvegarder")
+RicoTranslate.addPhrase("Add","Ajouter")
+RicoTranslate.addPhrase("Edit","Éditer")
+RicoTranslate.addPhrase("Delete","Supprimer")
+RicoTranslate.addPhrase("Are you sure you want to delete","Êtes-vous sûr de vouloir supprimer")
+RicoTranslate.addPhrase("record","enregistrement")
+RicoTranslate.addPhrase("this record","cet enregistrement")
+RicoTranslate.addPhrase("new record","nouvel enregistrement")
+RicoTranslate.addPhrase("Please enter","Veuillez saisir")
+RicoTranslate.addPhrase("a value for","une valeur pour")
+RicoTranslate.addPhrase("an integer value for","un nombre entier pour")
+RicoTranslate.addPhrase("a positive integer value for","un nombre positif pour")
+RicoTranslate.addPhrase("The request returned an error","La requête a renvoyé une erreur")
+
+// for demo only:
+
+RicoTranslate.addPhrase("Show orders for","Montrer les commandes pour")
+RicoTranslate.addPhrase("Show order detail","Montrer les détails de la commande")
+RicoTranslate.addPhrase("order","la commande")\r
+RicoTranslate.addPhrase("this order","cette commande")\r
+RicoTranslate.addPhrase("new order","nouvelle commande")\r
+
+// add your own here:
diff --git a/NP_TrackBack/trunk/trackback/js/rico/translations/livegrid_it.js b/NP_TrackBack/trunk/trackback/js/rico/translations/livegrid_it.js
new file mode 100644 (file)
index 0000000..efc7124
--- /dev/null
@@ -0,0 +1,80 @@
+/*****************************************************************
+ Page : livegrid_IT.js
+ Description : LiveGrid text for Italian menus
+ Version 0.1 (these are guesses based mostly on babelfish results)
+ If you have better translations, or would like to include
+ translations for another language, please send them to dowdybrown@yahoo.com
+******************************************************************/
+
+RicoTranslate.addPhrase("Sort by","Ordina per")
+RicoTranslate.addPhrase("Filter by","Filtra per")
+RicoTranslate.addPhrase("Hide","Nascondi")
+RicoTranslate.addPhrase("Show","Mostra")
+RicoTranslate.addPhrase("Show All","Mostra Tutte")
+RicoTranslate.addPhrase("Ascending","Crescente")
+RicoTranslate.addPhrase("Descending","Decrescente")
+
+RicoTranslate.addPhrase("Include only this value","Ritieni soltanto questo valore")
+RicoTranslate.addPhrase("Exclude this value","Escludi questo valore")
+RicoTranslate.addPhrase("Exclude this value also","Escludi questo valore anche")
+RicoTranslate.addPhrase("Greater than or equal to this value","Superiore o uguale a questo valore")
+RicoTranslate.addPhrase("Less than or equal to this value","Inferiore o uguale a questo valore")
+RicoTranslate.addPhrase("Contains keyword","Contiene la parola chiave")
+RicoTranslate.addPhrase("Change keyword","Cambia la parola chiave")
+RicoTranslate.addPhrase("Enter keyword to search for","Entra la parola chiave da cercare")
+RicoTranslate.addPhrase("use * as a wildcard","usa * come \"jolly\"")
+RicoTranslate.addPhrase("Remove filter","Rimuovi filtro")
+
+RicoTranslate.addPhrase("Waiting for data","Attendere per i dati")
+RicoTranslate.addPhrase("Request for data timed out","La richiesta dei dati ha raggiunto il limite di tempo")
+
+RicoTranslate.addPhrase("No matching records","Nessun articoli corrispondenti")
+RicoTranslate.addPhrase("Listing records","Risultati")
+RicoTranslate.addPhrase("of","di")
+RicoTranslate.addPhrase("of about","di circa")
+
+RicoTranslate.monthNames=["Gennaio", "Febbraio", "Marzo", "Aprile", "Maggio", "Giugno", "Luglio", "Agosto", "Settembre", "Ottobre", "Novembre", "Dicembre"]
+RicoTranslate.dayNames=["Domenica", "Lunedì", "Martedì", "Mercoledì", "Giovedì", "Venerdì", "Sabato"]
+
+RicoTranslate.thouSep="."
+RicoTranslate.decPoint=","
+RicoTranslate.dateFmt="dd/mm/yyyy"
+
+// added for Feb 2007 release
+
+RicoTranslate.addPhrase("Loading","Caricamento")
+RicoTranslate.addPhrase("minutes before your session expires","minuti prima che la vostra sessione termini")
+RicoTranslate.addPhrase("EXPIRED","TERMINATA")
+RicoTranslate.addPhrase("Close","Chiudi")
+RicoTranslate.addPhrase("Cancel","Annulla")
+RicoTranslate.addPhrase("Save","Salva")
+RicoTranslate.addPhrase("Add","Aggiungi")
+RicoTranslate.addPhrase("Edit","Modifica")
+RicoTranslate.addPhrase("Delete","Cancella")
+RicoTranslate.addPhrase("Are you sure you want to delete","Siete sicuri che volete cancellare")
+RicoTranslate.addPhrase("record","registrazione")
+RicoTranslate.addPhrase("this record","qesta registrazione")
+RicoTranslate.addPhrase("new record","nuova registrazione")
+RicoTranslate.addPhrase("Please enter","Per favore inserire")
+RicoTranslate.addPhrase("a value for","un valore per")
+RicoTranslate.addPhrase("an integer value for","un numero intero per")
+RicoTranslate.addPhrase("a positive integer value for","un numero intero positivo per")
+RicoTranslate.addPhrase("The request returned an error","La richiesta ha generato un errore")
+
+// for demo only:
+
+RicoTranslate.addPhrase("Show orders for","Mostra gli ordini per")
+RicoTranslate.addPhrase("Show order detail","Mostra i dettagli dell'ordine")
+RicoTranslate.addPhrase("order","ordine")\r
+RicoTranslate.addPhrase("this order","questo ordine")\r
+RicoTranslate.addPhrase("new order","nuovo ordine")\r
+
+// added for 22 August release
+
+RicoTranslate.addPhrase("Print","Stampa")
+RicoTranslate.addPhrase("Export","Esporta")
+RicoTranslate.addPhrase("Visible rows","Righe visibili")
+RicoTranslate.addPhrase("All rows","Tutte le righe")
+
+// add your own here:
+
diff --git a/NP_TrackBack/trunk/trackback/js/rico/translations/livegrid_ja.js b/NP_TrackBack/trunk/trackback/js/rico/translations/livegrid_ja.js
new file mode 100644 (file)
index 0000000..479f9e2
--- /dev/null
@@ -0,0 +1,49 @@
+/*****************************************************************\r
+ Page : livegrid_ja.js\r
+ Description : LiveGrid text for Japanese menus\r
+ Translator: hsur\r
+******************************************************************/\r
+\r
+RicoTranslate.addPhrase("Sort by","ソート")\r
+RicoTranslate.addPhrase("Filter by","絞込み")\r
+RicoTranslate.addPhrase("Hide","隠す")\r
+RicoTranslate.addPhrase("Show","表示")\r
+RicoTranslate.addPhrase("Ascending","昇順")\r
+RicoTranslate.addPhrase("Descending","降順")\r
+\r
+RicoTranslate.addPhrase("Include only this value","この値を含む")\r
+RicoTranslate.addPhrase("Exclude this value","この値を除く")\r
+RicoTranslate.addPhrase("Greater than or equal to this value","これ以上の値")\r
+RicoTranslate.addPhrase("Less than or equal to this value","これ以下の値")\r
+RicoTranslate.addPhrase("Contains keyword","このキーワードを含む")\r
+RicoTranslate.addPhrase("Change keyword","キーワードを変更")\r
+RicoTranslate.addPhrase("Enter keyword to search for","キーワードを入力してください")\r
+RicoTranslate.addPhrase("use * as a wildcard","ワイルドカードとして*を使います")\r
+RicoTranslate.addPhrase("also","も")\r
+RicoTranslate.addPhrase("Remove filter","絞込みを削除")\r
+\r
+RicoTranslate.addPhrase("Waiting for data","データ取得中")\r
+RicoTranslate.addPhrase("Request for data timed out","タイムアウトしました")\r
+\r
+RicoTranslate.addPhrase("No matching records","一致するレコードはありません")\r
+RicoTranslate.addPhrase("Listing records","")\r
+RicoTranslate.addPhrase("of","of")\r
+RicoTranslate.addPhrase("of about","of about")\r
+\r
+RicoTranslate.monthNames=["1月", "2月", "3月", "4月", "5月","6月", "7月", "8月", "9月", "10月", "11月", "12月"]\r
+RicoTranslate.dayNames=["月","火", "水","木", "金", "土", "日"]\r
+\r
+RicoTranslate.thouSep="."\r
+RicoTranslate.decPoint=","\r
+RicoTranslate.dateFmt="yyyy/mm/dd"\r
+RicoTranslate.timeFmt="HH:nn:ss"\r
+\r
+// added for 22 August release\r
+\r
+RicoTranslate.addPhrase("Print","印刷")\r
+RicoTranslate.addPhrase("Export","抽出")\r
+RicoTranslate.addPhrase("Visible rows","Visible rows")\r
+RicoTranslate.addPhrase("All rows","全ての行")\r
+\r
+// add your own here:\r
+\r
diff --git a/NP_TrackBack/trunk/trackback/js/rico/translations/livegrid_pt.js b/NP_TrackBack/trunk/trackback/js/rico/translations/livegrid_pt.js
new file mode 100644 (file)
index 0000000..bdaa9d2
--- /dev/null
@@ -0,0 +1,55 @@
+/*****************************************************************\r
+ Page : livegrid_pt.js (brazilian)\r
+ Description : LiveGrid text for Brazilian Portuguese menus\r
+ Version 0.1 (by Adriano Accorsi - adriano@token.com.br)\r
+ If you have better translations, or would like to include\r
+ translations for another language, please send them to dowdybrown@yahoo.com\r
+******************************************************************/\r
\r
+RicoTranslate.addPhrase("Sort by","Ordenar por")\r
+RicoTranslate.addPhrase("Filter by","Filtrar por")\r
+RicoTranslate.addPhrase("Hide","Ocultar")\r
+RicoTranslate.addPhrase("Show","Exibir")\r
+RicoTranslate.addPhrase("Show All","Exibir tudo")\r
+RicoTranslate.addPhrase("Ascending","Ascendente")\r
+RicoTranslate.addPhrase("Descending","Descendente")\r
\r
+RicoTranslate.addPhrase("Include only this value","Incluir apenas este valor")\r
+RicoTranslate.addPhrase("Exclude this value","Excluir este valor")\r
+RicoTranslate.addPhrase("Greater than or equal to this value","Maior ou igual a este valor")\r
+RicoTranslate.addPhrase("Less than or equal to this value","Menor ou igual a este valor")\r
+RicoTranslate.addPhrase("Contains keyword","Contém texto")\r
+RicoTranslate.addPhrase("Change keyword","Alterar texto")\r
+RicoTranslate.addPhrase("Enter keyword to search for","Informe texto a ser pesquisado")\r
+RicoTranslate.addPhrase("use * as a wildcard","use * como 'coringa'")\r
+RicoTranslate.addPhrase("also","também")\r
+RicoTranslate.addPhrase("Remove filter","Remover filtro")\r
\r
+RicoTranslate.addPhrase("Waiting for data","Aguardando dados")\r
+RicoTranslate.addPhrase("Request for data timed out","Tempo esgotado para espera de dados")\r
\r
+RicoTranslate.addPhrase("No matching records","Nenhum registro encontrado.")\r
+RicoTranslate.addPhrase("Listing records","Listar registros")\r
+RicoTranslate.addPhrase("of","de")\r
+RicoTranslate.addPhrase("of about","de aproximadamente")\r
\r
+RicoTranslate.thouSep="."\r
+RicoTranslate.decPoint=","\r
+RicoTranslate.dateFmt="dd/mm/yyyy"\r
\r
+RicoTranslate.monthNames=["Janeiro", "Fevereiro", "Março", "Abril", "Maio","Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"]\r
+RicoTranslate.dayNames=["Domingo", "Segunda","Terça", "Quarta", "Quinta", "Sexta", "Sábado"]\r
\r
+// added for 22 August release\r
+\r
+RicoTranslate.addPhrase("Print","Imprimir")\r
+RicoTranslate.addPhrase("Export","Exportar")\r
+RicoTranslate.addPhrase("Visible rows","Fileiras visíveis")\r
+RicoTranslate.addPhrase("All rows","Todas as fileiras")\r
+\r
+// for demo only:\r
\r
+RicoTranslate.addPhrase("Show orders for","Exibir pedidos de")\r
+RicoTranslate.addPhrase("Show order detail","Exibir detalhes do pedido")\r
\r
+// add your own here:\r
diff --git a/NP_TrackBack/trunk/trackback/js/rico/translations/livegrid_ru.js b/NP_TrackBack/trunk/trackback/js/rico/translations/livegrid_ru.js
new file mode 100644 (file)
index 0000000..c4ceea7
--- /dev/null
@@ -0,0 +1,82 @@
+/*****************************************************************\r
+ Page : livegrid_ru.js\r
+ Description : LiveGrid text for Russian menus\r
+ Version 0.2 (by Illiya Gannitskiy,Alexey Uvarov)\r
+ If you have better translations, or would like to include\r
+ translations for another language, please send them to dowdybrown@yahoo.com\r
+******************************************************************/\r
+\r
+RicoTranslate.addPhrase("Sort by","Ñîðòèðîâêà ïî")\r
+RicoTranslate.addPhrase("Filter by","Ôèëüòðàöèÿ ïî")\r
+RicoTranslate.addPhrase("Hide","Ñïðÿòàòü")\r
+RicoTranslate.addPhrase("Show","Ïîêàçàòü")\r
+RicoTranslate.addPhrase("Show All","Ïîêàçàòü âñå")\r
+RicoTranslate.addPhrase("Ascending","Âîçðàñòàþùàÿ")\r
+RicoTranslate.addPhrase("Descending","Óáûâàþùàÿ")\r
+\r
+RicoTranslate.addPhrase("Include only this value","Âêëþ÷èòü òîëüêî ýòî çíà÷åíèå")\r
+RicoTranslate.addPhrase("Exclude this value","Èñêëþ÷èòü ýòî çíà÷åíèå")\r
+RicoTranslate.addPhrase("Greater than or equal to this value","Áîëüøå èëè ðàâíî äàííîìó çíà÷åíèþ")\r
+RicoTranslate.addPhrase("Less than or equal to this value","Ìåíüøå èëè ðàâíî äàííîìó çíà÷åíèþ")\r
+RicoTranslate.addPhrase("Contains keyword","Ñîäåðæèò çíà÷åíèå")\r
+RicoTranslate.addPhrase("Change keyword","Èçìåíèòü çíà÷åíèå")\r
+RicoTranslate.addPhrase("Enter keyword to search for","Èñêàòü ïî êëþ÷ó")\r
+RicoTranslate.addPhrase("use * as a wildcard","Èñïîëüçîâàòü * äëÿ âñåõ çàïèñåé")\r
+RicoTranslate.addPhrase("also","òàêæå")\r
+RicoTranslate.addPhrase("Remove filter","Óáðàòü ôèëüòð")\r
+\r
+RicoTranslate.addPhrase("Waiting for data","Îæèäàíèå äàííûõ")\r
+RicoTranslate.addPhrase("Request for data timed out","Ïðåâûøåí èíòåðâàë îæèäàíèÿ äàííûõ")\r
+\r
+RicoTranslate.addPhrase("No matching records","Íåò ñîâïàäåíèé")\r
+RicoTranslate.addPhrase("Listing records","Ïðîñìîòð çàïèñåé")\r
+RicoTranslate.addPhrase("of","èç")\r
+RicoTranslate.addPhrase("of about","èç î")\r
+\r
+RicoTranslate.thouSep=","\r
+RicoTranslate.decPoint="."\r
+RicoTranslate.dateFmt="dd/mm/yyyy"\r
+\r
+RicoTranslate.monthNames=['ßíâàðü','Ôåâðàëü', 'Ìàðò', 'Àïðåëü', 'Ìàé','Èþíü', 'Èþëü','Àâãóñò','Ñåíòÿáðü','Îòêòÿáðü','Íîÿáðü','Äåêàáðü']\r
+RicoTranslate.dayNames=['Ïîíåäåëüíèê','Âòîðíèê','Ñðåäà','×åòâåðã','Ïÿòíèöà','Ñóááîòà','Âîñêðåñåíüå']\r
+\r
+// added for 22 August release\r
+\r
+RicoTranslate.addPhrase("Print","Ïå÷àòü")\r
+RicoTranslate.addPhrase("Export","Ýêñïîðò")\r
+RicoTranslate.addPhrase("Visible rows","Âèäèìûå çàïèñè")\r
+RicoTranslate.addPhrase("All rows","Âñå çàïèñè")\r
+\r
+// added for Feb 2007 release\r
+\r
+RicoTranslate.addPhrase("Loading","Çàãðóçêà")\r
+RicoTranslate.addPhrase("minutes before your session expires","ìèíóò äî èñòå÷åíèÿ ñåññèè")\r
+RicoTranslate.addPhrase("EXPIRED","ÈÑÒÅÊËÎ")\r
+RicoTranslate.addPhrase("Close","Çàêðûòî")\r
+RicoTranslate.addPhrase("Cancel","Îòìåíà")\r
+RicoTranslate.addPhrase("Save","Ñîõðàíèòü")\r
+RicoTranslate.addPhrase("Add","Äîáàâèòü")\r
+RicoTranslate.addPhrase("Edit","Ðåäàêòèðîâàòü")\r
+RicoTranslate.addPhrase("Delete","Óäàëèòü")\r
+RicoTranslate.addPhrase("Are you sure you want to delete","Âû óâåðåííû,÷òî õîòèòå óäàëèòü")\r
+RicoTranslate.addPhrase("record","çàïèñü")\r
+RicoTranslate.addPhrase("this record","ýòà çàïèñü")\r
+RicoTranslate.addPhrase("new record","íîâàÿ çàïèñü")\r
+RicoTranslate.addPhrase("Please enter","Ïîæàëóéñòà ââåäèòå")\r
+RicoTranslate.addPhrase("a value for","çíà÷åíèå äëÿ")\r
+RicoTranslate.addPhrase("an integer value for","öåëîå çíà÷åíèå äëÿ")\r
+RicoTranslate.addPhrase("a positive integer value for","ïîëîæèòåëüíîå öåëîå çíà÷åíèå äëÿ")\r
+RicoTranslate.addPhrase("The request returned an error","Çàïðîñ âîçâðàòèë îøèáêó")\r
+\r
+\r
+\r
+// for demo only:\r
+\r
+RicoTranslate.addPhrase("Show orders for","Ïîêàçàòü çíà÷åíèÿ äëÿ")\r
+RicoTranslate.addPhrase("Show order detail","Ïîêàçàòü çíà÷åíèÿ ïîäðîáíî")\r
+RicoTranslate.addPhrase("order","çíà÷åíèå")\r
+RicoTranslate.addPhrase("this order","ýòî çíà÷åíèå")\r
+RicoTranslate.addPhrase("new order","íîâîå çíà÷åíèå")\r
+\r
+\r
+// add your own here:\r
diff --git a/NP_TrackBack/trunk/trackback/js/rico/translations/livegrid_ua.js b/NP_TrackBack/trunk/trackback/js/rico/translations/livegrid_ua.js
new file mode 100644 (file)
index 0000000..18206c8
--- /dev/null
@@ -0,0 +1,82 @@
+/*****************************************************************\r
+ Page : livegrid_ua.js\r
+ Description : LiveGrid text for Ukrainian menus\r
+ Version 0.1 (by Illiya Gannitskiy,Alexey Uvarov)\r
+ If you have better translations, or would like to include\r
+ translations for another language, please send them to dowdybrown@yahoo.com\r
+******************************************************************/\r
+\r
+RicoTranslate.addPhrase("Sort by","Cîðòóâàííÿ ïî")\r
+RicoTranslate.addPhrase("Filter by","Ô³ëüòðàö³ÿ ïî")\r
+RicoTranslate.addPhrase("Hide","Ñõîâàòè")\r
+RicoTranslate.addPhrase("Show","Ïîêàçàòè")\r
+RicoTranslate.addPhrase("Show All","Ïîêàçàòè âñå")\r
+RicoTranslate.addPhrase("Ascending","Çðîñòàþ÷à")\r
+RicoTranslate.addPhrase("Descending","Óáóâàþ÷à")\r
+\r
+RicoTranslate.addPhrase("Include only this value","Âêëþ÷èòè ò³ëüêè öå çíà÷åííÿ")\r
+RicoTranslate.addPhrase("Exclude this value","Âèêëþ÷èòè öå çíà÷åííÿ")\r
+RicoTranslate.addPhrase("Greater than or equal to this value","Á³ëüøå àáî ð³âíî äàíîìó çíà÷åííþ")\r
+RicoTranslate.addPhrase("Less than or equal to this value","Ìåíøå àáî ð³âíî äàíîìó çíà÷åííþ")\r
+RicoTranslate.addPhrase("Contains keyword","̳ñòèòü çíà÷åííÿ")\r
+RicoTranslate.addPhrase("Change keyword","Çì³íèòè çíà÷åííÿ")\r
+RicoTranslate.addPhrase("Enter keyword to search for","Øóêàòè ïî êëþ÷ó")\r
+RicoTranslate.addPhrase("use * as a wildcard","Âèêîðèñòîâóâàòè * äëÿ âñ³õ çàïèñ³â")\r
+RicoTranslate.addPhrase("also","òàêîæ")\r
+RicoTranslate.addPhrase("Remove filter","Ïðèáðàòè ô³ëüòð")\r
+\r
+RicoTranslate.addPhrase("Waiting for data","Î÷³êóâàííÿ äàíèõ")\r
+RicoTranslate.addPhrase("Request for data timed out","Ïåðåâèùåíèé ³íòåðâàë î÷³êóâàííÿ äàíèõ")\r
+\r
+RicoTranslate.addPhrase("No matching records","Íåìຠçá³ã³â")\r
+RicoTranslate.addPhrase("Listing records","Ïåðåãëÿä çàïèñ³â")\r
+RicoTranslate.addPhrase("of","ç")\r
+RicoTranslate.addPhrase("of about","ç î")\r
+\r
+RicoTranslate.thouSep=","\r
+RicoTranslate.decPoint="."\r
+RicoTranslate.dateFmt="dd/mm/yyyy"\r
+\r
+RicoTranslate.monthNames=['ѳ÷åíü','Ëþòèé', 'Áåðåçåíü', 'Êâ³òåíü', 'Òðàâåíü','×åðâåíü', 'Ëèïåíü','Ñåðïåíü','Âåðåñåíü','Æîâòåíü','Ëèñòîïàä','Ãðóäåíü']\r
+RicoTranslate.dayNames=['Ïîíåä³ëîê','³âòîðîê','Ñåðåäà','×åòâåð','Ï'ÿòíèöÿ','Ñóáîòà','Íåä³ëÿ']\r
+\r
+// added for 22 August release\r
+\r
+RicoTranslate.addPhrase("Print","Äðóê")\r
+RicoTranslate.addPhrase("Export","Åêñïîðò")\r
+RicoTranslate.addPhrase("Visible rows","Âèäèì³ çàïèñè")\r
+RicoTranslate.addPhrase("All rows","Âñ³ çàïèñè")\r
+\r
+// added for Feb 2007 release\r
+\r
+RicoTranslate.addPhrase("Loading","Çàâàíòàæåííÿ")\r
+RicoTranslate.addPhrase("minutes before your session expires","õâèëèí äî çàê³í÷åííÿ ñåñ³¿")\r
+RicoTranslate.addPhrase("EXPIRED","ÇÀʲÍ×ÈËÎÑß")\r
+RicoTranslate.addPhrase("Close","Çàêðèòî")\r
+RicoTranslate.addPhrase("Cancel","³äì³íà")\r
+RicoTranslate.addPhrase("Save","Çáåðåãòè")\r
+RicoTranslate.addPhrase("Add","Äîäàòè")\r
+RicoTranslate.addPhrase("Edit","Ðåäàãóâàòè")\r
+RicoTranslate.addPhrase("Delete","Âèäàëèòè")\r
+RicoTranslate.addPhrase("Are you sure you want to delete","Âè óïåâíåí³,ùî áàæàºòå âèäàëèòè")\r
+RicoTranslate.addPhrase("record","çàïèñ")\r
+RicoTranslate.addPhrase("this record","öåé çàïèñ")\r
+RicoTranslate.addPhrase("new record","íîâèé çàïèñ")\r
+RicoTranslate.addPhrase("Please enter","Áóäü ëàñêà, ââåä³òü")\r
+RicoTranslate.addPhrase("a value for","çíà÷åííÿ äëÿ")\r
+RicoTranslate.addPhrase("an integer value for","ö³ëå çíà÷åííÿ äëÿ")\r
+RicoTranslate.addPhrase("a positive integer value for","ïîçèòèâíå ö³ëå çíà÷åííÿ äëÿ")\r
+RicoTranslate.addPhrase("The request returned an error","Çàïèò ïîâåðíóâ ïîìèëêó")\r
+\r
+\r
+\r
+// for demo only:\r
+\r
+RicoTranslate.addPhrase("Show orders for","Ïîêàçàòè çíà÷åííÿ")\r
+RicoTranslate.addPhrase("Show order detail","Ïîêàçàòè çíà÷åííÿ äåòàëüíî")\r
+RicoTranslate.addPhrase("order","Çíà÷åííÿ")\r
+RicoTranslate.addPhrase("this order","öå çíà÷åííÿ")\r
+RicoTranslate.addPhrase("new order","íîâå çíà÷åííÿ")\r
+\r
+\r
+// add your own here:\r
diff --git a/NP_TrackBack/trunk/trackback/js/rico/translations/livegrid_zh.js b/NP_TrackBack/trunk/trackback/js/rico/translations/livegrid_zh.js
new file mode 100644 (file)
index 0000000..96b9a78
--- /dev/null
@@ -0,0 +1,55 @@
+/*****************************************************************
+ Page : livegrid_zh.js
+ Description : LiveGrid text for CHINA menus
+ Translator: Sam Shan at gz_shanming@yahoo.com
+ Version 0.1 - please send corrections to dowdybrown@yahoo.com
+ Version 0.2 - Updated based on Xinjun liu's comments.
+******************************************************************/
+
+RicoTranslate.addPhrase("Sort by","排序")
+RicoTranslate.addPhrase("Filter by","过滤")
+RicoTranslate.addPhrase("Hide","隐藏")
+RicoTranslate.addPhrase("Show","显示")
+RicoTranslate.addPhrase("Ascending","升序")
+RicoTranslate.addPhrase("Descending","降序")
+
+RicoTranslate.addPhrase("Include only this value","只包含此值")
+RicoTranslate.addPhrase("Exclude this value","不包含此值")
+RicoTranslate.addPhrase("Greater than or equal to this value","大于等于")
+RicoTranslate.addPhrase("Less than or equal to this value","小于等于")
+RicoTranslate.addPhrase("Contains keyword","包含关键字")
+RicoTranslate.addPhrase("Change keyword","更改关键字")
+RicoTranslate.addPhrase("Enter keyword to search for","输入关键字")
+RicoTranslate.addPhrase("use * as a wildcard","使用*通配符")
+RicoTranslate.addPhrase("also","也")
+RicoTranslate.addPhrase("Remove filter","移除过滤器")
+
+RicoTranslate.addPhrase("Waiting for data","等待接收数据")
+RicoTranslate.addPhrase("Request for data timed out","等待数据超时")
+
+RicoTranslate.addPhrase("No matching records","没有匹配的数据")
+RicoTranslate.addPhrase("Listing records","列出纪录")
+RicoTranslate.addPhrase("of","共")
+RicoTranslate.addPhrase("of about","大约")
+
+RicoTranslate.monthNames=["一月", "二月", "三月", "四月", "五月","六月", "七月", "八月", "九月", "十月", "十一月", "十二月"]
+RicoTranslate.dayNames=["星期日","星期一", "星期二","星期三", "星期四", "星期五", "星期六"]
+
+RicoTranslate.thouSep="."
+RicoTranslate.decPoint=","
+RicoTranslate.dateFmt="yyyy/mm/dd"
+
+// added for 22 August release
+
+RicoTranslate.addPhrase("Print","打印")
+RicoTranslate.addPhrase("Export","输出")
+RicoTranslate.addPhrase("Visible rows","当前显示记录")
+RicoTranslate.addPhrase("All rows","所有记录")
+
+// for demo only:
+
+RicoTranslate.addPhrase("Show orders for","显示订单")
+RicoTranslate.addPhrase("Show order detail","显示订单详情")
+
+// add your own here:
+
diff --git a/NP_TrackBack/trunk/trackback/language/english.php b/NP_TrackBack/trunk/trackback/language/english.php
new file mode 100644 (file)
index 0000000..54f2aaf
--- /dev/null
@@ -0,0 +1,52 @@
+<?php 
+define('_TB_LIST_IT', 'List it');
+define('_TB_DESCRIPTION', 'Send trackbacks to other weblogs and receive tracbacks from others.');
+
+define('_TB_NORTIFICATION_MAIL_BODY', "Your weblog received a new trackback from <%blogname%> for ID <%tb_id%>. Below are the full details:\n\nURL:\t<%url%>\nTitle:\t<%title%>\nExcerpt:\t<%excerpt%>\nBlogname:\t<%blogname%>");
+define('_TB_NORTIFICATION_MAIL_TITLE', "New Trackback received for ID <%tb_id%>");
+
+define('_TB_AcceptPing', 'Accept pings');
+define('_TB_SendPings', 'Allow sending pings');
+define('_TB_AutoXMLHttp', 'Auto-detect Trackback URLs as you type');
+define('_TB_CheckIDs', 'Only allow valid itemids as trackback-ids');
+
+define('_TB_tplHeader', 'Header');
+define('_TB_tplEmpty', 'Empty');
+define('_TB_tplItem', 'Item');
+define('_TB_tplFooter', 'Footer');
+
+define('_TB_tplHeader_VAL', "<div class=\"tb\">\n\t<div class=\"head\">Trackback</div><%admin%>\n\n");
+define('_TB_tplEmpty_VAL', "\t<div class=\"empty\">\n\t\tThere are currently no trackbacks for this item.\n\t</div>\n\n");
+define('_TB_tplItem_VAL', "\t<div class=\"item\">\n\t\t<div class=\"name\"><%name%></div>\n\t\t<div class=\"body\">\n\t\t\t<a href=\"<%url%>\"><%title%>:</a> <%excerpt%>\n\t\t</div>\n\t\t<div class=\"date\">\n\t\t\t<%date%>\n\t\t</div>\n\t</div>\n\n");
+define('_TB_tplFooter_VAL', "\t<div class=\"info\">\n\t\tUse this <a href=\"<%action%>\">TrackBack url</a> to ping this item (right-click, copy link target).\n\t\tIf your blog does not support Trackbacks you can manually add your trackback by using <a href=\"<%form%>\" onclick=\"window.open(this.href, 'trackback', 'scrollbars=yes,width=600,height=340,left=10,top=10,status=yes,resizable=yes'); return false;\">this form</a>.\n\t</div>\n</div>");
+
+define('_TB_tplLocalHeader_VAL', "<div class=\"tblocal\">\n\t<div class=\"head\">Local Trackback</div>\n\n");
+define('_TB_tplLocalEmpty_VAL', "");
+define('_TB_tplLocalItem_VAL', "\t<div class=\"item\">\n\t\t<div class=\"body\">\n\t\t\t<%delete%> <a href=\"<%url%>\"><%title%></a>: <%excerpt%>\n\t\t</div>\n\t\t<div class=\"date\">\n\t\t\t<%timestamp%>\n\t\t</div>\n\t</div>\n\n");
+define('_TB_tplLocalFooter_VAL', "\t</div>");
+
+define('_TB_tplLocalHeader', 'Header (Local)');
+define('_TB_tplLocalEmpty', 'Empty (Local)');
+define('_TB_tplLocalItem', 'Item (Local)');
+define('_TB_tplLocalFooter', 'Footer (Local)');
+
+define('_TB_tplTbNone', 'Trackback count (none)');
+define('_TB_tplTbOne', 'Trackback count (one)');
+define('_TB_tplTbMore', 'Trackback count (more)');
+
+define('_TB_dateFormat', 'Date format');
+define('_TB_dateFormat_VAL', "%e/%m/%g");
+
+define('_TB_ajaxEnabled', 'Enable Ajax ?');
+define('_TB_NotifyEmail', 'Which e-mail address to send these notification to?');
+define('_TB_NotifyEmailBlog', 'Which e-mail address to send these notification to?');
+
+define('_TB_DropTable', 'Clear the database when uninstalling');
+define('_TB_HideUrl', 'Hide external URL');
+define('_TB_ItemAcceptPing', 'Accept pings');
+define('_TB_isAcceptWOLink', 'Accept pings w/o link ?');
+define('_TB_isAcceptWOLinkDef', 'Accept pings w/o link ? (blog default)');
+define('_TB_AllowTrackBack', 'Accept pings to this blog');
+
+define('_TB_isAcceptWOLink_VAL', 'default|default|yes|yes|no|no');
+define('_TB_isAcceptWOLinkDef_VAL', 'yes|yes|no (block)|block|no (ignore)|ignore');
diff --git a/NP_TrackBack/trunk/trackback/language/japanese-euc.php b/NP_TrackBack/trunk/trackback/language/japanese-euc.php
new file mode 100644 (file)
index 0000000..38467a3
--- /dev/null
@@ -0,0 +1,51 @@
+<?php 
+define('_TB_LIST_IT', 'Á÷¿®¥ê¥¹¥È¤ËÄɲÃ');
+define('_TB_DESCRIPTION', '¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤Î¼õÁ÷¿®¤ò¹Ô¤¤¤Þ¤¹');
+
+define('_TB_NORTIFICATION_MAIL_BODY', "<%blogname%> ¤«¤é ID:<%tb_id%> ¤Îµ­»ö¤ËÂФ·¤Æ¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤ò¼õ¿®¤·¤Þ¤·¤¿¡£ ¾ÜºÙ¤Ï²¼µ­¤Î¤È¤ª¤ê¤Ç¤¹:\n\nURL:\t<%url%>\n¥¿¥¤¥È¥ë:\t<%title%>\n³µÍ×:\t<%excerpt%>\n¥Ö¥í¥°Ì¾:\t<%blogname%>");
+define('_TB_NORTIFICATION_MAIL_TITLE', "¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤ò¼õ¿®¤·¤Þ¤·¤¿ ID:<%tb_id%>");
+
+define('_TB_AcceptPing', '¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤Î¼õÉÕ¤ò¤¹¤ë¤«?');
+define('_TB_SendPings', '¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤ÎÁ÷¿®¤ò²Äǽ¤Ë¤¹¤ë¤«?');
+define('_TB_AutoXMLHttp', 'autodiscoveryµ¡Ç½(µ­»öÆâ¤Î¥ê¥ó¥¯Àè¤ÎTrackbackURL¤Î¼«Æ°¸¡ÃÎ)¤ò»È¤¦¤«?');
+define('_TB_CheckIDs', 'ping¼õÉÕ»þ¤ËÍ­¸ú¤Êitemid¤«¤É¤¦¤«¤ò¥Á¥§¥Ã¥¯¤¹¤ë¤«?');
+
+define('_TB_tplHeader', 'TB°ìÍ÷¥Æ¥ó¥×¥ì¡¼¥È(¥Ø¥Ã¥ÀÉô)');
+define('_TB_tplEmpty', 'TB°ìÍ÷¥Æ¥ó¥×¥ì¡¼¥È(0·ï¤Î¤È¤­)');
+define('_TB_tplItem', 'TB°ìÍ÷¥Æ¥ó¥×¥ì¡¼¥È(¥¢¥¤¥Æ¥àÉô)');
+define('_TB_tplFooter', 'TB°ìÍ÷¥Æ¥ó¥×¥ì¡¼¥È(¥Õ¥Ã¥¿Éô)');
+
+define('_TB_tplHeader_VAL', "<div class=\"tb\">\n\t<div class=\"head\">¥È¥é¥Ã¥¯¥Ð¥Ã¥¯</div><%admin%>\n\n");
+define('_TB_tplEmpty_VAL', "\t<div class=\"empty\">\n\t\t¤³¤Î¥¨¥ó¥È¥ê¤Ë¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤Ï¤¢¤ê¤Þ¤»¤ó\n\t</div>\n\n");
+define('_TB_tplItem_VAL', "\t<div class=\"item\">\n\t\t<div class=\"name\"><%name%></div>\n\t\t<div class=\"body\">\n\t\t\t<a href=\"<%url%>\"><%title%>:</a> <%excerpt%>\n\t\t</div>\n\t\t<div class=\"date\">\n\t\t\t<%date%>\n\t\t</div>\n\t</div>\n\n");
+define('_TB_tplFooter_VAL', "\t<div class=\"info\">\n\t\t¤³¤Î<a href=\"<%action%>\">¥È¥é¥Ã¥¯¥Ð¥Ã¥¯URL</a>¤ò»È¤Ã¤Æ¤³¤Îµ­»ö¤Ë¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤òÁ÷¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£\n\t\t¤â¤·¤¢¤Ê¤¿¤Î¥Ö¥í¥°¤¬¥È¥é¥Ã¥¯¥Ð¥Ã¥¯Á÷¿®¤ËÂбþ¤·¤Æ¤¤¤Ê¤¤¾ì¹ç¤Ë¤Ï<a href=\"<%form%>\" onclick=\"window.open(this.href, 'trackback', 'scrollbars=yes,width=600,height=340,left=10,top=10,status=yes,resizable=yes'); return false;\">¤³¤Á¤é¤Î¥Õ¥©¡¼¥à</a>¤«¤é¥È¥é¥Ã¥¯¥Ð¥Ã¥¯¤òÁ÷¿®¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£.\n\t</div>\n</div>");
+
+define('_TB_tplLocalHeader', '¥í¡¼¥«¥ëTB°ìÍ÷¥Æ¥ó¥×¥ì¡¼¥È(¥Ø¥Ã¥ÀÉô)');
+define('_TB_tplLocalEmpty', '¥í¡¼¥«¥ëTB°ìÍ÷¥Æ¥ó¥×¥ì¡¼¥È(0·ï¤Î¤È¤­)');
+define('_TB_tplLocalItem', '¥í¡¼¥«¥ëTB°ìÍ÷¥Æ¥ó¥×¥ì¡¼¥È(¥¢¥¤¥Æ¥àÉô)');
+define('_TB_tplLocalFooter', '¥í¡¼¥«¥ëTB°ìÍ÷¥Æ¥ó¥×¥ì¡¼¥È(¥Õ¥Ã¥¿Éô)');
+
+define('_TB_tplLocalHeader_VAL', "<div class=\"tblocal\">\n\t<div class=\"head\">¥í¡¼¥«¥ë¥È¥é¥Ã¥¯¥Ð¥Ã¥¯</div>\n\n");
+define('_TB_tplLocalEmpty_VAL', "");
+define('_TB_tplLocalItem_VAL', "\t<div class=\"item\">\n\t\t<div class=\"body\">\n\t\t\t<%delete%> <a href=\"<%url%>\"><%title%></a>: <%excerpt%>\n\t\t</div>\n\t\t<div class=\"date\">\n\t\t\t<%timestamp%>\n\t\t</div>\n\t</div>\n\n");
+define('_TB_tplLocalFooter_VAL', "\t</div>");
+
+define('_TB_tplTbNone', 'TB¿ôɽ¼¨·Á¼°(0·ï)');
+define('_TB_tplTbOne', 'TB¿ôɽ¼¨·Á¼°(1·ï)');
+define('_TB_tplTbMore', 'TB¿ôɽ¼¨·Á¼°(2·ï°Ê¾å)');
+define('_TB_dateFormat', 'ÆüÉդηÁ¼°');
+define('_TB_dateFormat_VAL', "%Y/%m/%d %H:%I");
+
+define('_TB_ajaxEnabled', '´ÉÍý²èÌ̤ÇAjax¤òÍ­¸ú¤Ë¤¹¤ë¤«');
+define('_TB_NotifyEmail', 'ping¼õÉÕ»þ¤Î¥á¡¼¥ëÁ÷¿®Àè(;¤Ç¶èÀڤäÆÊ£¿ôÆþÎϲÄǽ)');
+define('_TB_NotifyEmailBlog', 'ping¼õÉÕ»þ¤Î¥á¡¼¥ëÁ÷¿®Àè(;¤Ç¶èÀڤäÆÊ£¿ôÆþÎϲÄǽ)');
+
+define('_TB_DropTable', '¥×¥é¥°¥¤¥ó¤Îºï½ü»þ¤Ë¥Ç¡¼¥¿¤òºï½ü¤¹¤ë¤«?');
+define('_TB_HideUrl', '°ìÍ÷ɽ¼¨¤ÎºÝ¤Ë³°Éô¤ÎURL¤ò¥ê¥À¥¤¥ì¥¯¥È¤ËÊÑ´¹¤¹¤ë¤«?');
+define('_TB_ItemAcceptPing', 'TB¤ò¼õÉÕ¤¹¤ë¤«?');
+define('_TB_isAcceptWOLink', '¸ÀµÚ¥ê¥ó¥¯¤¬¤Ê¤¯¤Æ¤âTB¤ò¼õÉÕ¤¹¤ë¤«?');
+define('_TB_isAcceptWOLinkDef', '¸ÀµÚ¥ê¥ó¥¯¤¬¤Ê¤¯¤Æ¤âTB¤ò¼õÉÕ¤¹¤ë¤«? (blog¥Ç¥Õ¥©¥ë¥È)');
+define('_TB_AllowTrackBack', '¤³¤Î¥Ö¥í¥°¤ÇTB¤ò¼õÉÕ¤¹¤ë¤«?');
+
+define('_TB_isAcceptWOLink_VAL', '¥Ö¥í¥°¥Ç¥Õ¥©¥ë¥È¤Ë½¾¤¦|default|¤Ï¤¤|yes|¤¤¤¤¤¨|no');
+define('_TB_isAcceptWOLinkDef_VAL', '¤Ï¤¤|yes|¤¤¤¤¤¨(ÊÝα)|block|¤¤¤¤¤¨(̵»ë)|ignore');
diff --git a/NP_TrackBack/trunk/trackback/language/japanese-utf8.php b/NP_TrackBack/trunk/trackback/language/japanese-utf8.php
new file mode 100644 (file)
index 0000000..1d18999
--- /dev/null
@@ -0,0 +1,52 @@
+<?php 
+define('_TB_LIST_IT', '送信リストに追加');
+define('_TB_DESCRIPTION', 'トラックバックの受送信を行います');
+
+define('_TB_NORTIFICATION_MAIL_BODY', "<%blogname%> から ID:<%tb_id%> の記事に対してトラックバックを受信しました。 詳細は下記のとおりです:\n\nURL:\t<%url%>\nタイトル:\t<%title%>\n概要:\t<%excerpt%>\nブログ名:\t<%blogname%>");
+define('_TB_NORTIFICATION_MAIL_TITLE', "トラックバックを受信しました ID:<%tb_id%>");
+
+define('_TB_AcceptPing', 'トラックバックの受付をするか?');
+define('_TB_SendPings', 'トラックバックの送信を可能にするか?');
+define('_TB_AutoXMLHttp', 'autodiscovery機能(記事内のリンク先のTrackbackURLの自動検知)を使うか?');
+define('_TB_CheckIDs', 'ping受付時に有効なitemidかどうかをチェックするか?');
+
+define('_TB_tplHeader', 'TB一覧テンプレート(ヘッダ部)');
+define('_TB_tplEmpty', 'TB一覧テンプレート(0件のとき)');
+define('_TB_tplItem', 'TB一覧テンプレート(アイテム部)');
+define('_TB_tplFooter', 'TB一覧テンプレート(フッタ部)');
+
+define('_TB_tplHeader_VAL', "<div class=\"tb\">\n\t<div class=\"head\">トラックバック</div><%admin%>\n\n");
+define('_TB_tplEmpty_VAL', "\t<div class=\"empty\">\n\t\tこのエントリにトラックバックはありません\n\t</div>\n\n");
+define('_TB_tplItem_VAL', "\t<div class=\"item\">\n\t\t<div class=\"name\"><%name%></div>\n\t\t<div class=\"body\">\n\t\t\t<a href=\"<%url%>\"><%title%>:</a> <%excerpt%>\n\t\t</div>\n\t\t<div class=\"date\">\n\t\t\t<%date%>\n\t\t</div>\n\t</div>\n\n");
+define('_TB_tplFooter_VAL', "\t<div class=\"info\">\n\t\tこの<a href=\"<%action%>\">トラックバックURL</a>を使ってこの記事にトラックバックを送ることができます。\n\t\tもしあなたのブログがトラックバック送信に対応していない場合には<a href=\"<%form%>\" onclick=\"window.open(this.href, 'trackback', 'scrollbars=yes,width=600,height=340,left=10,top=10,status=yes,resizable=yes'); return false;\">こちらのフォーム</a>からトラックバックを送信することができます。.\n\t</div>\n</div>");
+
+define('_TB_tplLocalHeader', 'ローカルTB一覧テンプレート(ヘッダ部)');
+define('_TB_tplLocalEmpty', 'ローカルTB一覧テンプレート(0件のとき)');
+define('_TB_tplLocalItem', 'ローカルTB一覧テンプレート(アイテム部)');
+define('_TB_tplLocalFooter', 'ローカルTB一覧テンプレート(フッタ部)');
+
+define('_TB_tplLocalHeader_VAL', "<div class=\"tblocal\">\n\t<div class=\"head\">ローカルトラックバック</div>\n\n");
+define('_TB_tplLocalEmpty_VAL', "");
+define('_TB_tplLocalItem_VAL', "\t<div class=\"item\">\n\t\t<div class=\"body\">\n\t\t\t<%delete%> <a href=\"<%url%>\"><%title%></a>: <%excerpt%>\n\t\t</div>\n\t\t<div class=\"date\">\n\t\t\t<%timestamp%>\n\t\t</div>\n\t</div>\n\n");
+define('_TB_tplLocalFooter_VAL', "\t</div>");
+
+define('_TB_tplTbNone', 'TB数表示形式(0件)');
+define('_TB_tplTbOne', 'TB数表示形式(1件)');
+define('_TB_tplTbMore', 'TB数表示形式(2件以上)');
+define('_TB_dateFormat', '日付の形式');
+define('_TB_dateFormat_VAL', "%Y/%m/%d %H:%I");
+
+
+define('_TB_ajaxEnabled', '管理画面でAjaxを有効にするか');
+define('_TB_NotifyEmail', 'ping受付時のメール送信先(;で区切って複数入力可能)');
+define('_TB_NotifyEmailBlog', 'ping受付時のメール送信先(;で区切って複数入力可能)');
+
+define('_TB_DropTable', 'プラグインの削除時にデータを削除するか?');
+define('_TB_HideUrl', '一覧表示の際に外部のURLをリダイレクトに変換するか?');
+define('_TB_ItemAcceptPing', 'TBを受付するか?');
+define('_TB_isAcceptWOLink', '言及リンクがなくてもTBを受付するか?');
+define('_TB_isAcceptWOLinkDef', '言及リンクがなくてもTBを受付するか? (blogデフォルト)');
+define('_TB_AllowTrackBack', 'このブログでTBを受付するか?');
+
+define('_TB_isAcceptWOLink_VAL', 'ブログデフォルトに従う|default|はい|yes|いいえ|no');
+define('_TB_isAcceptWOLinkDef_VAL', 'はい|yes|いいえ(保留)|block|いいえ(無視)|ignore');
diff --git a/NP_TrackBack/trunk/trackback/mkeuc.sh b/NP_TrackBack/trunk/trackback/mkeuc.sh
new file mode 100644 (file)
index 0000000..d9f44be
--- /dev/null
@@ -0,0 +1,11 @@
+#!/bin/bash -x\r
+\r
+FILES=`find japanese-utf8.templates -name '*ml'`\r
+\r
+for utf8file in $FILES\r
+do\r
+       eucfile=`echo $utf8file | sed 's/japanese-utf8/japanese-euc/'`\r
+       nkf -e -W -d < $utf8file > $eucfile\r
+done\r
+\r
+nkf -e -W -d < japanese-utf8.help.html > japanese-euc.help.html\r
diff --git a/NP_TrackBack/trunk/trackback/silk/accept.png b/NP_TrackBack/trunk/trackback/silk/accept.png
new file mode 100644 (file)
index 0000000..f103759
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/silk/accept.png differ
diff --git a/NP_TrackBack/trunk/trackback/silk/application_view_list.png b/NP_TrackBack/trunk/trackback/silk/application_view_list.png
new file mode 100644 (file)
index 0000000..1745b48
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/silk/application_view_list.png differ
diff --git a/NP_TrackBack/trunk/trackback/silk/cross.png b/NP_TrackBack/trunk/trackback/silk/cross.png
new file mode 100644 (file)
index 0000000..2802c2c
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/silk/cross.png differ
diff --git a/NP_TrackBack/trunk/trackback/silk/delete.png b/NP_TrackBack/trunk/trackback/silk/delete.png
new file mode 100644 (file)
index 0000000..49d3864
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/silk/delete.png differ
diff --git a/NP_TrackBack/trunk/trackback/silk/exclamation.png b/NP_TrackBack/trunk/trackback/silk/exclamation.png
new file mode 100644 (file)
index 0000000..39d4592
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/silk/exclamation.png differ
diff --git a/NP_TrackBack/trunk/trackback/silk/help.png b/NP_TrackBack/trunk/trackback/silk/help.png
new file mode 100644 (file)
index 0000000..477a015
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/silk/help.png differ
diff --git a/NP_TrackBack/trunk/trackback/silk/house_go.png b/NP_TrackBack/trunk/trackback/silk/house_go.png
new file mode 100644 (file)
index 0000000..396ecb7
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/silk/house_go.png differ
diff --git a/NP_TrackBack/trunk/trackback/silk/link.png b/NP_TrackBack/trunk/trackback/silk/link.png
new file mode 100644 (file)
index 0000000..93d4f0e
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/silk/link.png differ
diff --git a/NP_TrackBack/trunk/trackback/silk/link_break.png b/NP_TrackBack/trunk/trackback/silk/link_break.png
new file mode 100644 (file)
index 0000000..244b018
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/silk/link_break.png differ
diff --git a/NP_TrackBack/trunk/trackback/silk/plugin_edit.png b/NP_TrackBack/trunk/trackback/silk/plugin_edit.png
new file mode 100644 (file)
index 0000000..80c9a62
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/silk/plugin_edit.png differ
diff --git a/NP_TrackBack/trunk/trackback/silk/readme.txt b/NP_TrackBack/trunk/trackback/silk/readme.txt
new file mode 100644 (file)
index 0000000..400a64d
--- /dev/null
@@ -0,0 +1,22 @@
+Silk icon set 1.3\r
+\r
+_________________________________________\r
+Mark James\r
+http://www.famfamfam.com/lab/icons/silk/\r
+_________________________________________\r
+\r
+This work is licensed under a\r
+Creative Commons Attribution 2.5 License.\r
+[ http://creativecommons.org/licenses/by/2.5/ ]\r
+\r
+This means you may use it for any purpose,\r
+and make any changes you like.\r
+All I ask is that you include a link back\r
+to this page in your credits.\r
+\r
+Are you using this icon set? Send me an email\r
+(including a link or picture if available) to\r
+mjames@gmail.com\r
+\r
+Any other questions about this icon set please\r
+contact mjames@gmail.com
\ No newline at end of file
diff --git a/NP_TrackBack/trunk/trackback/silk/tick.png b/NP_TrackBack/trunk/trackback/silk/tick.png
new file mode 100644 (file)
index 0000000..b2d0522
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/silk/tick.png differ
diff --git a/NP_TrackBack/trunk/trackback/silk/transmit_go.png b/NP_TrackBack/trunk/trackback/silk/transmit_go.png
new file mode 100644 (file)
index 0000000..57993b7
Binary files /dev/null and b/NP_TrackBack/trunk/trackback/silk/transmit_go.png differ
diff --git a/NP_TrackBack/trunk/trackback/template.php b/NP_TrackBack/trunk/trackback/template.php
new file mode 100644 (file)
index 0000000..64ffe8a
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+class Trackback_Template {
+    var $vars; 
+
+    function Trackback_Template($file = null, $prefix = '') {
+        $this->file = ($prefix ? $prefix . '/' : '') . $file;
+               $this->prefix = $prefix;
+    }
+
+    function set($name, $value) {
+        $this->vars[$name] = is_object($value) ? $value->fetch() : $value;
+    }
+       
+       function template($file = null) {
+               $language = ereg_replace( '[\\|/]', '', getLanguageName());
+               $this->file = (file_exists(($this->prefix ? $this->prefix . '/' : '') . $language.'.'.$file))? ($this->prefix ? $this->prefix . '/' : '') . $language.'.'.$file: ($this->prefix ? $this->prefix . '/' : '') . $file;
+       }
+
+    function fetch($file = null) {
+        if(!$file) $file = $this->file;
+               else ($prefix ? $prefix . '/' : '') . $file;
+               
+               if ($file != null)
+               {
+               if (is_array($this->vars)) extract($this->vars);          
+        
+                       ob_start();
+               include($file);
+               $contents = ob_get_contents();
+               ob_end_clean();
+        
+                       return $contents;
+               }
+    }
+}
diff --git a/NP_TrackBack/trunk/trackback/templates/all.html b/NP_TrackBack/trunk/trackback/templates/all.html
new file mode 100644 (file)
index 0000000..56c75b1
--- /dev/null
@@ -0,0 +1,106 @@
+<?php global $manager; ?>
+<h2>
+       All trackbacks
+       <?php if ($count > $amount): ?>
+               (Page <?php echo ceil($start / $amount) + 1;?> of <?php echo ceil($count / $amount);?>)
+       <?php endif; ?>
+</h2>
+
+<?php if(count($items)): ?>
+<?php if ($count > $amount): ?>
+<table class="navigation">
+       <tr>
+               <td style='padding: 0;'>
+                       <?php if ($start > 0): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="&lt;&lt; Previous" />       
+                                       <input type="hidden" name="action" value="all" />
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+               <td style='padding: 0; text-align: right;'>     
+                       <?php if ($start + $amount < $count): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="Next &gt; &gt;" />
+                                       <input type="hidden" name="action" value="all" />
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+       </tr>
+</table>
+<?php endif; ?>
+
+<table>
+       <thead>
+               <tr>
+                       <th>Date</th>
+                       <th>Story</th>
+                       <th>Title, Blog and Excerpt</th>
+                       <th colspan="2">Actions</th>
+               </tr>
+       </thead>
+       <tbody>
+               <?php while (list(,$item) = each ($items)): ?>
+               <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>
+                       <td>
+                               <?php echo str_replace(' ', '&nbsp;', date("Y-m-d @ H:i",$item['timestamp']));?>
+                       </td>
+                       <td>
+                               <a href="<?php echo $item['story_url']; ?>"><?php echo $item['story'];?></a>
+                       </td>
+                       <td>
+                               <a href="<?php echo $item['url'];?>"><img alt="Visit" border="0" src="<?php echo $plugindirurl?>silk/house_go.png" /></a>
+                               <strong><?php echo $item['title'];?></strong> 
+                               <em>(<?php echo $item['blog_name'];?>)</em><br />
+                               <?php echo $item['excerpt'];?>
+                       </td>
+                       <td>
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=block&tb='.$item['id'].'&next=all&start='.$start),ENT_QUOTES);?>"><img alt="Block" border="0" src="<?php echo $plugindirurl?>silk/delete.png" /></a>
+                       </td>
+                       <td>
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=delete&tb='.$item['id'].'&next=all&start='.$start),ENT_QUOTES);?>"><img alt="Delete" border="0" src="<?php echo $plugindirurl?>silk/cross.png" /></a>
+                       </td>
+               </tr>
+               <?php endwhile; ?>
+       </tbody>
+</table>
+
+<?php if ($count > $amount): ?>
+<table class="navigation">
+       <tr>
+               <td style='padding: 0;'>
+                       <?php if ($start > 0): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="&lt;&lt; Previous" />       
+                                       <input type="hidden" name="action" value="all" />
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+               <td style='padding: 0; text-align: right;'>     
+                       <?php if ($start + $amount < $count): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="Next &gt; &gt;" />
+                                       <input type="hidden" name="action" value="all" />
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+       </tr>
+</table>
+<?php endif; ?>
+<?php endif; ?>
\ No newline at end of file
diff --git a/NP_TrackBack/trunk/trackback/templates/all_ajax.html b/NP_TrackBack/trunk/trackback/templates/all_ajax.html
new file mode 100644 (file)
index 0000000..cb19843
--- /dev/null
@@ -0,0 +1,128 @@
+<?php global $manager; ?>
+<h2>
+       All trackbacks
+</h2>
+
+<div id="message" style="color: red;"></div>
+
+<div style="width: 95%">
+<span id="tb_grid_bookmark"></span>
+
+<table id="tb_grid" style="border:0; margin:0;">
+       <colgroup>
+               <col style="width:25px;" />
+               <col style="width:40px;" />
+               <col style="width:70px;" />
+               <col style="width:150px;" />
+               <col style="width:200px;"/>
+               <col style="width:25px;" />
+       </colgroup>
+       <thead>
+               <tr>
+                       <th>&#160;</th>
+                       <th>id</th>
+                       <th>Date</th>
+                       <th>Story</th>
+                       <th>Title, Blog and Excerpt</th>
+                       <th>&#160;</th>
+               </tr>
+       </thead>
+</table>
+With selected: 
+<a href="#" onclick="javascript: doDelete()"><img alt="Delete" border="0" src="<?php echo $plugindirurl?>silk/cross.png" /></a>
+<a href="#" onclick="javascript: doBlock()"><img alt="Block" border="0" src="<?php echo $plugindirurl?>silk/delete.png" /></a>
+</div>
+
+<!--
+<textarea id='tb_grid_debugmsgs' rows='5' cols='80' style='font-size:smaller;'></textarea>
+-->
+
+<script type="text/javascript">
+//<![CDATA[
+       Rico.loadModule('LiveGridAjax');
+       Rico.loadModule('LiveGridMenu');
+       Rico.include('translations/livegrid_ja.js');
+       Rico.include('ricoAjaxEngine.js');
+       
+       Rico.onLoad( function() {
+               var params = [
+                       'action=ajax',
+                       'type=all',
+                       'ticket=<?php echo $ticket ;?>'
+               ]; 
+               
+               var cb = new Rico.TableColumn.checkbox('1','0');
+               var colspec = [
+                       {canHide:false, type:'control', control:cb, ClassName:'aligncenter'},
+                       {type:'raw'},
+                       {type:'raw'},
+                       ,
+                       ,
+                       ,
+               ];
+               
+               var opts = {
+                       saveColumnInfo   : {width:true, filter:false, sort:false}, 
+                       menuEvent       : 'none',
+                       frozenColumns   : 2,
+                       canSortDefault  : true,
+                       canHideDefault  : true,
+                       allowColResize  : true,
+                       canFilterDefault: false,
+                       highlightElem   : 'none',
+                       columnSpecs     : colspec
+               };
+               
+               buffer = new Rico.Buffer.AjaxSQL('<?php echo $CONF['PluginURL'].'trackback/';?>grid.php',
+                               {TimeOut:10, requestParameters:params, sortParmFmt: 'displayName'}
+               );
+               orderGrid=new Rico.LiveGrid ('tb_grid', buffer, opts);
+               orderGrid.menu=new Rico.GridMenu({});
+               
+               // ajaxEngine
+               ajaxEngine = new Rico.AjaxEngine;
+               ajaxEngine.registerRequest('updateData', '<?php echo $CONF['PluginURL'].'trackback/';?>grid.php' );
+               ajaxEngine.registerAjaxElement('message');
+       });
+
+       function checkUpdateIds(){
+               var updateIds = [];
+               Rico.writeDebugMsg('check updated rows');
+               for(var i = 0; i < buffer.size; i++){
+                       row = buffer.rows[i];
+                       if( row[0].content && row[0].content == '1' ){
+                               updateIds.push(row[1].content);
+                               Rico.writeDebugMsg('id: '+row[1].content+' updated');
+                       }
+               }
+               return updateIds;
+       }
+       
+       function doBlock(){
+               var ids = checkUpdateIds();
+               if( !(ids.length && ids.length > 0) ) return ;
+               var params = [
+                       'action=doblock',
+                       'ticket=<?php echo $ticket ;?>',
+                       'ids='+ids.join(',')
+               ]; 
+               ajaxEngine.sendRequest('updateData', {parameters: ajaxEngine._createQueryString(params, 0)});
+               orderGrid.resetContents('tb_grid');
+               buffer.fetch(-1);
+       }
+       
+       function doDelete(){
+               var ids = checkUpdateIds();
+               if( !(ids.length && ids.length > 0) ) return ;
+               
+               var params = [
+                       'action=dodelete',
+                       'ticket=<?php echo $ticket ;?>',
+                       'ids='+ids.join(',')
+               ];
+               ajaxEngine.sendRequest('updateData', {parameters: ajaxEngine._createQueryString(params, 0)});
+               orderGrid.resetContents('tb_grid');
+               buffer.fetch(-1);
+       }
+//]]>
+</script>
diff --git a/NP_TrackBack/trunk/trackback/templates/blocked.html b/NP_TrackBack/trunk/trackback/templates/blocked.html
new file mode 100644 (file)
index 0000000..15b98de
--- /dev/null
@@ -0,0 +1,119 @@
+<?php global $manager; ?>
+<h2>
+       Blocked trackbacks
+       <?php if ($count > $amount): ?>
+               (Page <?php echo ceil($start / $amount) + 1;?> of <?php echo ceil($count / $amount);?>)
+       <?php endif; ?>
+</h2>
+
+<ul>
+       <li><a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=blocked_clear&next=blocked'),ENT_QUOTES); ?>">ブロックされたトラックバックのクリア</a></li>
+       <li><a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=blocked_spamclear&next=blocked'),ENT_QUOTES); ?>">spam判定されたトラックバックのクリア</a></li> 
+</ul>
+
+<?php if(count($items)): ?>
+<?php if ($count > $amount): ?>
+<table class="navigation">
+       <tr>
+               <td style='padding: 0;'>
+                       <?php if ($start > 0): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="&lt;&lt; Previous" />       
+                                       <input type="hidden" name="action" value="blocked" />
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+               <td style='padding: 0; text-align: right;'>     
+                       <?php if ($start + $amount < $count): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="Next &gt; &gt;" />
+                                       <input type="hidden" name="action" value="blocked" />
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+       </tr>
+</table>
+<?php endif; ?>
+
+<table>
+       <thead>
+               <tr>
+                       <th>Date</th>
+                       <th>Story</th>
+                       <th>Title, Blog and Excerpt</th>
+                       <th colspan="2">Actions</th>
+               </tr>
+       </thead>
+       <tbody>
+               <?php while (list(,$item) = each ($items)): ?>
+               <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>
+                       <td>
+                               <?php echo str_replace(' ', '&nbsp;', date("Y-m-d @ H:i",$item['timestamp']));?>
+                       </td>
+                       <td>
+                               <a href="<?php echo $item['story_url']; ?>"><?php echo $item['story'];?></a>
+                       </td>
+                       <td>
+                               <a href="<?php echo $item['url'];?>"><?php echo $item['url'];?>"><img alt="Visit" border="0" src="<?php echo $plugindirurl;?>silk/house_go.png" /></a>
+                               <strong><?php echo $item['title'];?></strong> 
+                               <em>(<?php echo $item['blog_name'];?>)</em>
+                               <?php echo $item['spam'] ? 
+                                       '<img alt="spam" border="0" src="' . $plugindirurl . 'silk/delete.png" />' : 
+                                       '';?>
+                               <?php echo $item['link'] ? 
+                                       '' : 
+                                       '<img alt="NOT Linked" border="0" src="' . $plugindirurl . 'silk/link_break.png" />';?>
+                               <br />
+                               <?php echo $item['excerpt'];?>
+                       </td>
+                       <td>
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=unblock&tb='.$item['id'].'&next=blocked&start='.$start),ENT_QUOTES);?>"><img alt="Unblock" border="0" src="<?php echo $plugindirurl;?>silk/accept.png" /></a>
+                       </td>
+                       <td>
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=delete&tb='.$item['id'].'&next=blocked&start='.$start),ENT_QUOTES);?>"><img alt="Delete" border="0" src="<?php echo $plugindirurl;?>silk/cross.png" /></a>
+                       </td>
+               </tr>
+               <?php endwhile; ?>
+       </tbody>
+</table>
+
+<?php if ($count > $amount): ?>
+<table class="navigation">
+       <tr>
+               <td style='padding: 0;'>
+                       <?php if ($start > 0): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="&lt;&lt; Previous" />       
+                                       <input type="hidden" name="action" value="blocked" />
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+               <td style='padding: 0; text-align: right;'>     
+                       <?php if ($start + $amount < $count): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="Next &gt; &gt;" />
+                                       <input type="hidden" name="action" value="blocked" />
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+       </tr>
+</table>
+<?php endif; ?>
+<?php endif; ?>
+
diff --git a/NP_TrackBack/trunk/trackback/templates/blocked_ajax.html b/NP_TrackBack/trunk/trackback/templates/blocked_ajax.html
new file mode 100644 (file)
index 0000000..394a603
--- /dev/null
@@ -0,0 +1,133 @@
+<?php global $manager; ?>
+<h2>
+       Blocked Trackbacks
+</h2>
+
+<ul>
+       <li><a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=blocked_clear&next=blocked'),ENT_QUOTES); ?>">ブロックされたトラックバックのクリア</a></li>
+       <li><a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=blocked_spamclear&next=blocked'),ENT_QUOTES); ?>">spam判定されたトラックバックのクリア</a></li> 
+</ul>
+
+<div id="message" style="color: red;"></div>
+
+<div style="width: 95%">
+<span id="tb_grid_bookmark"></span>
+
+<table id="tb_grid" style="border:0; margin:0;">
+       <colgroup>
+               <col style="width:25px;" />
+               <col style="width:40px;" />
+               <col style="width:70px;" />
+               <col style="width:150px;" />
+               <col style="width:200px;"/>
+               <col style="width:25px;" />
+       </colgroup>
+       <thead>
+               <tr>
+                       <th>&#160;</th>
+                       <th>id</th>
+                       <th>Date</th>
+                       <th>Story</th>
+                       <th>Title, Blog and Excerpt</th>
+                       <th>&#160;</th>
+               </tr>
+       </thead>
+</table>
+With selected: 
+<a href="#" onclick="javascript: doUnblock()"><img alt="Unblock" border="0" src="<?php echo $plugindirurl;?>silk/accept.png" /></a>
+<a href="#" onclick="javascript: doDelete()"><img alt="Delete" border="0" src="<?php echo $plugindirurl?>silk/cross.png" /></a>
+</div>
+
+<!--
+<textarea id='tb_grid_debugmsgs' rows='5' cols='80' style='font-size:smaller;'></textarea>
+-->
+
+<script type="text/javascript">
+//<![CDATA[
+       Rico.loadModule('LiveGridAjax');
+       Rico.loadModule('LiveGridMenu');
+       Rico.include('translations/livegrid_ja.js');
+       Rico.include('ricoAjaxEngine.js');
+       
+       Rico.onLoad( function() {
+               var params = [
+                       'action=ajax',
+                       'type=blocked',
+                       'ticket=<?php echo $ticket ;?>'
+               ]; 
+               
+               var cb = new Rico.TableColumn.checkbox('1','0');
+               var colspec = [
+                       {canHide:false, type:'control', control:cb, ClassName:'aligncenter'},
+                       {type:'raw'},
+                       {type:'raw'},
+                       ,
+                       ,
+                       ,
+               ];
+               
+               var opts = {
+                       saveColumnInfo   : {width:true, filter:false, sort:false}, 
+                       menuEvent       : 'none',
+                       frozenColumns   : 2,
+                       canSortDefault  : true,
+                       canHideDefault  : true,
+                       allowColResize  : true,
+                       canFilterDefault: false,
+                       highlightElem   : 'none',
+                       columnSpecs     : colspec
+               };
+               
+               buffer = new Rico.Buffer.AjaxSQL('<?php echo $CONF['PluginURL'].'trackback/';?>grid.php',
+                               {TimeOut:10, requestParameters:params, sortParmFmt: 'displayName'}
+               );
+               orderGrid=new Rico.LiveGrid ('tb_grid', buffer, opts);
+               orderGrid.menu=new Rico.GridMenu({});
+               
+               // ajaxEngine
+               ajaxEngine = new Rico.AjaxEngine;
+               ajaxEngine.registerRequest('updateData', '<?php echo $CONF['PluginURL'].'trackback/';?>grid.php' );
+               ajaxEngine.registerAjaxElement('message');
+       });
+
+       function checkUpdateIds(){
+               var updateIds = [];
+               Rico.writeDebugMsg('check updated rows');
+               for(var i = 0; i < buffer.size; i++){
+                       row = buffer.rows[i];
+                       if( row[0].content && row[0].content == '1' ){
+                               updateIds.push(row[1].content);
+                               Rico.writeDebugMsg('id: '+row[1].content+' updated');
+                       }
+               }
+               return updateIds;
+       }
+       
+       function doUnBlock(){
+               var ids = checkUpdateIds();
+               if( !(ids.length && ids.length > 0) ) return ;
+               var params = [
+                       'action=dounblock',
+                       'ticket=<?php echo $ticket ;?>',
+                       'ids='+ids.join(',')
+               ]; 
+               ajaxEngine.sendRequest('updateData', {parameters: ajaxEngine._createQueryString(params, 0)});
+               orderGrid.resetContents('tb_grid');
+               buffer.fetch(-1);
+       }
+       
+       function doDelete(){
+               var ids = checkUpdateIds();
+               if( !(ids.length && ids.length > 0) ) return ;
+               
+               var params = [
+                       'action=dodelete',
+                       'ticket=<?php echo $ticket ;?>',
+                       'ids='+ids.join(',')
+               ];
+               ajaxEngine.sendRequest('updateData', {parameters: ajaxEngine._createQueryString(params, 0)});
+               orderGrid.resetContents('tb_grid');
+               buffer.fetch(-1);
+       }
+//]]>
+</script>
diff --git a/NP_TrackBack/trunk/trackback/templates/form.html b/NP_TrackBack/trunk/trackback/templates/form.html
new file mode 100644 (file)
index 0000000..ba61b48
--- /dev/null
@@ -0,0 +1,56 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+       <head>
+               <title>Add TrackBack</title>
+               <link rel="stylesheet" type="text/css" href="<?php echo $CONF['AdminURL']?>styles/bookmarklet.css" />
+       </head>
+       
+       <body>
+               <h1>TrackBack Ping</h1>
+<?php if ($success): ?>
+               <p>
+                       <strong>Your trackback has been received properly.</strong>
+               </p>
+<?php endif; ?>
+<?php if ($error): ?>
+               <p>
+                       <strong><?php echo $status; ?></strong>
+               </p>
+<?php endif; ?>
+<?php if ($form): ?>           
+               <form method="post" action="<?php echo $CONF['ActionURL'] ?>">
+               
+               <div>
+                       <input type="hidden" name="tb_id" value="<?php echo $itemid;?>" />
+                       <input type="hidden" name="action" value="plugin" />
+                       <input type="hidden" name="name" value="TrackBack" />
+                       <input type="hidden" name="type" value="ping" />
+                       
+                       <table>
+                               <tr>
+                                       <td>Article URL</td>
+                                       <td><input type="text" value="" name="url" size="60" /></td>
+                               </tr>
+                               <tr>
+                                       <td>Article Title</td>
+                                       <td><input type="text" value="" name="title" size="60" /></td>
+                               </tr>
+                               <tr>
+                                       <td>Excerpt from article</td>
+                                       <td><textarea name="excerpt" cols="40" rows="5"></textarea></td>
+                               </tr>
+                               <tr>
+                                       <td>Your Blog Name</td>
+                                       <td><input type="text" value="" name="blog_name" size="60" /></td>
+                               </tr>
+                               <tr>
+                                       <td>Add TrackBack</td>
+                                       <td><input type="submit" value="Add TrackBack" /></td>
+                               </tr>
+                       </table>
+               </div>
+               
+               </form>
+<?php endif; ?>
+       </body>
+</html>
\ No newline at end of file
diff --git a/NP_TrackBack/trunk/trackback/templates/index.html b/NP_TrackBack/trunk/trackback/templates/index.html
new file mode 100644 (file)
index 0000000..15a2ce4
--- /dev/null
@@ -0,0 +1,34 @@
+<?php global $manager; ?>
+<h2>Overview of all items</h2>
+
+<?php if(count($blogs)): ?>
+
+<table>
+<?php while (list(,$blog) = each ($blogs)): ?>
+<?php if(count($blog['items'])): ?>
+       <thead>
+               <tr>
+                       <th>Blog: <?php echo htmlspecialchars($blog['bname']);?></th>
+                       <th>Total</th>
+                       <th>Action</th>
+               </tr>
+       </thead>
+       <tbody>
+               <?php while (list(,$item) = each ($blog['items'])): ?>
+               <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>
+                       <td>
+                               <?php echo $item['ititle'];?>
+                       </td>
+                       <td>
+                               <?php echo htmlspecialchars($item['total']);?>
+                       </td>
+                       <td>
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=list&id='.$item['inumber']),ENT_QUOTES);?>">Trackbacks</a>
+                       </td>
+               </tr>
+               <?php endwhile; ?>
+       </tbody>
+<?php endif; ?>
+<?php endwhile; ?>
+</table>
+<?php endif; ?>
\ No newline at end of file
diff --git a/NP_TrackBack/trunk/trackback/templates/list.html b/NP_TrackBack/trunk/trackback/templates/list.html
new file mode 100644 (file)
index 0000000..0fcf3c3
--- /dev/null
@@ -0,0 +1,107 @@
+<?php global $manager; ?>
+<h2>
+       All trackbacks for &quot;<?php echo $story['title'];?>&quot;
+       <?php if ($count > $amount): ?>
+               (Page <?php echo ceil($start / $amount) + 1;?> of <?php echo ceil($count / $amount);?>)
+       <?php endif; ?>
+</h2>
+
+<?php if(count($items)): ?>
+<?php if ($count > $amount): ?>
+<table class="navigation">
+       <tr>
+               <td style='padding: 0;'>
+                       <?php if ($start > 0): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="&lt;&lt; Previous" />       
+                                       <input type="hidden" name="action" value="list" />
+                                       <input type="hidden" name="id" value="<?php echo $story['id'];?>" />
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+               <td style='padding: 0; text-align: right;'>     
+                       <?php if ($start + $amount < $count): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="Next &gt; &gt;" />
+                                       <input type="hidden" name="action" value="list" />
+                                       <input type="hidden" name="id" value="<?php echo $story['id'];?>" />
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+       </tr>
+</table>
+<?php endif; ?>
+
+<table>
+       <thead>
+               <tr>
+                       <th>Date</th>
+                       <th>Title, Blog and Excerpt</th>
+                       <th colspan="2">Actions</th>
+               </tr>
+       </thead>
+       <tbody>
+               <?php while (list(,$item) = each ($items)): ?>
+               <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>
+                       <td>
+                               <?php echo str_replace(' ', '&nbsp;', date("Y-m-d @ H:i",$item['timestamp']));?>
+                       </td>
+                       <td>
+                               <a href="<?php echo $item['url'];?>"><img alt="Visit" border="0" src="<?php echo $plugindirurl?>silk/house_go.png" /></a>
+                               <strong><?php echo $item['title'];?></strong> 
+                               <em>(<?php echo $item['blog_name'];?>)</em><br />
+                               <?php echo $item['excerpt'];?>
+                       </td>
+                       <td>
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=block&tb='.$item['id'].'&next=list&id='.$story['id'].'&start='.$start),ENT_QUOTES);?>"><img alt="Block" border="0" src="<?php echo $plugindirurl?>silk/delete.png" /></a>
+                       </td>
+                       <td>
+                               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=delete&tb='.$item['id'].'&next=list&id='.$story['id'].'&start='.$start),ENT_QUOTES);?>"><img alt="Delete" border="0" src="<?php echo $plugindirurl?>silk/cross.png" /></a>
+                       </td>
+               </tr>
+               <?php endwhile; ?>
+       </tbody>
+</table>
+
+<?php if ($count > $amount): ?>
+<table class="navigation">
+       <tr>
+               <td style='padding: 0;'>
+                       <?php if ($start > 0): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="&lt;&lt; Previous" />       
+                                       <input type="hidden" name="action" value="list" />
+                                       <input type="hidden" name="id" value="<?php echo $story['id'];?>" />
+                                       <input type="hidden" name="start" value="<?php echo max(0,$start - $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+               <td style='padding: 0; text-align: right;'>     
+                       <?php if ($start + $amount < $count): ?>
+                       <form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+                               <div>
+                                       <input type="submit" value="Next &gt; &gt;" />
+                                       <input type="hidden" name="action" value="list" />
+                                       <input type="hidden" name="id" value="<?php echo $story['id'];?>" />
+                                       <input type="hidden" name="start" value="<?php echo ($start + $amount);?>" />
+                                       <?php $manager->addTicketHidden(); ?>
+                               </div>
+                       </form>
+                       <?php endif; ?>
+               </td>
+       </tr>
+</table>
+<?php endif; ?>
+<?php endif; ?>
+
diff --git a/NP_TrackBack/trunk/trackback/templates/menu.html b/NP_TrackBack/trunk/trackback/templates/menu.html
new file mode 100644 (file)
index 0000000..92fa7e1
--- /dev/null
@@ -0,0 +1,33 @@
+<?php global $manager?>
+
+<script type="text/javascript" src="<?php echo $CONF['PluginURL'].'trackback/js/'?>prototype.js"></script>
+<script type="text/javascript" src="<?php echo $CONF['PluginURL'].'trackback/js/'?>rico.js"></script>
+
+<h2>Trackback</h2>
+
+<ul>
+       <li>
+               <img border="0" src="<?php echo $plugindirurl?>silk/application_view_list.png" />
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=index'),ENT_QUOTES);?>">Overview of all items</a>
+       </li>
+       <li>
+               <img border="0" src="<?php echo $plugindirurl?>silk/tick.png" />
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=all'),ENT_QUOTES);?>">All trackbacks</a>
+       </li>
+       <li>
+               <img border="0" src="<?php echo $plugindirurl?>silk/exclamation.png" />
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=blocked'),ENT_QUOTES);?>">Blocked trackbacks</a>
+       </li> 
+       <li>
+               <img border="0" src="<?php echo $plugindirurl?>silk/transmit_go.png" />
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=ping'),ENT_QUOTES);?>">Manually ping another weblog</a>
+       </li>
+       <li>
+               <img border="0" src="<?php echo $plugindirurl?>silk/help.png" />
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['PluginURL'].'trackback/index.php?action=help'),ENT_QUOTES);?>">Help</a>
+       </li>
+    <li>
+               <img border="0" src="<?php echo $plugindirurl?>silk/plugin_edit.png" />
+               <a href="<?php echo htmlspecialchars($manager->addTicketToUrl($CONF['AdminURL'].'index.php?action=pluginoptions&plugid='.$plugid),ENT_QUOTES);?>">Plugin Options</a>
+       </li>
+</ul>
\ No newline at end of file
diff --git a/NP_TrackBack/trunk/trackback/templates/ping.html b/NP_TrackBack/trunk/trackback/templates/ping.html
new file mode 100644 (file)
index 0000000..91216ce
--- /dev/null
@@ -0,0 +1,50 @@
+<?php global $manager; ?>
+<h2>Manually ping another weblog</h2>
+
+<form method="post" action="<?php echo $CONF['PluginURL'];?>trackback/index.php">
+
+    <input type="hidden" name="action" value="sendping" />
+    <input type="hidden" name="next" value="ping" />
+    <?php $manager->addTicketHidden(); ?>
+       
+    <table>
+        <tr>
+            <th colspan='2'>Manually Ping</th>
+        </tr>
+        <tr>
+            <td>Your URL</td>
+            <td>
+                <input type="text" name="url" size="60" value="<?php echo htmlspecialchars($item['url']);?>" />
+            </td>
+        </tr>
+        <tr>
+            <td>Your Title</td>
+            <td>
+                <input type="text" name="title" size="60" value="<?php echo htmlspecialchars($item['title']);?>" />
+            </td>
+        </tr>
+        <tr>
+            <td>Your Excerpt</td>
+            <td>
+                    <textarea name="excerpt" cols="40" rows="5"><?php echo $item['excerpt'];?></textarea>
+            </td>
+        </tr>
+        <tr>
+            <td>Your Blog Name</td>
+            <td>
+                <input type="text" name="blog_name" size="60" value="<?php echo htmlspecialchars($item['blogname']);?>" />
+            </td>
+        </tr>
+        <tr>
+            <td>External Ping URL</td>
+            <td>
+                <input type="text" value="" name="ping_url" size="60" />
+            </td>
+        </tr>
+        <tr>
+            <td>Send Ping</td>
+            <td><input type="submit" value="Send Ping" /></td>
+        </tr>
+    </table>
+
+</form>
\ No newline at end of file
diff --git a/NP_TrackBack/trunk/trackback/templates/response_all.xml b/NP_TrackBack/trunk/trackback/templates/response_all.xml
new file mode 100644 (file)
index 0000000..7a160ad
--- /dev/null
@@ -0,0 +1,35 @@
+<?php echo '<'.'?xml version="1.0" encoding="UTF-8"?'.'>';?>
+<?php global $manager; ?>
+
+<ajax-response>
+       <response type="object" id="tb_grid_updater">
+               <rowcount><?php echo $count; ?></rowcount>
+               <rows update_ui="true" offset="<?php echo $start; ?>" >
+                       <?php while (list(,$item) = each ($items)): ?>
+                       <tr>
+                               <td>0</td>
+                               <td><?php echo $item['id'];?></td>
+                               <td>
+                                       <?php echo date("Y-m-d H:i:s",$item['timestamp']);?>
+                               </td>
+                               <td>
+                                       <!--
+                                       <a href="<?php echo $item['story_url']; ?>"><?php echo $item['story'];?></a>
+                                       -->
+                               </td>
+                               <td>
+                                       <!--
+                                       <a href="<?php echo $item['url'];?>">
+                                               <img alt="Visit" border="0" src="<?php echo $plugindirurl?>silk/house_go.png" />
+                                       </a>
+                                       <strong><?php echo $item['title'];?></strong>
+                                       <?php echo $item['excerpt'];?>
+                                       <em>(<?php echo $item['blog_name'];?>)</em>
+                                       -->
+                               </td>
+                               <td></td>
+                       </tr>
+                       <?php endwhile; ?>
+               </rows> 
+       </response>
+</ajax-response>
diff --git a/NP_TrackBack/trunk/trackback/templates/response_blocked.xml b/NP_TrackBack/trunk/trackback/templates/response_blocked.xml
new file mode 100644 (file)
index 0000000..d085390
--- /dev/null
@@ -0,0 +1,41 @@
+<?php echo '<'.'?xml version="1.0" encoding="UTF-8"?'.'>';?>
+<?php global $manager; ?>
+
+<ajax-response>
+       <response type="object" id='tb_grid_updater'>
+               <rowcount><?php echo $count; ?></rowcount>
+               <rows update_ui='true' >
+                       <?php while (list(,$item) = each ($items)): ?>
+                       <tr>
+                               <td>0</td>
+                               <td><?php echo $item['id'];?></td>
+                               <td>
+                                       <?php echo date("Y-m-d H:i:s",$item['timestamp']);?>
+                               </td>
+                               <td>
+                                       <!--
+                                       <a href="<?php echo $item['story_url']; ?>"><?php echo $item['story'];?></a>
+                                       -->
+                               </td>
+                               <td>
+                                       <!--
+                                       <a href="<?php echo $item['url'];?>">
+                                               <img alt="Visit" border="0" src="<?php echo $plugindirurl?>silk/house_go.png" />
+                                       </a>
+                                       <strong><?php echo $item['title'];?></strong>
+                                       <?php echo $item['spam'] ? 
+                                               '<img alt="spam" border="0" src="' . $plugindirurl . 'silk/delete.png" />' : 
+                                               '';?>
+                                       <?php echo $item['link'] ? 
+                                               '' : 
+                                               '<img alt="NOT Linked" border="0" src="' . $plugindirurl . 'silk/link_break.png" />';?>
+                                       <?php echo $item['excerpt'];?>
+                                       <em>(<?php echo $item['blog_name'];?>)</em>
+                                       -->
+                               </td>
+                               <td></td>
+                       </tr>
+                       <?php endwhile; ?>
+               </rows> 
+       </response>
+</ajax-response>
diff --git a/NP_TrackBack/trunk/trackback/templates/response_doblock.xml b/NP_TrackBack/trunk/trackback/templates/response_doblock.xml
new file mode 100644 (file)
index 0000000..a53f37c
--- /dev/null
@@ -0,0 +1,8 @@
+<?echo '<'.'?xml version="1.0" encoding="UTF-8"?'.'>';?>
+<?php global $manager; ?>
+
+<ajax-response>
+   <response type="element" id="message">
+               <?php echo $message; ?>
+   </response>
+</ajax-response>
\ No newline at end of file
diff --git a/NP_TrackBack/trunk/trackback/templates/response_dodelete.xml b/NP_TrackBack/trunk/trackback/templates/response_dodelete.xml
new file mode 100644 (file)
index 0000000..a53f37c
--- /dev/null
@@ -0,0 +1,8 @@
+<?echo '<'.'?xml version="1.0" encoding="UTF-8"?'.'>';?>
+<?php global $manager; ?>
+
+<ajax-response>
+   <response type="element" id="message">
+               <?php echo $message; ?>
+   </response>
+</ajax-response>
\ No newline at end of file
diff --git a/NP_TrackBack/trunk/trackback/templates/response_dounblock.xml b/NP_TrackBack/trunk/trackback/templates/response_dounblock.xml
new file mode 100644 (file)
index 0000000..a53f37c
--- /dev/null
@@ -0,0 +1,8 @@
+<?echo '<'.'?xml version="1.0" encoding="UTF-8"?'.'>';?>
+<?php global $manager; ?>
+
+<ajax-response>
+   <response type="element" id="message">
+               <?php echo $message; ?>
+   </response>
+</ajax-response>
\ No newline at end of file
diff --git a/NP_TrackBack/trunk/trackback/templates/updatetable.html b/NP_TrackBack/trunk/trackback/templates/updatetable.html
new file mode 100644 (file)
index 0000000..113e9e6
--- /dev/null
@@ -0,0 +1,9 @@
+<?php global $manager; ?>
+<blockquote style="color: red;border:1px solid red;padding:1em;"><b>Table update:</b><br />
+                       <form method="post"><div>
+                               <input type="hidden" name="action" value="tableUpgrade" />
+                               <input type="submit" tabindex="10" value="upgrade table" />
+                               <?php $manager->addTicketHidden(); ?>
+                       </div></form>
+</blockquote>
+       
diff --git a/NP_TrackBack/trunk/trackback/templates/updatetablefinished.html b/NP_TrackBack/trunk/trackback/templates/updatetablefinished.html
new file mode 100644 (file)
index 0000000..d94969e
--- /dev/null
@@ -0,0 +1,5 @@
+<?php global $manager; ?>
+<blockquote style="color: red;border:1px solid red;padding:1em;">
+Table update done !
+</blockquote>
+